hmhv

HobjectiveRecordの紹介

2015-03-09

HobjectiveRecordとは

HobjectiveRecordはObjective-C製のCoreData用ライブラリーです。
オープンソースでGithubから自由に利用できます。

HobjectiveRecordの特徴

短いコード量でCoreDataを自由に操作できます。
特にバッググラウンド処理をベースにしているのでUIスレッドをブロックせず使うことも可能です。

CoreDataのバッググラウンド処理の詳細は以下の内容を参考してください。

HobjectiveRecordを利用

利用準備

  1. Github -> HobjectiveRecordからダウンロードしてHobjectiveRecordフォルダを利用するプロジェクトにコピー、
    又はCocoaPodsを利用 pod 'HobjectiveRecord'
  2. 必要なファイルに#import "HobjectiveRecord.h"を追加

初期化

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // まずCoreDataの利用前にストアを初期化
    [NSPersistentStoreCoordinator setupDefaultStore];
    
    // your code here
    
    return YES;
}
/*
+ (void)setupDefaultStore;
+ (void)setupDefaultStoreWithModelURL:(NSURL *)modelURL storeURL:(NSURL *)storeURL useInMemoryStore:(BOOL)useInMemoryStore;
*/

Basic

基本、全ての処理はperformBlock:内で行う。

[[NSManagedObjectContext defaultContext] performBlock:^{
  // your code here
}];
NSManagedObjectContext *childContext = [[NSManagedObjectContext defaultContext] createChildContext];
[childContext performBlock:^{
  // your code here
}];

Create / Save / Delete

[[NSManagedObjectContext defaultContext] performBlock:^{
    Tweet *tweet = [Tweet create];
    tweet.text = @"I am here";
    [tweet save];
    [tweet delete];
    
    tweet = [Tweet create:@{@"text" : @"hello!!",
                            @"lang" : @"en"} ];
    [tweet saveToStore];
    
    [Tweet deleteAll];
}];
/*
+ (instancetype)create;
+ (instancetype)create:(NSDictionary *)attributes;
+ (void)deleteAll;
+ (void)deleteWithCondition:(id)condition;
- (void)save;
- (void)saveToStore;
- (void)delete;
*/

Finders

[[NSManagedObjectContext defaultContext] performBlock:^{
    NSArray *tweets = [Tweet all];
    
    NSArray *tweetsInEnglish = [Tweet find:@"lang == 'en'"];
    
    User *hmhv = [User first:@"screenName == 'hmhv'"];
    
    NSArray *englishMen = [User find:@{@"lang" : @"en",
                                       @"timeZone" : @"London"
                                       }];
    
    NSPredicate *predicate = [NSPredicate predicateWithFormat:@"friendsCount > 100"];
    NSArray *manyFriendsUsers = [User find:predicate];
}];
/*
+ (NSArray *)all;
+ (NSArray *)find:(id)condition;
+ (instancetype)first:(id)condition;
+ (instancetype)firstOrCreate:(NSDictionary *)condition;
*/

Order and Limit

[[NSManagedObjectContext defaultContext] performBlock:^{
    NSArray *sortedUsers = [User allWithOrder:@"name"];
    
    NSArray *allUsers = [User allWithOrder:@"screenName ASC, name DESC"];
    // or
    NSArray *allUsers2 = [User allWithOrder:@"screenName A, name D"];
    // or
    NSArray *allUsers3 = [User allWithOrder:@"screenName, name d"];

    NSArray *manyFriendsUsers = [User find:@"friendsCount > 100" order:@"screenName DESC"];
    
    NSArray *fiveEnglishUsers = [User find:@"lang == 'en'" order:@"screenName ASC" limit:@(5)];
}];
/*
+ (NSArray *)allWithOrder:(NSString *)order;
+ (NSArray *)find:(id)condition order:(NSString *)order;
+ (NSArray *)find:(id)condition limit:(NSNumber *)limit;
+ (NSArray *)find:(id)condition order:(NSString *)order limit:(NSNumber *)limit;
*/

