CoreDataStackがObsoleteだと気づいて、別の便利なライブラリを探していて見つけました。
Xcodeでプロジェクトを作ると、テンプレートとして、AppDelegateにCoreDataのコードが追加されます。
どこをみても、AppDelegateに置くのは筋が良くないと書いてあります。
SwiftUIであれば、environmentを使って、NSManagedObjectContextは、ビュー階層には渡せますが、
Viewではないクラスに渡したいときに、常にAppDelegate経由になるのは、あまり推奨されないと思います。
でも、自分で、CoreData系の情報を集めるクラスを1から作るのも、車輪の再発明っぽくて、躊躇してました。
ということで見つけたのが、JSQCoreDataKitです。
適当なところで、以下のコードを使うと、CoreDataがセットアップされます。
modelNameは、デフォルトでは、プロジェクトの名前が設定されているxcdatamodeldのことです。
Bundleは、上記のモデルがふくまれているBundleです。これもデフォルトでは、mainのBundleとなります。
1 2 3 4 5 6 7 8 9 10 11 12 |
let model = CoreDataModel(name: modelName, bundle: modelBundle) let factory = CoreDataStackProvider(model: model) factory.createStack { result in switch result { case .success(let s): self.stack = s case .failure(let err): assertionFailure("Error creating stack: \(err)") } } |
上記の.successで渡されるsを保持しておけば、終了です。stack.mainContextがManagedObjectContextです。(必要に応じて、childContextも作れます)
ちなみに、SwiftUIの場合は、EnvironmentにManagedObjectContextをビュー構築時に渡す必要があるので、factoryの結果を待つ必要があります。なので、以下のようなコードで実行する必要があります。
# asyncでセットアップを実行してしまうと、environmentに渡そうと取得したタイミングが、初期化前のために、エラーとなるケースがあります。(タイミングの問題なので、100%ではないですが、ほぼエラーとなります)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
let model = CoreDataModel(name: "CoreDataModel") let factory = CoreDataStackProvider(model: model) factory.createStack(onQueue: nil, completion: { result in switch result { case .success(let s): // store CoreDataStack in somewhere self.coreDataStack = s case .failure(let err): assertionFailure("Error in creating stack: \(err)") } }) // Get the managed object context from the shared persistent container. let context = coreDataStack.mainContext // Create the SwiftUI view and set the context as the value for the managedObjectContext environment keyPath. // Add `@Environment(\.managedObjectContext)` in the views that will need the context. let contentView = ContentView().environment(\.managedObjectContext, context) |
P.S.
このライブラリのサンプルを見ての通りですが、CoreDataのモデルだけのプロジェクト/Bundleを作っています。
このアイデアも良い感じでした。
Sponsor Link