CoreDataStackがObsoleteだと気づいて、別の便利なライブラリを探していて見つけました。
Xcodeでプロジェクトを作ると、テンプレートとして、AppDelegateにCoreDataのコードが追加されます。
どこをみても、AppDelegateに置くのは筋が良くないと書いてあります。
SwiftUIであれば、environmentを使って、NSManagedObjectContextは、ビュー階層には渡せますが、
Viewではないクラスに渡したいときに、常にAppDelegate経由になるのは、あまり推奨されないと思います。
でも、自分で、CoreData系の情報を集めるクラスを1から作るのも、車輪の再発明っぽくて、躊躇してました。
ということで見つけたのが、JSQCoreDataKitです。
適当なところで、以下のコードを使うと、CoreDataがセットアップされます。
modelNameは、デフォルトでは、プロジェクトの名前が設定されているxcdatamodeldのことです。
Bundleは、上記のモデルがふくまれているBundleです。これもデフォルトでは、mainのBundleとなります。
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%ではないですが、ほぼエラーとなります)
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