読者です 読者をやめる 読者になる 読者になる

pixyzehn blog

iPhone App, Mac App, Programming, Web service, Tool, Evernote, etc

【CoreData】NSFetchedResultsControllerで日付毎にセクションをまとめる方法をやってみました。

Xcode

f:id:inagex:20140417111644p:plain


NSFetchedResultsControllerで日付毎にセクションをまとめてみました。


http://qiita.com/takayama/items/aa3a7acd2f40ffb93dfb


こちらの記事をおおいに参考にしました。


まずCoreDataのことについてまとめてみました。


CoreDataでデータの永続化をするには、以下の手順で処理を作成していきます。


・XcodeでData Model(モデル構造)を作成
・NSManagedObjectContext のインスタンスを作成
・NSManagedObjectContext のインスタンスを通じて、データのCRUD*1を行う。
・CRUD操作結果をファイルに書き出す。


CoreDataを使うには以下のオブジェクトを利用します。


・NSManagedObjectModel
Data Modelで作成したモデル情報を読み込み、プログラムコード上で利用できるようにします。

・NSPersistentStoreCoordinator
NSPersistentStoreクラスを使い、ファイルの読み書きを行ないます。

・NSManagedObjectContext
データベースの1レコードに相当するNSPersistentObjectクラスのを使い、データの追加・更新・削除を行ないます。


上記のオブジェクトをそれぞれインスタンス化していきます。


ここではAppleのサンプルコードを利用してそれに変更を加えていきます。


https://developer.apple.com/library/IOS/samplecode/DateSectionTitles/Introduction/Intro.html

ダウンロードする


こちらのサンプルコードのCoreDataのNSManagedObjectModel、
NSPersistentStoreCoordinator、NSManagedObjectContextをインスタンス化する部分を以下のように変更します。

- (NSManagedObjectContext *)managedObjectContext
{
    if (_managedObjectContext != nil)
    {
        return _managedObjectContext;
    }
    
    NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
    if (coordinator != nil)
    {
        _managedObjectContext = [[NSManagedObjectContext alloc] init];
        [_managedObjectContext setPersistentStoreCoordinator:coordinator];
    }
    return _managedObjectContext;
}


- (NSManagedObjectModel *)managedObjectModel
{
    if (_managedObjectModel != nil)
    {
        return _managedObjectModel;
    }
    NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"DateSectionTitles" withExtension:@"momd"];
    _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
    return _managedObjectModel;
}

// NSPersistentStoreCoodinatorの生成
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
    if (_persistentStoreCoordinator != nil)
    {
        return _persistentStoreCoordinator;
    }
    
    NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"DateSectionTitles.dateSectionTitles"];

    BOOL firstRun = ![storeURL checkResourceIsReachableAndReturnError:NULL];

    NSError *error;
    _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
    // SQLiteを使用する
    if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error])
    {
        NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
        abort();
    }    

    // はじめての起動であれば初期値を保存する
	if (firstRun)
    {
	NSManagedObjectContext *context = [[NSManagedObjectContext alloc] init];
	[context setPersistentStoreCoordinator:_persistentStoreCoordinator];

	NSDateComponents *dateComponents = [[NSDateComponents alloc] init];
	[dateComponents setYear:2013];

        NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
        [dateFormatter setDateStyle:NSDateFormatterMediumStyle];
        [dateFormatter setTimeStyle:NSDateFormatterNoStyle];

	NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
        [dateFormatter setCalendar:calendar];

        for (NSInteger day = 1; day < 365; day += 7)
        {
		[dateComponents setDay:day];
		NSDate *date = [calendar dateFromComponents:dateComponents];

		APLEvent *newEvent = [NSEntityDescription insertNewObjectForEntityForName:@"APLEvent" inManagedObjectContext:context];
		newEvent.timeStamp = date;
                newEvent.title = [NSString stringWithFormat:@"Gregorian: %@ (day %ld)", [dateFormatter stringFromDate:date], (long)[dateComponents day]];
		}

		[context save:NULL];
	}

    return _persistentStoreCoordinator;
}


