はじめに
ソフトウェアエンジニアのTatsunoriです。
個人開発でサブスクリプションを管理するアプリを作っています。
今までだとローカルデータはRealmを使用していましたが、Appleから新しくSwiftDataが使えるようになったので、使用してみた感想になります。
結論
個人的にはRealmよりも使いやすくていいと思います。
これからアプリ開発でローカルDBを使用するならSwiftDataを選択します。
開発環境
- Swift5
- Xcode15.2
- iOS17.2
メリット
プリミティブ型以外も使用できる
By default, SwiftData includes all noncomputed properties of a class as long as they use compatible types. The framework supports primitive types such as
Bool
,Int
, andString
, as well as complex value types such as structures, enumerations, and other value types that conform to theCodable
protocol.
リファレンスにも記載ありますが、Cpdableを継承すればstructureでもenumerationでも保存できます。
@Model
final class Subscription {
var color: ColorComponents
}
struct ColorComponents: Codable {
let red: Float
let green: Float
let blue: Float
var color: Color {
Color(red: Double(red), green: Double(green), blue: Double(blue))
}
static func fromColor(_ color: Color) -> ColorComponents {
let resolved = color.resolve(in: EnvironmentValues())
return ColorComponents(
red: resolved.red,
green: resolved.green,
blue: resolved.blue
)
}
}
上記サンプルのようにColorComponetsとしてユーザが選択した色をstructとしてSwiftDataに保存可能です。
プリミティブ型に変換して出し入れする必要がないので、変換コードを書く必要がなくシンプルになっていいです。
Widgetと共有するのにApp GroupのURLを設定しなくてもいい
Realmだとインスタンス作成する際に、Wiegetでも共有で使用するにはApp GroupのURLをConfigureationに設定する必要がありますが、
SwiftDataではそんな必要はないので、初めてApp Groupを使ってWidgetとDBを共有する際にアプリ側で保存したデータにWidget側でアクセスできない問題が回避できていいかなと思いました。
public static var previewModelContainer: ModelContainer = {
let schema = Schema([
Subscription.self,
])
let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: true)
do {
let container = try ModelContainer(for: schema, configurations: [modelConfiguration])
loadPreviewData().forEach { subscription in
container.mainContext.insert(subscription)
}
return container
} catch {
fatalError("Could not create ModelContainer: \(error)")
}
}()
デメリット
Realm Studioが使えない
Realmを使った場合、シュミレータに保存したDBのデータを確認する際にRealm studioが使えますが、SwiftDataでは使いないです。
UIが見やすくて気に入っていたので残念です。代わりにDB Browser for SQLiteが使用できます。
Widget Previewでは.modelContainerが使えない
使えないないようです。
@Model
final class Subscription {
var name: String
...
}
@MainActor
class DataContainer {
public static func loadPreviewData() -> [Subscription] {
let subscriptions: [Subscription] = [
.init(name: "Apple music")
}
}
}
#Preview(as: .systemSmall) {
StarshipWidget()
} timeline: {
SimpleEntry(date: .now, configuration: .smiley, subscriptions: DataContainer.loadPreviewData())
}
だからといって上記のようにmodelContainerなしで@Modelの付いたModelを呼び出すとCanvasでエラーがでます。
public static func fetchPreviewSubscriptions() -> [Subscription] {
let context = DataContainer.previewModelContainer.mainContext
let subscriptions = try? context.fetch(FetchDescriptor<Subscription>())
return subscriptions ?? []
}
#Preview(as: .systemSmall) {
StarshipWidget()
} timeline: {
SimpleEntry(date: .now, configuration: .smiley, subscriptions: DataContainer.fetchPreviewSubscriptions())
}
WidgetではmodelContainerからfetchでデータ取得する必要があるみたいです。
おわりに
まだ使用し始めたところですが、RealmよりSwiftUIと親和性が高く、CoreDataやSQLiteよりも使いやすく感じます。
これからいろいろと使ってみたいと思います。
参考
https://developer.apple.com/xcode/swiftdata/
https://developer.apple.com/documentation/swiftdata
https://developer.apple.com/videos/play/wwdc2023/10187/
コメント