Aggregation

[[NSManagedObjectContext defaultContext] performBlock:^{
    NSUInteger allUserCount = [User count];
    
    NSUInteger englishUserCount = [User countWithCondition:@"lang == 'en'"];
}];
/*
+ (NSUInteger)count;
+ (NSUInteger)countWithCondition:(id)condition;
*/

BatchUpdate

[[NSManagedObjectContext defaultContext] performBlock:^{
    
    [User batchUpdateWithCondition:@"friendsCount > 10" propertiesToUpdate:@{@"friendsCount": @0}];
    
    // update all entities
    [User batchUpdateWithCondition:nil propertiesToUpdate:@{@"friendsCount": @100}];
}];
/*
+ (NSUInteger)batchUpdateWithCondition:(id)condition propertiesToUpdate:(NSDictionary *)propertiesToUpdate;
+ (NSUInteger)batchUpdateWithCondition:(id)condition propertiesToUpdate:(NSDictionary *)propertiesToUpdate inContext:(NSManagedObjectContext *)context;
*/

NSFetchedResultsController

    [[NSManagedObjectContext defaultContext] performBlock:^{
        NSFetchedResultsController *frc = [User createFetchedResultsControllerWithCondition:nil order:@"name" sectionNameKeyPath:nil];
        frc.delegate = self;
        
        NSError *error = nil;
        if ([frc performFetch:&error]) {
            [self reloadData];
        }
    }
}];
/*
+ (NSFetchedResultsController *)createFetchedResultsControllerWithCondition:(id)condition order:(NSString *)order sectionNameKeyPath:(NSString *)sectionNameKeyPath;
*/

Custom ManagedObjectContext

NSManagedObjectContext *childContext = [[NSManagedObjectContext defaultContext] createChildContext];

[childContext performBlock:^{
    User *john = [User createInContext:childContext];
    john.name = @"John";
    [john save];
    
    john = [User first:@"name == 'John'" inContext:childContext];
    
    NSArray *manyFriendsUsers = [User find:@"friendsCount > 100" order:@"screenName DESC" inContext:childContext];
    
    NSArray *allUsers = [User allInContext:childContext];
}];

カスタムモデル、カスタムsqliteファイルの利用

NSURL *modelURL = [NSURL defaultModelURL:@"model_name"];
[NSPersistentStoreCoordinator setupDefaultStoreWithModelURL:modelURL storeURL:nil useInMemoryStore:NO];
    
// or
NSURL *storeURL = [NSURL defaultStoreURL:@"file_name.sqlite"];
[NSPersistentStoreCoordinator setupDefaultStoreWithModelURL:nil storeURL:storeURL useInMemoryStore:NO];

マッピング

ウェブサービスからのJSONはfirst_name, last_nameのようなスネークケースの場合が多いですが、
Objective-CではfirstName, lastNameのようにキャメルケースが主流です。
なのでスネークケースからキャメルケースへのマッピングは自動で行われます。
それ以外の特別な名前マッピングが必要な場合は各モデルスラスでmappingsをオーバライドして設定可能です。

!! Date, Transformableタイプはマッピング対象外です。 !!

// just override +mappings in your NSManagedObject subclass
#import "User+Mappings.h"

@implementation User (Mappings)

+ (NSDictionary *)mappings
{
    return @{@"description" : @"userDescription",
             @"name" : @"fullName"};
}
  // first_name => firstNameは自動でマッピングされる。

@end
/*
+ (NSDictionary *)mappings
+ (BOOL)useFindOrCreate
+ (BOOL)returnsObjectsAsFaults
+ (NSArray *)relationshipKeyPathsForPrefetching
*/

テスト

HobjectiveRecordはメモリ内ストアを設定可能です。テストなどで利用すると便利です。

[NSPersistentStoreCoordinator setupDefaultStoreWithModelURL:nil storeURL:nil useInMemoryStore:YES];