Docs Menu
Docs Home
/ /
Atlas Device SDK
/ /

SwiftUI プレビューで Realm を使用する

項目一覧

  • Overview
  • 詳細ビューのオブジェクトを初期化
  • リストビューでの ObservedResult の条件付き使用
  • プレビュー用のデータを含む Realm の作成
  • インメモリ Realm の使用
  • SwiftUI プレビューの削除
  • SwiftUI プレビュー クラッシュに関する詳細情報の取得
  • プレビューでの同期された Realm の使用

SwiftUI プレビューは、開発中に役立つツールです。 SwiftUI プレビューでは、次の方法で Realm データを操作できます。

  • 詳細ビューで使用する個々のオブジェクトを初期化

  • 条件付きで、以下の代わりにオブジェクトの配列を使用する @ObservedResults

  • プレビュー用のデータを含むRealmの作成

SwiftUI プレビューのデバッグは不確定になる場合があるため、SwiftUI プレビュー内で Realm を永続化する際の問題をデバッグするためのヒントもいくつかあります。

最も単純な場合は、初期化時に直接設定できる Realm プロパティを使用する 1 つ以上のオブジェクトで SwiftUI プレビューを使用できます。 詳細ビューをプレビューするときは、これを行う必要があります。 DogoDB のDogDetailViewについて考えてみます。

struct DogDetailView: View {
@ObservedRealmObject var dog: Dog
var body: some View {
VStack {
Text(dog.name)
.font(.title2)
Text("\(dog.name) is a \(dog.breed)")
AsyncImage(url: dog.profileImageUrl) { image in
image.resizable()
} placeholder: {
ProgressView()
}
.aspectRatio(contentMode: .fit)
.frame(width: 150, height: 150)
Text("Favorite toy: \(dog.favoriteToy)")
}
}
}

モデル オブジェクトの拡張機能を作成します。 この拡張機能を配置する場所は、コードベースの規則によって異なります。 モデル ファイルに直接配置したり、サンプル データ用の専用ディレクトリを用意したり、コードベースでその他の規則を使用したりできます。

この拡張機能では、1 つ以上の Realm オブジェクトをstatic letで初期化します。

extension Dog {
static let dog1 = Dog(value: ["name": "Lita", "breed": "Lab mix", "weight": 27, "favoriteToy": "Squeaky duck", "profileImageUrl": "https://www.corporaterunaways.com/images/2021/04/lita-768x768.jpeg"])
static let dog2 = Dog(value: ["name": "Maui", "breed": "English Springer Spaniel", "weight": 42, "favoriteToy": "Wubba", "profileImageUrl": "https://www.corporaterunaways.com/images/2021/04/maui_with_leaf-768x576.jpeg"])
static let dog3 = Dog(value: ["name": "Ben", "breed": "Border Collie mix", "weight": 48, "favoriteToy": "Frisbee", "profileImageUrl": "https://www.corporaterunaways.com/images/2012/03/ben-630x420.jpg"])
}

この例では、オブジェクトを の値で初期化します。 直接初期化できるプロパティがモデルに含まれている場合は、 値を持つオブジェクトのみを初期化できます。 モデル オブジェクトに、 List プロパティなどの書込みトランザクション内でのみミューテーション可能なプロパティが含まれている場合は、代わりにが SwiftUI プレビューで使用する Realm を作成する必要があります。

モデル クラスの拡張機能としてオブジェクトを初期化したら、SwiftUI プレビューで使用できます。 オブジェクトはプレビューのビューに直接渡すことができます。

struct DogDetailView_Previews: PreviewProvider {
static var previews: some View {
NavigationView {
DogDetailView(dog: Dog.dog1)
}
}
}

リスト ビューで@ObservedResultsを使用すると、暗黙的に Realm が開き、それがクエリされます。 これをプレビューで動作させるには、データを入力する Realm が必要です。 代替策として、プレビューでは静的配列を条件付きで使用し、アプリの実行中にのみ@ObservedResults変数を使用することができます。

これは複数の方法で行えますが、コードを読みやすくして理解できるようにするために、アプリがプレビューで実行されているかどうかを検出できるEnvironmentValueを作成します。

import Foundation
import SwiftUI
public extension EnvironmentValues {
var isPreview: Bool {
#if DEBUG
return ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1"
#else
return false
#endif
}
}

