1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

ZManagedObjectStorage で Core Data をちょっとだけ便利に

Last updated at Posted at 2016-06-18

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 のファイルがない場合には新たに作られます。

.swift
let documentURL = ...
let storage = ZManagedObjectStorage(fileURL: documentURL, modelName: "MyModel")

あとは、managed object context はmanagedObjectContextプロパティをアクセスすればあとは、通常の Core Data のようにオブジェクトを Fetch したり Insert したりできます。

.swift
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)")
}
.swift
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では、モデルを別に指定するようにしています。

ソース

ZManagedObjectStorage.swift
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 を使えば良いのかご確認いただけるようになっています。

Simulator-Screen-Shot-Jun-19,-2016,-3.17.58-AM-2.png

[環境表示]

.console
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
1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?