【iOS開発】Core Dataの基礎を学習

こんにちは
ビンゴ中西です。

弊社では凄腕エンジニアがCore Dataの難しい部分を
ある程度隠蔽して使えるようにしてくれていますので、
以下の手順を丸っと踏むことはないのですが、
今回はCore Dataの基礎を学びます。

1. プロジェクトを作成したらCoreData.frameworkを追加

何も考えずにCoraData.frameworkを追加しましょう。
とくにダウンロードすることもなくXcodeから普通にできます。

2. モデルをペコペコ作っていくファイルを作成


今回は、

Model.xcdatamodeld

という名前で作ってみました。

3. エンティティを作ってみる


Usersというエンティティで nameとageを持たせました。

4. いよいよソースを実装しよう! その前に.....

おっしゃ!ソースが書けると思いきや、その前に、
NSManagedObjectを継承したUsersクラスを作成します。

NSManagedObject subclassを選んで、
Model.xcdatamodeldのUsersが選ばれるように作成します。

5. 今度こそソースを!

今度こそソースを書こうということで、
サルでもわかる Core Data 入門【概念編】 - A Day In The Life
を参考にさせていただきました。

ソースは下記などを参考に少し書き換えています。
iPhoneのディレクトリ - bi_naの日記
iphone - CoreData application directory crashes on iOS3 - Stack Overflow]

- (void)viewDidLoad
{
    [super viewDidLoad];
	// Do any additional setup after loading the view, typically from a nib.
    // NSManagedObjectModel
    
    NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"Model" withExtension:@"momd"];
    NSManagedObjectModel *managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
    // NSPersistentStoreCoordinator
    NSPersistentStoreCoordinator *coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:managedObjectModel];
    
    NSArray *searchPaths   = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentPath = [searchPaths lastObject];
    NSString *path         = [documentPath stringByAppendingPathComponent:@"Hoge.sqlite"];
    NSURL *storeURL         = [NSURL fileURLWithPath:path];
    
    [coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:nil];
    // NSManagedObjectContext
    NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init];
    [context setPersistentStoreCoordinator:coordinator];
    
    // context は NSManagedObjectContext クラスのインスタンス
    NSManagedObject *managedObject = [NSEntityDescription insertNewObjectForEntityForName:@"Users"
                                                                   inManagedObjectContext:context];
    NSError *error = nil;
    if (![context save:&error]) {
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    }
}

5.1 ソースの説明

手順2で作ったファイル名を教えてやる。

NSURL *modelURL =
     [[NSBundle mainBundle] URLForResource:@"Model" withExtension:@"momd"];
NSManagedObjectModel *managedObjectModel =
    [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];

5.1.2 いやいやmomdってなによ?

momdってなんなでしょう?
Cocoaの日々: [iOS][Mac] CoreData - マイグレーション[4] モデルファイルの構成
こちらに詳しい説明がありました。

たしかに
.appファイルを開いてみると存在しました。

5.1.3 SQLiteファイルを作って教えてやる

たしかに、

存在しています。

6. データを登録したい!

サルでもわかる Core Data 入門【実装編】 - A Day In The Life
今度はこちらのソースをgithubからDownloadして直に読むことで、
以下の様に手を加えてみました。

- (void)viewDidLoad
{
    [super viewDidLoad];
	// Do any additional setup after loading the view, typically from a nib.
    // NSManagedObjectModel
    
    NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"Model" withExtension:@"momd"];
    NSManagedObjectModel *managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
    // NSPersistentStoreCoordinator
    NSPersistentStoreCoordinator *coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:managedObjectModel];
    
    NSArray *searchPaths   = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentPath = [searchPaths lastObject];
    NSString *path         = [documentPath stringByAppendingPathComponent:@"Hoge.sqlite"];
    NSURL *storeURL        = [NSURL fileURLWithPath:path];
    
    [coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:nil];
    // NSManagedObjectContext
    NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init];
    [context setPersistentStoreCoordinator:coordinator];
    
    
    // データ書き込み
    Users *u = [NSEntityDescription insertNewObjectForEntityForName:NSStringFromClass([Users class])
                                                inManagedObjectContext:context];
    u.name = @"bingo";
    u.age  = @19;
    
    NSError *error = nil;
    if (![context save:&error]) {
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    }
    
}

6.1 確認してみた


入ってますね。

7. データを取り出してみよう

[XCODE] CoreDataを用いてデータ管理を行う方法。検索編!!!! - YoheiM .NET
を参考にさせていただき、viewDidLoadを書き換えました。
なお、データは
name => bingo, age => 19
name => miya, age => 20
の2件を入れた状態を行っています。

- (void)viewDidLoad
{
    [super viewDidLoad];
	// Do any additional setup after loading the view, typically from a nib.
    // NSManagedObjectModel
    
    NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"Model" withExtension:@"momd"];
    NSManagedObjectModel *managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
    // NSPersistentStoreCoordinator
    NSPersistentStoreCoordinator *coordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:managedObjectModel];
    
    NSArray *searchPaths   = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentPath = [searchPaths lastObject];
    NSString *path         = [documentPath stringByAppendingPathComponent:@"Hoge.sqlite"];
    NSURL *storeURL        = [NSURL fileURLWithPath:path];
    
    [coordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:nil];
    // NSManagedObjectContext
    NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init];
    [context setPersistentStoreCoordinator:coordinator];
    
    
    // NSFetchRequestは、検索条件などを保持させるオブジェクトです。
    // 後続処理では、このインスタンスに色々と検索条件を設定します。
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
    
    // 検索対象のエンティティを指定します。
    NSEntityDescription *entity
    = [NSEntityDescription entityForName:@"Users" inManagedObjectContext:context];
    [fetchRequest setEntity:entity];
    
    // 一度に読み込むサイズを指定します。
    [fetchRequest setFetchBatchSize:20];
    
    // 検索結果を保持する順序を指定します。
    // ここでは、keyというカラムの値の降順で保持するように指定しています。
    NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"age" ascending:NO];
    [fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];
    

    // NSFetchedResultsControllerを作成します。
    // 上記までで作成したFetchRequestを指定します。
    NSFetchedResultsController *fetchedResultsController
    = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest
                                          managedObjectContext:context
                                            sectionNameKeyPath:nil
                                                     cacheName:nil];
    
    // データ検索を行います。
    // 失敗した場合には、メソッドはfalseを返し、引数errorに値を詰めてくれます。
    NSError *error = nil;
    if (![fetchedResultsController performFetch:&error]) {
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
    }
    
    
    // 検索結果をコンソールに出力してみます。
    // fetchedObjectsというメソッドで、検索結果一覧を配列で受け取れます。
    NSArray *moArray = [fetchedResultsController fetchedObjects];
    for (int i = 0; i < moArray.count; i++) {
        Users *u = [moArray objectAtIndex:i];
        NSLog(@"name=%@, age=%@", u.name, u.age);
    }
    
    
}

結果:

2012-12-07 18:58:50.796 coreDataSample[7829:11603] name=miya, age=20
2012-12-07 18:58:50.797 coreDataSample[7829:11603] name=bingo, age=19

取得できていました。

んー エンティティ名はUsersじゃなくてUserのが良さそうな予感....