次に、これを ビューの環境値として使用し、プレビューであるかどうかに基づいて使用する変数を条件付きで変更できます。

この例では、上記で定義したdog 拡張機能上に構築します。 Dog 拡張機能に として作成し、すでに作成したアイテム オブジェクトを含めます。dogArraystatic let

static let dogArray = [dog1, dog2, dog3]

次に、リストを反復処理するときに、プレビューで実行されている場合は静的dogArrayを使用し、プレビューで実行されていない場合は@ObservedResultsクエリを使用します。

struct DogsView: View {
@Environment(\.isPreview) var isPreview
@ObservedResults(Dog.self) var dogs
var previewDogs = Dog.dogArray
var body: some View {
NavigationView {
VStack {
List {
if isPreview {
ForEach(previewDogs) { dog in
DogRow(dog: dog)
}
} else {
ForEach(dogs) { dog in
DogRow(dog: dog)
}.onDelete(perform: $dogs.remove)
}
}
... More View code

これには軽量でデータを保持しないという利点がありますが、ビュー コードが冗長になるというダウングレードが行われます。 よりクリーンなビュー コードを使用する場合は、プレビューで使用するデータを使用して Realm を作成できます。

場合によっては、SwiftUI プレビューで Realm データを表示する唯一のオプションは、データを含む Realm を作成することです。 そのため、 ListMutableSetなど、値を直接初期化するのではなく、書込みトランザクション中にのみ入力できるプロパティを入力する場合などがあります。 また、ビューが他のビューから渡されるより複雑なオブジェクト階層に依存している場合も、この操作を実行することをお勧めします。

ただし、Realm を直接使用すると、SwiftUI プレビューに状態が導入されるため、制約が生じる可能性があります。 Realm を使用しているか Core Data を使用しているかにかかわらず、ステートフルな SwiftUI プレビューを使用すると次のような問題が発生する可能性があります。

  • Realm ファイル作成手順が繰り返し再実行されることで、予期しないデータまたは重複したデータが表示される

  • モデル変更を行う場合に SwiftUI プレビュー内で移行を実行する必要がある

  • ビュー内の状態の変更に関連する潜在的な問題

  • SwiftUI プレビューで可視化されていない問題に関連する未確認のクラッシュまたはパフォーマンスの問題

以下のヒントを使用して、これらの問題の一部を回避または修正できます。

モデル 拡張機能で Realm の静的変数を作成できます。 ここで、Realm にデータを入力する作業を行います。 この場合は、 Personを作成し、いくつかのDogオブジェクトをdogs List プロパティに追加します。 この例は、Do 拡張機能でいくつかの Do オブジェクトを初期化した上記の例に構築されています。

Person拡張機能を作成し、その拡張機能内に単一のPersonオブジェクトを作成します。 次に、先ほど作成したPersonを追加し、 Dog拡張機能のサンプルDogオブジェクトを追加してpreviewRealmを作成します。

これらのオブジェクトを複数回追加しないようにするために、 権限オブジェクト をクエリし、カウントが 1 であることを確認して、権限がすでに存在するかどうかを確認するチェックを追加します。 Realm に Perl が含まれている場合は、SwiftUI プレビューで使用できます。 そうでない場合、データを追加します。

static var previewRealm: Realm {
var realm: Realm
let identifier = "previewRealm"
let config = Realm.Configuration(inMemoryIdentifier: identifier)
do {
realm = try Realm(configuration: config)
// Check to see whether the in-memory realm already contains a Person.
// If it does, we'll just return the existing realm.
// If it doesn't, we'll add a Person append the Dogs.
let realmObjects = realm.objects(Person.self)
if realmObjects.count == 1 {
return realm
} else {
try realm.write {
realm.add(person)
person.dogs.append(objectsIn: [Dog.dog1, Dog.dog2, Dog.dog3])
}
return realm
}
} catch let error {
fatalError("Can't bootstrap item data: \(error.localizedDescription)")
}
}

SwiftUI プレビューで使用するには、ProfileView コードにプロファイルが必要です。 これはロール オブジェクト のプロジェクションです。 プレビューでは、Realm を取得し、プロファイルをクエリし、それをビューに渡すことができます。

struct ProfileView_Previews: PreviewProvider {
static var previews: some View {
let realm = Person.previewRealm
let profile = realm.objects(Profile.self)
ProfileView(profile: profile.first!)
}
}

Realm オブジェクトが渡されることを想定しているビューはなく、代わりに@ObservedResultsを使用して Realm をクエリしたり、既存の Realm を操作したりする場合は、Realm を環境値としてビューに挿入できます。

struct SomeListView_Previews: PreviewProvider {
static var previews: some View {
SomeListView()
.environment(\.realm, Person.previewRealm)
}
}

可能であれば、インメモリ レルムを使用して、SwiftUI プレビュー内でデータベースを使用することで発生し得る状態に関連する問題の一部を回避します。

Realm を初期化するときに、 inMemoryIdentifier構成プロパティを使用します。

static var previewRealm: Realm {
var realm: Realm
let identifier = "previewRealm"
let config = Realm.Configuration(inMemoryIdentifier: identifier)
do {
realm = try Realm(configuration: config)
// ... Add data to realm

注意

SwiftUI プレビューの Realm を初期化するときに、 deleteRealmIfMigrationNeted構成プロパティを使用しないでください。 Apple が SwiftUI プレビューを実装した方法により、このプロパティを使用して移行の問題をバイパスすると、SwiftUI プレビューがクラッシュします。

移行が必要になったためにプレビューにRealmをロードできないなど、状態に関連するその他の SwiftUI プレビューの問題が発生した場合は、キャッシュされたプレビュー データを削除するためにできる操作がいくつかあります。

Appleが推奨する修正は、Xcode を閉じ、コマンドラインを使用して既存の SwiftUI プレビュー データをすべて削除することです。

  1. Xcode を閉じます。

  2. コマンドラインから、次のコマンドを実行します。

    xcrun simctl --set previews delete all

このコマンドの実行後にデータが残っている可能性があります。 これは、Xcode がプレビューで何らかの参照を保持し、それを削除できないことが原因である可能性があります。 問題を解決するために、次の手順を試すこともできます。

  • 別のシミュレーターをビルドします

  • コンピューターを再起動し、次を再実行します xcrun simctl --set previews delete all

  • 保存されたプレビュー データを直接削除します。 このデータは~/Library/Developer/Xcode/UserData/Previewsに保存されています。

Realm を使用しているときに説明されていない SwiftUI プレビュー クラッシュが発生した場合は、まずシミュレーターでアプリケーションを実行してみてください。 シミュレーターで利用可能なエラーメッセージとログにより、問題を見つけて診断するのが容易になります。 シミュレーターで問題をデバッグできる場合は、これが最も簡単なルートです。

シミュレーターで SwiftUI プレビュー クラッシュを複製できない場合は、 SwiftUI プレビュー アプリのクラッシュ ログを表示できます。 これらのログは~/Library/Logs/DiagnosticReports/で利用できます。 これらのログは遅延後に表示されることがあるため、関連するログがすぐに表示されない場合は、クラッシュした後数分待ちます。

アプリで Atlas Device Sync を使用している場合、SwiftUI プレビューで同期された Realm を使用するにはどうすればよいですか。 静的オブジェクトまたはデータを入力するローカル Realm を SwiftUI プレビューに使用する方法がおすすめです。

このサンプル アプリでは、Realm を使用しなくても、Device Sync に関連付けられたビューである LoginView をプレビューできます。

struct LoginView_Previews: PreviewProvider {
static var previews: some View {
LoginView()
}
}

静的 UI のみを表示しているため、LoginView を表示するか OpenSyncedRealmView にGoするかについてのロジックを含む SyncContentView について心配する必要はありません。 OpenSyncedRealmView のプレビューをスキップすることもできます。これは、同期された Realm を開き、DogsView に入力することに関連するロジックのみを処理するためです。 したがって、プレビューに次に表示するビューは、DogsView です。

幸いなことに、Realm では、Realm を操作するコードは、Realm が Device Sync を使用するかどうかに関係ありません。Realm は同じ方法で操作します。 したがって、上の例で作成したのと同じローカル Realm を SwiftUI プレビューで使用できます。

struct DogsView_Previews: PreviewProvider {
static var previews: some View {
let realm = Person.previewRealm
DogsView()
.environment(\.realm, realm)
}
}

戻る

バックグラウンドでのデータの同期