オフラインファースト
Amplify DataStore の独自のアプローチについてさらに理解するために、以下のリソースを参照してください。
オフラインファースト アプリケーション構築の原則をさらに探索し理解するために、Android のオフラインファースト アプリケーション構築ガイドを確認することをお勧めします。
これらの機能がアプリケーションにとって重要であることを理解しており、DataStore は新機能を受け取らなくなりますが、スムーズに移行するために必要なリソースを提供したいと考えています。以下のセクションでは、代替ソリューションについての高レベルな推奨事項を示します。
リモート同期
ローカル ストアを定期的に同期することで、アプリケーションのプライマリ ソースとして機能し、オフラインで実行できる読み取り操作を実現できます。オフライン書き込みを管理することを選択すると、アプリケーションに追加の複雑さが生じるため、各個別モデルに対してこれを処理するかどうかを検討することが重要です。
カスケード削除
SwiftData はリレーションシップを持つモデルのカスケード削除をサポートし、削除を処理するためのさまざまなオプションを提供します。リレーションシップは Relationship(_:deleteRule:minimumModelCount:maximumModelCount:originalName:inverse:hashModifier:) 修飾子を使用して定義できます。
@Modelclass Trip { var name: String var destination: String var startDate: Date var endDate: Date var accommodation: Accommodation?}削除ルールは DeleteRule を使用して設定できます。
@Relationship(.cascade) var accommodation: Accommodation?ライブ同期
AWS AppSync を活用することで、インターネットに接続している限り、リモート リポジトリへの変更(作成、更新、削除)をリアルタイムで検出できます。以下のスニペットは、Post タイプのリモート作成、更新、削除をサブスクライブし、それらの変更をローカル SwiftData ストアに伝播します。これらのサブスクリプションは、アプリケーションがインターネットに再接続するたびに確立する必要があります。
let createSubscription = apolloClient.subscribe( subscription: OnCreateSubscriptionSubscription()) { result in guard let data = try? result.get().data else { return } if let postDetails = data.onCreatePost?.fragments.postDetails { let model = PostEntity(postDetails: postDetails) context.insert(model) try? context.save() }}
let deleteSubscription = apolloClient.subscribe( subscription: OnDeleteSubscriptionSubscription()) { result in guard let data = try? result.get().data else { return } if let deletedId = data.onDeletePost?.id { let fetchDescriptor = FetchDescriptor<PostEntity>( predicate: #Predicate { $0.id == deletedId }) if let post = try? context.fetch(fetchDescriptor).first { context.delete(post) } }}
let updateSubscription = apolloClient.subscribe( subscription: OnUpdateSubscriptionSubscription()) { result in guard let data = try? result.get().data else { return } if let postDetails = data.onUpdatePost?.fragments.postDetails { let model = PostEntity(postDetails: postDetails) context.insert(model) // upsert via @Attribute(.unique) on id try? context.save() }}ローカル キャッシュの更新
アプリケーションがインターネットに再接続するたびに、アプリケーション起動またはネットワーク更新を通じて、ローカル ストアの完全な更新を実行して、見落とした可能性のあるすべての更新を確実に受け取るようにしてください。
func syncAllPosts(apolloClient: ApolloClient, context: ModelContext) { var nextToken: String? = nil repeat { let query = GetPostsQuery(nextToken: nextToken.flatMap { .some($0) } ?? .none) apolloClient.fetch(query: query) { result in guard let data = try? result.get().data else { return } if let items = data.listPosts?.items { for item in items { guard let postDetails = item?.fragments.postDetails else { continue } let model = PostEntity(postDetails: postDetails) context.insert(model) try? context.save() } } nextToken = data.listPosts?.nextToken } } while (nextToken != nil)}大量のデータを頻繁に同期する場合、AWS AppSync Delta Sync 操作を設定することで、ローカル ストアを更新するのに必要な時間を短縮できます。Delta Sync 機能を設定および実装する方法の詳細については、AWS AppSync Delta Sync ガイドをご覧ください。
ネットワーク ステータスの検出
NWPathMonitor を使用してネットワーク ステータスをプロアクティブに検出できます。これにより、ローカル キャッシュを同期し、サブスクリプションを再確立する必要があるシナリオを特定できます。
protocol NetworkMonitor: AnyObject { var isOnline: Bool { get } func startMonitoring(using queue: DispatchQueue) func stopMonitoring()}
extension NWPathMonitor: NetworkMonitor { var isOnline: Bool { currentPath.status == .satisfied }
func startMonitoring(using queue: DispatchQueue) { self.pathUpdateHandler = { [weak self] path in let isConnected = path.status == .satisfied if isConnected { // start sync and reestablish subscriptions } else { // stop sync } } start(queue: queue) }
func stopMonitoring() { cancel() }}オフラインミューテーション
ローカルで行われた変更を別のペンディング ミューテーション テーブルに格納し、後で同期することで、ユーザーがオフラインで作業できるようにします。ペンディング ミューテーション テーブルには、以下を含める必要があります。
- 自動インクリメント プライマリ キー(モデルの通常のプライマリ キーではない)
- ミューテーション タイプの列挙(作成、更新、または削除)
- アクション時のモデルの状態
例として、サンプル Post タイプのペンディング ミューテーションは以下のようになります。
enum MutationType: Codable { case create case update case delete}
@Modelclass PostMutationEntity { var postId: String var type: MutationType var title: String? var content: String? var status: PostEntityStatus? var rating: Int? var timestamp: String init(postId: String, type: MutationType, title: String? = nil, content: String? = nil, status: PostEntityStatus? = nil, rating: Int? = nil, timestamp: String) { self.postId = postId self.type = type self.title = title self.content = content self.status = status self.rating = rating self.timestamp = timestamp }}ペンディング ミューテーションを適用
これらのペンディング ミューテーションは、アプリケーションに接続がある場合、AWS AppSync に同期できます。ペンディング ミューテーション テーブル エントリを同期する前にアプリに組み込むには、PostEntity と PostMutationEntity の両方のインスタンスをフェッチし、それらを単一のディスプレイ モデルに組み合わせ、AWS AppSync によって処理される前にミューテーションを楽観的に適用できます。
extension PostMutationEntity { func applyTo(model: PostEntity) -> PostEntity { if let title = self.title { model.title = title } if let content = self.content { model.content = content } if let rating = self.rating { model.rating = rating } if let status = self.status { model.status = status } return model }}