概要
この記事は二つのテーマについて書きます。
『SwiftでCoreDataを使うときの注意点』 & 『超忙しい人のためのCoreData入門』。
フレームワークの使い方に重きを置いています。
Swiftの記法はむしろ得意ではないので、お気づきの点はご指摘頂けると嬉しいです。
※Xcode6.1
CoreDataとは
CoreDataはCocoaFrameworkに搭載されているO/Rマッパー。高速、且つメモリー消費を抑えて大量のデータを扱うことができます。
データベースはSqliteであればCoreDataの機能をフルに使うことができます。
『SwiftでCoreDataを使うときの注意点』
CoreDataを使うにあたって、Objective-Cとの相違点について。
二つの注意点
SwiftでCoreDataを実装する際の注意点。たった2つです。
@NSManaged
と 名前空間です。
@NSManaged
Objective-Cの@dynamic
属性がSwiftではなくなりました。
代わりに@NSManaged
属性が追加されました。
@dynamic
属性
CoreDataではエンティティを表すモデルクラス(NSManagedObjectのサブクラス)のプロパティに@dynamic
属性を付加していました。
@dynamic
属性は@synthesize
属性と同様に、プロパティのアクセサを指定します。
属性 | プロパティの実装 |
---|---|
@synthesize |
コンパイル時に生成 |
@dynamic |
実行時に生成 |
と言えます。 |
CoreDataではフレームワークがプロパティを実行時に生成し、その実行効率はプログラマーが実装するよりも良いとされています。
@NSManaged
属性
Swiftで追加された機能です。
実装時は@dynamic
の単なる置き換えと考えて良いでしょう。やはり、実行時にプロパティの実装は実行時に生成されるようです。
唯一@dynamic
と異なる点は、この属性がCoreData専用の属性であるということです。
名前空間
Swiftでは名前空間の仕組みが取り入れられました。Objective-Cまではプロジェクト毎にプレフィックスを作ることが推奨されていましたが、それに置き換わる仕組みと私は解釈しています。リファレンスによるとモジュール毎(概ねプロジェクト毎)に名前空間のスコープが作られるとか。
CoreDataにおいてはUtilitiesでEntityのクラスを指定する際に
名前空間から始まるクラス名で記述しなければなりません。
名前空間はモジュール名…とリファレンスにありますが、ターゲット名がそれにあたるようです。
まとめ
Swiftでは@NSManaged
属性を使いましょう。
Entityのクラス名は名前空間から書きましょう。
『超忙しい人のためのCoreData入門』
私がCoreDataに手を出した時、とても敷居が高く感じました。
登場するクラスが多かったり、クラス名が長くて・似ていて混乱しました。
私なりに、CoreDataに初めて触れる方がわかったつもりになれる記事を目指します。
登場するクラス
CoreDataを理解するにはフレームワークに用意されたクラスを知る必要があります。
- NSPersistentStore
データベースの抽象化クラス - NSManagedObjectContext
エンティティモデルのお世話をするクラス - NSPersistentStoreCoordinator
NSPersistentStoreとNSManagedObjectContextの仲介クラス - NSManagedObjectModel
スキーマを表すクラス - NSManagedObject
エンティティクラス、モデルクラス、テーブルのレコード
主要なクラスは以上。
っと、いきなりですが、この入門では、1と3と4は忘れてください。
1. NSPersistentStore
データベースの抽象化クラス
2. NSManagedObjectContext
エンティティモデルのお世話をするクラス
3. NSPersistentStoreCoordinator
NSPersistentStoreとNSManagedObjectContextの仲介クラス
4. NSManagedObjectModel
スキーマを表すクラス
5. NSManagedObject
エンティティクラス、モデルクラス、テーブルのレコード
エンティティモデルのお世話をしてくれるNSManagedObjectContextを使って
エンティティモデルのNSManagedObjectを
作る(insert)
検索する(select)
変更する(update)
削除する(delete)
こんなイメージです。
この先はサンプルプロジェクトを作りながら追っていきます。
新規プロジェクト作成
「Use Core Data」にチェックを入れましょう。
すると、AppDelegateクラスにCoreDataに関するコードが自動生成されます。
先ほど忘れてくださいと言ったクラス達は、この自動生成されるコードで殆ど事足ります。
単一DB、単一スレッドでCoreDataを操作する場合は、変更の必要はほぼないでしょう。
また、3の__エンティティモデルのお世話をしてくれるNSManagedObjectContext__のゲッターまで自動生成されます。
残るはエンティティモデルのNSManagedObjectだけですね。
GUIでエンティティモデルを定義
プロジェクトに.xcdatamodeldという拡張子のファイルが初めから含まれているはずです。
このファイルを選択するとエンティティモデルを定義する画面に変わります。
①で囲ったところでテーブル名を定義
②で囲ったところでテーブルのカラムを定義するイメージです。
ここではModelというテーブルにsomeDataAというカラム(型はString)と
someDataBというカラム(Int16)を追加しました。
GUIでエンティティモデルのコードを生成
エンティティモデルの定義を元にモデルクラスを生成します。
Editorメニューから…
はい、
5. NSManagedObject
エンティティクラス、モデルクラス、テーブルのレコード
このNSManagedObjectのサブクラスとして生成します。
エンティティ同士のリレーションシップが無い場合は自動生成のコードのままで問題ないでしょう。
import Foundation
import CoreData
class Model: NSManagedObject {
@NSManaged var someDataA: String
@NSManaged var someDataB: NSNumber
}
ここまででCoreDataを使うためのお膳立ては全て終了です。
まだコードを1行も書いてません。
GUIでエンティティモデルを定義して、モデルクラスを自動生成しただけです。
CoreDataなんて怖くない。
データ挿入
データ挿入のサンプルです。
NSEntityDescriptionというクラスが登場します。
このクラスはエンティティのモデルクラスの定義とインスタンスを管理するクラスです。
// AppDelegateクラスのインスタンスを取得
let appDelegate = UIApplication.sharedApplication().delegate as AppDelegate
// AppDelegateクラスからNSManagedObjectContextを取得
// ゲッターはプロジェクト作成時に自動生成されている
if let managedObjectContext = appDelegate.managedObjectContext {
// NSEntityDescriptionから新しいエンティティモデルのインスタンスを取得
// 第一引数はモデルクラスの名前、第二引数はNSManagedObjectContext
let managedObject: AnyObject = NSEntityDescription.insertNewObjectForEntityForName("Model", inManagedObjectContext: managedObjectContext)
// エンティティモデルにデータをセット
let model = managedObject as CoreDataSample.Model
model.someDataA = "hogehoge"
model.someDataB = 100
// AppDelegateクラスに自動生成された saveContext で保存完了 appDelegate.saveContext()
}
データ検索
データの検索を行うサンプルです。
ここでも新しいクラスが登場します。
NSFetchRequestはSQLのSelect文、NSPredicateはSQLのWhere句と捉えるとイメージし易いと思います。
// AppDelegateクラスのインスタンスを取得
let appDelegate = UIApplication.sharedApplication().delegate as AppDelegate
// AppDelegateクラスからNSManagedObjectContextを取得
// ゲッターはプロジェクト作成時に自動生成されている
if let managedObjectContext = appDelegate.managedObjectContext {
// EntityDescriptionのインスタンスを生成
let entityDiscription = NSEntityDescription.entityForName("Model", inManagedObjectContext: managedObjectContext);
// NSFetchRequest SQLのSelect文のようなイメージ
let fetchRequest = NSFetchRequest();
fetchRequest.entity = entityDiscription;
// NSPredicate SQLのWhere句のようなイメージ
// someDataBプロパティが100のレコードを指定している
let predicate = NSPredicate(format: "%K = %d", "someDataB", 100)
fetchRequest.predicate = predicate
var error: NSError? = nil;
// フェッチリクエストの実行
if var results = managedObjectContext.executeFetchRequest(fetchRequest, error: &error) {
for managedObject in results {
let model = managedObject as Model;
println("String: \(model.someDataA), Number: \(model.someDataB)");
}
}
}
データの更新
データ更新のサンプルです。
someDataAプロパティが"hogehoge"のレコードを"hugahuga"に更新します。
処理は上のデータ検索と大きく変わりません。
// AppDelegateクラスのインスタンスを取得
let appDelegate = UIApplication.sharedApplication().delegate as AppDelegate
// AppDelegateクラスからNSManagedObjectContextを取得
// ゲッターはプロジェクト作成時に自動生成されている
if let managedObjectContext = appDelegate.managedObjectContext {
// EntityDescriptionのインスタンスを生成
let entityDiscription = NSEntityDescription.entityForName("Model", inManagedObjectContext: managedObjectContext);
// NSFetchRequest SQLのSelect文のようなイメージ
let fetchRequest = NSFetchRequest();
fetchRequest.entity = entityDiscription;
// NSPredicate SQLのWhere句のようなイメージ
let predicate = NSPredicate(format: "%K = %@", "someDataA", "hogehoge")
fetchRequest.predicate = predicate
var error: NSError? = nil;
// フェッチリクエストの実行
if var results = managedObjectContext.executeFetchRequest(fetchRequest, error: &error) {
for managedObject in results {
let model = managedObject as Model;
//
// レコードの更新!
// ここまではデータ検索のサンプルと同じ
//
model.someDataA = "hugahuga"
}
}
// AppDelegateクラスに自動生成された saveContext で保存完了
appDelegate.saveContext()
}
データの削除
データ更新のサンプルです。
someDataAプロパティが"hugahuga"のレコードを削除します。
// AppDelegateクラスのインスタンスを取得
let appDelegate = UIApplication.sharedApplication().delegate as AppDelegate
// AppDelegateクラスからNSManagedObjectContextを取得
// ゲッターはプロジェクト作成時に自動生成されている
if let managedObjectContext = appDelegate.managedObjectContext {
// EntityDescriptionのインスタンスを生成
let entityDiscription = NSEntityDescription.entityForName("Model", inManagedObjectContext: managedObjectContext);
// NSFetchRequest SQLのSelect文のようなイメージ
let fetchRequest = NSFetchRequest();
fetchRequest.entity = entityDiscription;
// NSPredicate SQLのWhere句のようなイメージ
let predicate = NSPredicate(format: "%K = %@", "someDataA", "hugahuga")
fetchRequest.predicate = predicate
var error: NSError? = nil;
// フェッチリクエストの実行
if var results = managedObjectContext.executeFetchRequest(fetchRequest, error: &error) {
for managedObject in results {
let model = managedObject as Model;
//
// レコード削除!
//
managedObjectContext.deleteObject(model);
}
}
// AppDelegateクラスに自動生成された saveContext で保存完了
appDelegate.saveContext()
}
カンのいい方はすでにお気づきでしょうが、NSManagedObjectクラス(このサンプルではModelクラス)のインスタンスを取得するまではデータの検索と同じ処理です。
インスタンスを取得したあとは、NSManagedObjectContext経由で変更を保存するか、削除するかの違いです。
この章、冒頭に述べた通り
エンティティモデルのお世話をしてくれるNSManagedObjectContextを使って
エンティティモデルのNSManagedObjectを
作る(insert)
検索する(select)
変更する(update)
削除する(delete)
これだけです。
おしまい
CoreData入門でした。入り口まではご案内できましたでしょうか。
必要最低限のインプットでCoreDataが操作できるコードを載せたつもりです。
新たにCoreDataをはじめる、足がかりにしていただけたら幸いです。