ObjecTips

Swift & Objective-C で iOS とか macOS とか

NSPersistentStoreDescription のデフォルト値とカスタマイズ

iOS 10で導入された NSPersistentContainer を使うとCore Dataのセットアップのコードがずいぶん短くなる。Xcode のテンプレートはこんな感じ

- (NSPersistentContainer *)persistentContainer {
    @synchronized (self) {
        if (_persistentContainer == nil) {
            _persistentContainer = [[NSPersistentContainer alloc] initWithName:@"AppName"];
            [_persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription *storeDescription, NSError *error) {
                if (error != nil) {
                    NSLog(@"Unresolved error %@, %@", error, error.userInfo);
                    abort();
                }
            }];
        }
    }
    
    return _persistentContainer;
}

これだけで済んでしまう。
でも以前セットアップで使ってた NSMigratePersistentStoresAutomaticallyOption NSInferMappingModelAutomaticallyOption NSSQLitePragmasOption といったオプション設定はどうなっているの?と疑問に思ったので調査。

設定内容は _persistentContainer.persistentStoreDescriptions で取得できる NSPersistentStoreDescription のインスタンスから参照出来るらしい。
このインスタンスから取得可能なプロパティを調べてみると以下の様になった。

property value
type SQLite
configuration null
URL ~/Library/Application Support/<Name>.sqlite
options {
NSInferMappingModelAutomaticallyOption = 1;
NSMigratePersistentStoresAutomaticallyOption = 1;
}
readOnly NO
timeout 240
sqlitePragmas {
}
shouldAddStoreAsynchronously NO
shouldMigrateStoreAutomatically YES
shouldInferMappingModelAutomatically YES

設定をカスタマイズする

loadPersistentStoresWithCompletionHandler: を呼ぶ前に NSPersistentStoreDescription の各値を設定する事でカスタマイズが出来る。

例えば URL の様に readwrite なプロパティはそのまま素直に変更可能

_persistentContainer = [[NSPersistentContainer alloc] initWithName:@"AppName"];
NSPersistentStoreDescription *storeDescription = _persistentContainer.persistentStoreDescriptions.firstObject;
NSURL *URL = [storeDescription.URL.URLByDeletingLastPathComponent URLByAppendingPathComponent:@"NewLocation.sqlite"];
storeDescription.URL = URL;
[_persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription *storeDescription, NSError *error) {

optionssqlitePragmas は readonly なプロパティになっている代わりに以下の値設定用のメソッドが用意されているのでこれを使う。

- (void)setOption:(nullable NSObject *)option forKey:(NSString *)key;
- (void)setValue:(nullable NSObject *)value forPragmaNamed:(NSString *)name;

例えば以下で SQLite の journal_mode を shm とwal ファイルを生成しない DELETE モードに設定する事が出来る。

_persistentContainer = [[NSPersistentContainer alloc] initWithName:@"AppName"];
NSPersistentStoreDescription *storeDescription = _persistentContainer.persistentStoreDescriptions.firstObject;
[storeDescription setValue:@"DELETE" forPragmaNamed:@"journal_mode"];
[_persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription *storeDescription, NSError *error) {

これらのメソッドを使えばCore Dataのセットアップが従来通り柔軟に出来そうな目処が立つ。

ちなみに shouldMigrateStoreAutomaticallyshouldInferMappingModelAutomaticallyNO にすると optionsNSInferMappingModelAutomaticallyOptionNSMigratePersistentStoresAutomaticallyOptionNO になっていた。
おそらくこれらは良く使うオプションなのでプロパティのフラグ設定と自動で連携して簡単に options にキーと値を設定してくれる様になっているらしい。


参考 SQLite の pragma のリスト
https://sqlite.org/pragma.html