初投稿。iOSアプリ開発ビギナーで至らないところもあるかと思いますがよろしくお願いします(ツッコミとか)。
データの永続化を行うにあたり、iOSではフラットファイル、SQLite、CoreDataを使うことが出来ます。前者2つはウェブ・アプリケーションで多少馴染みがあるものの、Cocoaプラットフォームでのデータ永続化はさっぱりわからずCoreDataって何?という状態からスタートしました。
このドキュメントでカバーすること:
- CoreData, mogenerator, MagicalRecordがどんなものであるかとその使い方
このカバーしないこと:
- Xcodeの基本的な使い方
- CoreData, mogenerator, MagicalRecord以外のライブラリのインストール方法
登場人物
CoreData
CoreDataプログラミングガイドより。
Core Dataフレームワークは、オブジェクトの永続性をはじめ、ライフサイクルやオブジェクトグラフの管理に関連して、頻繁に必要となるさまざまな処理機能を、汎用化、自動化して提供します。
Appleは CoreDataはORMではないと明言しています が、実際のところデータベースの一種(関連データベース=Relational Databaseではない)であり、プログラムからモデルクラスを通して操作する点はORMの概念と似通っています。
例えばPython / Djangoではモデルの作成を次のように行いますが、
bike = Bike()
bike.brand = u"Ravanello"
bike.name = u"SAT"
bike.save()
CoreData + MagicalRecordでは次のように行います。
Bike *bike = [Bike MR_createEntity];
bike.brand = @"Ravanello";
bike.name = @"SAT";
[[NSManagedObjectContext MR_context] MR_saveToPersistentStoreAndWait]; // この辺ちょっとうろ覚え
個人的にはほとんど同じ概念で扱えるんじゃないかと思ってます。
mogenerator
Xcode上でのモデル定義は、モデルエディタによりGUIで行い、NSManagementObjectのサブクラスとして、ヘッダ、実装ファイルに出力することが出来ます。この機能は一見便利なのですが、出力したファイルは次の問題があるように思います。
- Xcodeからファイルを出力すると既存のファイルが上書きされる
- 既存のファイルに実装があった場合、上書きで削除される
- 結果として、モデルクラスに独自の実装を追加することが出来ない(カテゴリ作って拡張したけど悪手すぎた)
mogeneratorはカスタマイズ可能なモデルクラスを作成することを目的として開発されており、Xcodeで作成したモデル定義を元に次の2つのクラスを作成します。
- mogenerator独自の内部クラス
- カスタマイズ用のサブクラス
カスタマイズ用のサブクラスには任意の実装を追加することが出来て、mogeneratorでファイルの再出力を行っても上書きされません。つまり、mogeneratorを使用することでカスタマイズの柔軟性を確保することが出来ます。
MagicalRecord
MagicalRecord本家サイトによれば、Ruby on RailsのActiveRecordにインスパイアされたCoreDataラッパーライブラリである、となります。
このライブラリを使用すると、CoreDataの初期化手順、モデルのフェッチなどを短い記述で行えるようになり、要はCoreDataの面倒くさい手続きを代わってくれるライブラリです(と個人的には考えています)。
インストール
mogenerator
公式の配布物からインストールするか、homebrewからインストールする。
brewの場合はbrewコマンドからどうぞ。
brew install mogenerator
MagicalRecord
CocoaPodsからインストールする。Podfileに以下を書きます。
pod 'MagicalRecord'
pod installします。
/usr/bin/pod install
インストールすると.pchファイルに次の行が追加されているので確認します。
#import "CoreData+MagicalRecord.h"
使い方
モデルの定義
mogeneratorによるヘッダ、実装ファイル出力
mogeneratorによるファイル出力は、入力にXcodeで作成したモデルファイルを、出力先に適当なディレクトリを指定します。
こんな感じ。
/usr/bin/mogenerator \
--template-var arc=true \
-m ./example/Model/Model.xcdatamodeld \
-O ./example/Model/
オプションを1つずつ超簡単に解説します。
--template-var arc=true
ARC環境で利用するには--template-var arc=true
を指定します(これ以上のことは調べていません!)。
-m ./example/Model/Model.xcdatamodeld
Xcodeで作成したモデルファイルを指定します。
-O ./example/Model/
ヘッダ、実装ファイルの出力先を指定します。
mogeneratorは機械生成によるmogenerator内部クラスを machine file, カスタマイズするためのサブクラスファイルを human file と呼んでいます。machine fileはファイル名のプリフィクスに_
が付いていて、内部クラスとして認識出来る形になっています。一方human fileにはXcodeで定義したモデルクラス名がそのまま使用されています。
例えばモデル定義がBike
だとしたら、
- machine file:
_Bike.h
,_Bike.m
- human file:
Bike.h
,Bike.m
となります。
なお、ファイル管理の都合でmachine file, human fileの出力先を分けたい場合には、--machine-dir
と--human-dir
オプションを利用してください。
mogeneratorの実行タイミングは用途によりけりですが、mogeneratorからのファイル出力はbuild phaseの1つとして加えても良いですし、必要なタイミングで都度コマンドラインから実行しても良いと思います。
MagicalRecordによるCoreDataの初期化
CoreDataの初期化はapplication:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
でMagicalRecordのセットアップメソッドを呼び出します。setupCoreDataStackWithStoreNamed:
メソッドでは、CoreDataのバックエンドとしてSQLiteを使用し、任意のデータベースファイル(ここではdb.sqlite)を作成することを意味します。
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
// initialize coredata
[MagicalRecord setupCoreDataStackWithStoreNamed:@"db.sqlite"];
// (snip)
}
次に、アプリケーション終了時に正しくデータを永続化するためapplicationWillTerminate:(UIApplication *)application
内でMagicalRecordのcleanUp
メソッドを呼び出します。
- (void)applicationWillTerminate:(UIApplication *)application
{
[MagicalRecord cleanUp];
}
モデルの作成、読み出し、更新、削除
// create
Bike *bike = [Bike MR_createEntity];
// retrieve
Bike *bike = [Bike MR_findFirstByAttribute:@"id" withValue:@1];
// update
Bike *bike = [Bike MR_findFirstByAttribute:@"id" withValue:@1];
bike.name = @"Colnago";
[[NSManagedObjectContext MR_defaultContext] MR_saveToPersistentStoreAndWait]; // この辺ちょっとうろ覚え
// delete
[bike MR_deleteEntity];
[[NSManagedObjectContext MR_defaultContext] MR_saveToPersistentStoreAndWait]; // この辺ちょっとうろ覚え
モデルのカスタマイズ
モデルをカスタマイズする場合には、mogeneratorが出力した human file を編集してください。次のファイルは某アプリの写真管理部分のソースコードです。
通常のObjective-Cプログラミングと同様に、ヘッダに宣言をして実装を行います。
Photo.h
#import "_Photo.h"
typedef NS_ENUM(NSUInteger, kMyBikePhotoSize) {
kMyBikePhotoSizeSmall,
kMyBikePhotoSizeMedium,
kMyBikePhotoSizeLarge,
kMyBikePhotoSizeOriginal
};
@interface Photo : _Photo {}
- (NSURL *)getThumbnailUrlWithSize:(enum kMyBikePhotoSize)photoSize;
@end
Photo.m
#import "Photo.h"
@interface Photo ()
// Private interface goes here.
@end
@implementation Photo
- (NSURL *)getThumbnailUrlWithSize:(enum kMyBikePhotoSize)photoSize
{
NSString *path = [NSString stringWithFormat:kMyBikeApiPhotoDetail, @"bike_thumbnail", self.id];
return [self urlWithPath:path photoSize:photoSize];
}
@end
というわけで私的メモ的なCoreData, MagicalRecord, mogeneratorの導入話はおしまいです。何か得ることがあったらまたアップデートすると思います。