SwiftUI プレビューで Realm を使用する
項目一覧
Overview
SwiftUI プレビューは、開発中に役立つツールです。 SwiftUI プレビューでは、次の方法で Realm データを操作できます。
詳細ビューで使用する個々のオブジェクトを初期化
条件付きで、以下の代わりにオブジェクトの配列を使用する
@ObservedResults
プレビュー用のデータを含むRealmの作成
SwiftUI プレビューのデバッグは不確定になる場合があるため、SwiftUI プレビュー内で Realm を永続化する際の問題をデバッグするためのヒントもいくつかあります。
詳細ビューのオブジェクトを初期化
最も単純な場合は、初期化時に直接設定できる Realm プロパティを使用する 1 つ以上のオブジェクトで SwiftUI プレビューを使用できます。 詳細ビューをプレビューするときは、これを行う必要があります。 DogoDB のDogDetailView
について考えてみます。
struct DogDetailView: View { 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) } } }
リストビューでの ObservedResult の条件付き使用
リスト ビューで@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 拡張機能に として作成し、すでに作成したアイテム オブジェクトを含めます。dogArray
static let
static let dogArray = [dog1, dog2, dog3]
次に、リストを反復処理するときに、プレビューで実行されている場合は静的dogArray
を使用し、プレビューで実行されていない場合は@ObservedResults
クエリを使用します。
struct DogsView: View { var isPreview (\.isPreview) 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 を作成できます。
プレビュー用のデータを含む Realm の作成
場合によっては、SwiftUI プレビューで Realm データを表示する唯一のオプションは、データを含む Realm を作成することです。 そのため、 ListやMutableSetなど、値を直接初期化するのではなく、書込みトランザクション中にのみ入力できるプロパティを入力する場合などがあります。 また、ビューが他のビューから渡されるより複雑なオブジェクト階層に依存している場合も、この操作を実行することをお勧めします。
ただし、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) } }
インメモリ Realm の使用
可能であれば、インメモリ レルムを使用して、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 プレビューがクラッシュします。
SwiftUI プレビューの削除
移行が必要になったためにプレビューにRealmをロードできないなど、状態に関連するその他の SwiftUI プレビューの問題が発生した場合は、キャッシュされたプレビュー データを削除するためにできる操作がいくつかあります。
Appleが推奨する修正は、Xcode を閉じ、コマンドラインを使用して既存の SwiftUI プレビュー データをすべて削除することです。
Xcode を閉じます。
コマンドラインから、次のコマンドを実行します。
xcrun simctl --set previews delete all
このコマンドの実行後にデータが残っている可能性があります。 これは、Xcode がプレビューで何らかの参照を保持し、それを削除できないことが原因である可能性があります。 問題を解決するために、次の手順を試すこともできます。
別のシミュレーターをビルドします
コンピューターを再起動し、次を再実行します
xcrun simctl --set previews delete all
保存されたプレビュー データを直接削除します。 このデータは
~/Library/Developer/Xcode/UserData/Previews
に保存されています。
SwiftUI プレビュー クラッシュに関する詳細情報の取得
Realm を使用しているときに説明されていない SwiftUI プレビュー クラッシュが発生した場合は、まずシミュレーターでアプリケーションを実行してみてください。 シミュレーターで利用可能なエラーメッセージとログにより、問題を見つけて診断するのが容易になります。 シミュレーターで問題をデバッグできる場合は、これが最も簡単なルートです。
シミュレーターで SwiftUI プレビュー クラッシュを複製できない場合は、 SwiftUI プレビュー アプリのクラッシュ ログを表示できます。 これらのログは~/Library/Logs/DiagnosticReports/
で利用できます。 これらのログは遅延後に表示されることがあるため、関連するログがすぐに表示されない場合は、クラッシュした後数分待ちます。
プレビューでの同期された Realm の使用
アプリで 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) } }