新規作成は、NSEntityDescriptionクラスの insertNewObjectForEntityForName:メッセージを利用します。


ここでは初回起動時のみ新規作成しています。

APLEvent *newEvent = [NSEntityDescription insertNewObjectForEntityForName:@"APLEvent" inManagedObjectContext:context];
newEvent.timeStamp = date;
newEvent.title = [NSString stringWithFormat:@"Gregorian: %@ (day %ld)", [dateFormatter stringFromDate:date], (long)[dateComponents day]];


こちらでも言及されているようにドット構文でアクセスしましょう。


potatotips - やはりお前らのCoreDataの使い方も間違っている - Qiitapotatotips - やはりお前らのCoreDataの使い方も間違っている - Qiitaはてなブックマーク - potatotips - やはりお前らのCoreDataの使い方も間違っている - Qiita


そして保存します。

[context save:NULL];


データの取り出しは以下の手順を処理をしていきます。


・NSFetchRequestインスタンスを作成する。
・NSFetchRequestのインスタンスにDBから検索する条件を設定する。
・NSFetchRequestのインスタンスを検索条件にNSFetchedResultsControllerのインスタンスを作成する。
・NSFetchedResultsControllerのインスタンスにperformFetch:メッセージを送り、検索結果を取得する。


こちらでいうとこの箇所になります。

#pragma mark - Fetched results controller

- (NSFetchedResultsController *)fetchedResultsController
{
    if (_fetchedResultsController != nil)
    {
        return _fetchedResultsController;
    }

    /*
	 Set up the fetched results controller.
     */
	// Create the fetch request for the entity.
	NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
	// Edit the entity name as appropriate.
	NSEntityDescription *entity = [NSEntityDescription entityForName:@"APLEvent" inManagedObjectContext:self.managedObjectContext];
	[fetchRequest setEntity:entity];

	// Set the batch size to a suitable number.
	[fetchRequest setFetchBatchSize:20];

	// Sort using the timeStamp property.
	NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"timeStamp" ascending:YES];
	[fetchRequest setSortDescriptors:@[sortDescriptor ]];

    // Use the sectionIdentifier property to group into sections.
    _fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:@"sectionIdentifier" cacheName:@"Root"];
    _fetchedResultsController.delegate = self;

	return _fetchedResultsController;
}    


NSPredicateによる検索条件の設定やsetFetchBatchSize:メッセージの設定はオプションです。これを設定しない場合、検索の絞り込みは行われません。


データの更新は特にコードはありません。


直接 NSManagedObject のインスタンスを書き換えれば更新となります。


ここにはないですが
データの削除は以下のような処理をしていきます。

  // self.deletableObjは削除したいオブジェクトとします
    NSManagedObject *obj = [self.deletableObj];
    if (! obj) {
        return;
    }

    // 値を取り除く
    [self.managedObjectContext deleteObject:obj];


今回CoreDataのEntityにセクション分類用のAttributeを追加して、Transientにチェックをいれてsection名が動的に変更するようにしています。


以前よりもちょっとはCoredataがわかってきた気がします。。


githubに今回のものをあげておきます。


https://github.com/pixyzehn/CoredataPractice


参考サイト


http://qiita.com/takayama/items/aa3a7acd2f40ffb93dfb


http://algoneet.com/39/%E6%99%82%E8%A8%88%E4%BB%95%E6%8E%9B%E3%81%91%E3%81%AEiphone%E3%82%A2%E3%83%97%E3%83%AA-6/


http://qiita.com/yimajo/items/9935bb1896fc5d2ea8e5

*1:CRUDとは「作成(Create)」「読み出し(Read)」「更新 (Update)」「削除(Delete)」をそれぞれ頭文字で表したものだそうです