ZManagedObjectStorage
今回は、Core Data をより便利に使う ZManagedObjectStorage
を紹介したいと思います。Core Data を使うためには、Xcode が Core Data 対応のプロジェクトを吐き出したコードの中にあるように、NSManagedObjectModel
から NSPersistentStoreCoordinator
さらに NSManagedObjectContext
の生成へと、結構な量のおきまりのコードを書かなくてはなりません。ZManagedObjectStorage
を使えばそんなボイラープレートなコードを書く必要がなく、より簡単に Core Data を扱う事ができます。
以下に典型的な Core Data の storage を用意するコードを示します。渡す情報は Core Data のドキュメントのURLと、CoreData のモデルの名前です。指定されたURLにCore Data のファイルがない場合には新たに作られます。
let documentURL = ...
let storage = ZManagedObjectStorage(fileURL: documentURL, modelName: "MyModel")
あとは、managed object context はmanagedObjectContext
プロパティをアクセスすればあとは、通常の Core Data のようにオブジェクトを Fetch したり Insert したりできます。
let managedObjectContext = storage.managedObjectContext
do {
let fetchRequest = NSFetchRequest()
let entityDescription = NSEntityDescription.entityForName("YourEntity", inManagedObjectContext: managedObjectContext)
fetchRequest.entity = entityDescription
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "some_key", ascending: true)]
let objects = try managedObjectContext.executeFetchRequest(fetchRequest)
// ...
}
catch let error {
print("\(error)")
}
let managedObjectContext = storage.managedObjectContext
do {
let managedObject = NSEntityDescription.insertNewObjectForEntityForName("YourEntity", inManagedObjectContext: managedObjectContext)
try managedObject.save()
}
catch let error {
print("\(error)")
}
なぜ モデル名を指定するのですか?
Xcode が Core Data 対応のプロジェクトを作成する際は全てのモデルをマージした状態で、managed object context を用意しています。多くの場合はこれで事足りるかもしれませんが、例えば、関連性の低い二つのデータを異なるドキュメントとして、Core Data で扱おうとすると、おそらく二つのモデルを用意しようと考えるのが自然だと思いますが、一方のデータを Core Data で保存したとしても、他方のモデル情報もその Core Data ファイルに含まれしまいます。
例えば、商品情報のCore Dataファイルと、株価情報のCore Dataファイルを同じアプリで扱う場合に、マージされたモデルを使うと、商品情報の Core Data ファイルにも株価情報のスキーマが、株価情報を扱う Core Data ファイルにも商品情報のスキーマが紛れんでしまいます。
したがって、この状態で商品情報のスキーマの変更があると、株価情報のCore Dataファイルにも影響を与えてしまいます。その逆も然りです。このように、二つのスキーマの関連性がない場合などには、モデルを分けて扱う方がマイグレーションなども含めて扱いやすくなると考えるのが自然なので、ZManagedObjectStorage
では、モデルを別に指定するようにしています。
ソース
import Foundation
import CoreData
class ZManagedObjectStorage {
var fileURL: NSURL
var modelName: String
init?(fileURL: NSURL, modelName: String) {
self.fileURL = fileURL
self.modelName = modelName
if self.managedObjectContext == nil {
return nil
}
}
lazy var managedObjectModel: NSManagedObjectModel? = {
var managedObjectModel: NSManagedObjectModel? = nil
let modelURL = NSBundle.mainBundle().URLForResource(self.modelName, withExtension: "momd")
if modelURL != nil {
managedObjectModel = NSManagedObjectModel(contentsOfURL: modelURL!)
}
if managedObjectModel == nil { NSLog("model not found: name=%@", self.modelName) }
return managedObjectModel
}()
lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator? = {
var coordinator: NSPersistentStoreCoordinator? = nil
var error: NSError? = nil
var managedObjectModel = self.managedObjectModel
if (managedObjectModel != nil) {
do {
coordinator = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel!)
let options = [NSMigratePersistentStoresAutomaticallyOption: true, NSInferMappingModelAutomaticallyOption: true];
let store = try coordinator?.addPersistentStoreWithType(NSSQLiteStoreType,
configuration:nil, URL:self.fileURL, options:options)
if store == nil {
NSLog("Failed migrating Core Data Model. Old file will be deleted.")
NSLog("file: %@", self.fileURL.path!)
#if DEBUG
NSFileManager.defaultManager().removeItemAtPath(self.file, error: &error)
ZReportError(error)
// recreate store file once again
store = coordinator.addPersistentStoreWithType(NSSQLiteStoreType,
configuration:nil, URL:self.storeURL, options:options, error:&error)
ZReportError(error)
if store == nil { return nil }
#endif
}
}
catch let error {
}
}
if coordinator == nil {
NSLog("failed to configure persistent store coodinator: %@", self.fileURL.path!)
}
return coordinator
}()
lazy var managedObjectContext: NSManagedObjectContext? = {
var context: NSManagedObjectContext? = nil
let coordinator = self.persistentStoreCoordinator
if coordinator != nil {
context = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)
context?.persistentStoreCoordinator = self.persistentStoreCoordinator
}
return context
}()
}
同コードはgistからも取得可能です。
https://gist.github.com/codelynx/2fb57bf3f8ea3701b1af57ed2f851cc5
GitHub
また、Xcode のサンプルプロジェクトも用意いたしました。サンプルプロジェクトでは iOS で +
をタップすると乱数を発生させタイムスタンプと共に、Core Data に保存するというものです。実用性はありませんが、どのようにZManagedObjectStorage
を使えば良いのかご確認いただけるようになっています。
[環境表示]
Apple Swift version 2.2 (swiftlang-703.0.18.1 clang-703.0.29)
Xcode 7.3 (7D175)
iOS 9.3 & OS X El Capitan 10.11