iOS sqliteのハンドリングにFMDBを使ってみた

一度使ってみたいと思っていた FMDBを使ってみました。
今回使ってませんが、 FMDatabaseQueue というようなマルチスレッドのキューイングもあるようで
なかなか便利そうです。あと、Evernoteさんでも使ってますし。

FMDB

導入

cocoapodsを使えば簡単です。

pod 'FMDB'

pod install
と。  
 

licenseは

MITですが、

If you are using fmdb in your project, I'd love to hear about it.  Let me 
know at gus@flyingmeat.com.

LICENSE.txt にこう記載されているので、
出来上がったら連絡してあげると喜んでくれるかもしれません。
 
 

使い方

基本的な使い方はこちらとても参考になりました。
iOS で SQLite – FMDB の使い方 | アカベコマイリ
 
 
ちょっと汎用的にしたかったのでselectのところを以下のように書いてみました。

- (NSArray *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray *)arguments
{
    NSString *dbPath = [self dbPath];
    FMDatabase *db= [FMDatabase databaseWithPath:dbPath];
    FMResultSet *rs = nil;
    NSMutableArray *ret = [NSMutableArray array];
    
    if (![db open]) {
        return nil;
    }
    rs = [db executeQuery:sql withArgumentsInArray:arguments];
    while ([rs next]) {
        [ret addObject:[rs resultDictionary]];
    }
    [db close];
    return [ret copy];
}

戻り値はDictionaryの配列で返すようにしています。
1つ目のポイントは、executeQuery:sql withArgumentsInArray:arguments で
sqlのplaceholderに値するところを配列で渡してます。
これで使う側は、動的にsqlを呼べます。
 
2つ目のポイントは、FMResultSet の resultDictionary です。
これで結果をNSDictionaryにしてくれるのですが、日付型は注意が必要です。
FMDBの場合、sqliteはREAL型でobjective-cはNSDate型にしておくと
FMDBがよしなになってくれて色々良いのですが、これ使っちゃうとUnixTimestampそのまんまで
扱われてしまいます。
FMResultSetの dateForColumnIndex:columnIdx というのを使えば NSDateにしてくれるので (中で NSDate dateWithTimeIntervalSince1970 呼んでるだけ)
カラム名がなんとかdateとかになっているのは自動で変換するみたいなのを書いてあげたりすればよさそうです。
 
 
あとは使う側(モデル)で以下のようにすればok。

+ (instancetype)findByRecordedDate:(NSDate *)recordedDate
{
    WCDataManager *dataMgr = [WCDataManager sharedInstance];
    NSString *sql = @"SELECT * FROM weights WHERE recorded_date = ?";
    NSArray *resultsDic = [dataMgr executeQuery:sql withArgumentsInArray:@[recordedDate]];
    
    if ([resultsDic count] == 0) {
        return nil;
    }
    return [self dictionaryToEntity:resultsDic[0]];
}

+ (instancetype)dictionaryToEntity:(NSDictionary *)dic
{
    WCWeight *weight = [WCWeight new];
    NSTimeInterval interval = [dic[@"recorded_date"] doubleValue];
    weight.recordedDate     = [NSDate dateWithTimeIntervalSince1970:interval];
    weight.value            = dic[@"value"];
    return weight;
}

 
 

使ってみて

お手軽っちゃー、お手軽です。
ActiveRecordに慣れた身としてはORMとしてもっと使い易いのが欲しいところではあります。
やっぱりCoreDataと比べてというところだと
お手軽さはFMDBですが、Xcode上でモデリングツールが使えてマイグレーション用のmappingも書けるなど
CoreDataでやっとく方がなんやかんやで便利な印象です。
CoreDataとっつきにくいし時々はまるんですが、一回書いちゃうと大体一緒ですし
速度的なところで文句無ければCoreDataでいいかなと思いました。
あとは、既存のsqliteのデータをそのまま使うならFMDBって感じでしょうか。

See Also

iOS で SQLite – FMDB の使い方 | アカベコマイリ
SQLite+FMDBで日付型を扱う場合の注意点 « sabitori works
 
ccgus/fmdb