🪶

SwiftDataを使ってみた

2024/07/13に公開

What SwiftData?

https://developer.apple.com/xcode/swiftdata/

SwiftData は宣言的なコードを使用してデータを簡単に永続化します。通常の Swift のコードを使用して、データをクエリし、フィルタリングすることができます。そして、SwiftUI とシームレスに統合するように設計されています。

「と書いてある」
ローカルDBでは、Realm、CoreDataが有名だが、SwiftDataは何が良いのか???

公式によると

Swift でモデルを作成

Modelを使うことで、追加のファイルやツールを管理することなく、通常のSwift型を使ってデータをモデリングできます。SwiftDataは自動的に多くの関係を推測し、制約を記述するために#Uniqueのような明確な宣言を使うことができます。SwiftUI のように、真実のソースはあなたのコードの中にあります。

@Model
class Recipe {
	@Attribute(.unique) var name: String
	var summary: String?
	var ingredients: [Ingredient]
}

自動永続化

SwiftData はモデルを使用してカスタムスキーマを構築し、そのフィールドを基礎となるストレージに効率的にマッピングします。SwiftData によって管理されるオブジェクトは、必要なときにデータベースから取得され、適切なタイミングで自動的に保存されます。ModelContext API を使って完全に制御することもできます。

カスタムデータストア

デフォルトでは、SwiftDataは永続化のためにCore Dataを使用しますが、新しいDataStoreプロトコルを使用して独自の永続化レイヤーを実装することもできます。DataStoreを使用することで、SwiftDataのAPIはSwiftUIとSwiftDataのモデリングコードを変更することなく、JSONファイルからWebサービスとデータベースエンジンまで、多くの種類の永続化で使用することができます。

SwiftUIと統合する

データを取得するためにSwiftUIのビューで@Queryを使用します。SwiftData と SwiftUI は、基礎となるデータが変更されたときに、手動で結果を更新することなく、ビューにライブアップデートを提供するために連携します。

@Query var recipes: [Recipe]
var body: some View {
	List(recipes) { recipe in
		NavigationLink(recipe.name, destination: RecipeView(recipe))
	}
}

Swiftネイティブ述語
コンパイラによってチェックすることができる Swift ネイティブの型を使用してデータをクエリとフィルタリングすることで、開発中に問題を早期に発見することができます。述語は、式が基礎となるストレージエンジンにマッピングできないときに、コンパイル時のエラーを提供します。

let simpleFood = #Predicate<Recipe> { recipe in
	recipe.ingredients.count < 3
}

CloudKit同期

データはDocumentGroupを使ってファイルに保存し、iCloud Drive経由で同期することもできますし、CloudKitを使ってデバイス間でデータを同期することもできます。

Core Dataとの互換性

SwiftDataはCore Dataの実績のあるストレージアーキテクチャを使用しているので、同じアプリで同じ基礎となるストレージで両方を使用することができます。準備ができたら、XcodeはあなたのCore DataモデルをSwiftDataで使用するためのクラスに変換することができます。


どうやって使えば良いの詳しくは、書いておりませんね。海外の動画見てチュートリアルをやってみました。

https://www.youtube.com/watch?v=krRkm8w22A8

スキーマを作成する

データベースを扱うためのスキーマを作成しましょう。初期化の処理と自動連番のidを生成する処理を記述したら完成です。Realmよりモデルの定義もロジックを書くのも簡単でした。

import Foundation
import SwiftData

// データスキーマを定義
@Model
class DataItem: Identifiable {
    
    var id: String
    var name: String
    
    // 初期化する処理を書く
    init(name: String) {
        // uuidを文字列に初期化
        self.id = UUID().uuidString
        self.name = name
    }
}

アプリケーションのエントリーポイントに、データーを永続化するコンテナを作成する必要があるので、以下のように作成する。

import SwiftUI
import SwiftData

@main
struct SwiftDataTutorialApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        // データを永続化するコンテナを作成
        // パラメータとして型を渡す場合は、.selfという名前をつける。
        .modelContainer(for: DataItem.self)
    }
}

今回は、ハードコーディングしてますが、データを追加・表示・更新・削除する処理を作りました。@Environmentを使って、構造体から、contextを読み込むことで、SwiftDataを操作できるようになります。

import SwiftUI
import SwiftData

struct ContentView: View {
    // このコンテキストを通じてすべての追加・更新・削除を実行できる
    @Environment(\.modelContext) private var context
    
    // all data fetch
    @Query private var items: [DataItem]
    
    var body: some View {
        VStack {
            
            Text("add data!")
            Button("Add an Item") {
                addItem()
            }
            // show array data
            List {
                ForEach(items) {item in
                    
                    HStack {
                        Text(item.name)
                        Spacer()
                        Button {
                            updateItem(item)
                        } label: {
                            Image(systemName: "arrow.triangle.2.circlepath")
                        }
                    }
                }.onDelete { indexes in
                    for index in indexes {
                        deleteItem(items[index])
                    }
                }
            }
        }
        .padding()
    }
    
    func addItem() {
        // Create the item
        let item = DataItem(name: "Test Item")
        // Add the item to the data context
        context.insert(item)
    }
    
    // update
    func updateItem(_ item: DataItem) {
        // Edit the item data
        item.name = "updated Test Item"
        // Save the context
        try? context.save()
    }
    
    // delete
    func deleteItem(_ item: DataItem) {
        context.delete(item)
    }
}

動作はこんな感じですね。

[追加]

[表示]

[更新]

[削除]

まとめ

SwiftDataを使ってみた感想ですが、簡潔なコードでローカルにデータを保存するビジネスロジックを作ることができるので、とても便利なものでした。注意点としては、iOS17からしか使用できないようです。ですので、iOSのバージョンアップ対応が必要です。

Discussion