私はリストアプリを作成し、それをコアデータでバックアップしてきました。
ユーザーが最初から始める必要がないように、たとえば10の空港のアイテムのデフォルトリストを作成したいと思います。
これを行う方法はありますか?
どんな助けでも大歓迎です。前もって感謝します。
私はリストアプリを作成し、それをコアデータでバックアップしてきました。
ユーザーが最初から始める必要がないように、たとえば10の空港のアイテムのデフォルトリストを作成したいと思います。
これを行う方法はありますか?
どんな助けでも大歓迎です。前もって感謝します。
最善の方法は次のとおりです(SQLの知識は必要ありません)
。リストアプリと同じオブジェクトモデルを使用して、簡単なCore Data iPhoneアプリ(またはMacアプリ)を作成します。数行のコードを記述して、保存するデフォルトの管理対象オブジェクトを保存します。次に、そのアプリをシミュレーターで実行します。次に、〜/ Library / Application Support / iPhone Simulator / User/Applicationsに移動します。GUIDの中からアプリケーションを見つけて、sqliteストアをリストアプリのプロジェクトフォルダーにコピーします。
次に、CoreDataBooksの例のようにそのストアをロードします。
はい、実際にはCoreDataBooksの例でこれを行っています。ここからコードをダウンロードできます:サンプルコード
通常の手順を使用して内部ストア(データベース)を作成し、他のストアと同じようにストアを初期化します。次に、コードを実行して、CoreDataBooksの例(以下のコードスニペット)で説明されているようにコードを実行します。 )。ストアが初期化されたら、を作成NSManagedObjectContext
して、作成された永続ストアで初期化し、必要なすべてのエンティティを挿入して、コンテキストを保存します。
コンテキストが正常に保存されたら、アプリケーションを停止し、ファインダーに移動してフォルダーに移動できます。~/Library/Developer
検索.sqliteと入力し、/ Developerの下を確認します。日付で並べ替えると、一致する最新の.sqliteデータベースが表示されます。コードが実行された時点で、このストアを取得してプロジェクトのリソースとして追加できます。このファイルは、永続ストアコーディネーターが読み取ることができます。
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
if (persistentStoreCoordinator) {
return persistentStoreCoordinator;
}
NSString *storePath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent: @"CoreDataBooks.sqlite"];
/*
Set up the store.
For the sake of illustration, provide a pre-populated default store.
*/
NSFileManager *fileManager = [NSFileManager defaultManager];
// If the expected store doesn't exist, copy the default store.
if (![fileManager fileExistsAtPath:storePath]) {
NSString *defaultStorePath = [[NSBundle mainBundle] pathForResource:@"CoreDataBooks" ofType:@"sqlite"];
if (defaultStorePath) {
[fileManager copyItemAtPath:defaultStorePath toPath:storePath error:NULL];
}
}
NSURL *storeUrl = [NSURL fileURLWithPath:storePath];
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]];
NSError *error;
if (![persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeUrl options:options error:&error]) {
// Update to handle the error appropriately.
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
exit(-1); // Fail
}
return persistentStoreCoordinator;
}
お役に立てば幸いです。
-オスカー
この方法では、別のアプリを作成したり、SQLの知識を持っている必要はありません。初期データ用のJSONファイルを作成できる必要があるだけです。
オブジェクトに解析したJSONファイルを使用して、それらをCoreDataに挿入します。これは、アプリの初期化時に行います。また、コアデータにこの初期データがすでに挿入されているかどうかを示すエンティティを1つ作成します。初期データを挿入した後、このエンティティを設定して、次にスクリプトを実行したときに、初期データがすでに初期化されていることを確認します。
jsonファイルをオブジェクトに読み込むには:
NSString *initialDataFile = [[NSBundle mainBundle] pathForResource:@"InitialData" ofType:@"json"];
NSError *readJsonError = nil;
NSArray *initialData = [NSJSONSerialization
JSONObjectWithData:[NSData dataWithContentsOfFile:initialDataFile]
options:kNilOptions
error:&readJsonError];
if(!initialData) {
NSLog(@"Could not read JSON file: %@", readJsonError);
abort();
}
次に、次のようにエンティティオブジェクトを作成できます。
[initialData enumerateObjectsUsingBlock:^(id objData, NSUInteger idx, BOOL *stop) {
MyEntityObject *obj = [NSEntityDescription
insertNewObjectForEntityForName:@"MyEntity"
inManagedObjectContext:dataController.managedObjectContext];
obj.name = [objData objectForKey:@"name"];
obj.description = [objData objectForKey:@"description"];
// then insert 'obj' into Core Data
}];
これを行う方法の詳細については、次のチュートリアルを確認してください: http ://www.raywenderlich.com/12170/core-data-tutorial-how-to-preloadimport-existing-data-updated
10個のアイテムの場合applicationDidFinishLaunching:
、アプリデリゲート内でこれを行うことができます。
insertPredefinedObjects
たとえば、空港アイテムの管理を担当するエンティティのインスタンスを作成してデータを設定し、コンテキストを保存するメソッドを定義します。ファイルから属性を読み取るか、コードに属性をハードワイヤードすることができます。次に、このメソッドを。内で呼び出しますapplicationDidFinishLaunching:
。
CoreDataBooksのサンプルコードに従う場合は、iOSデータストレージのガイドラインに違反している可能性があることに注意してください。
https://developer.apple.com/icloud/documentation/data-storage/
(読み取り専用の)事前入力されたデータベースをドキュメントディレクトリにコピーすることでアプリが拒否されました-その後iCloudにバックアップされるため-Appleはそれがユーザー生成ファイルにのみ発生することを望んでいます。
上記のガイドラインはいくつかの解決策を提供しますが、それらは主に次のように要約されます。
DBをcachesディレクトリに保存し、OSがキャッシュを削除する状況を適切に処理します。DBを再構築する必要があります。これにより、ほとんどの場合、DBが除外される可能性があります。
DBファイルに「キャッシュしない属性」を設定します。これは、OSのバージョンごとに異なる方法で実行する必要があるため、少し難解です。
トリッキーすぎるとは思いませんが、そのサンプルコードをiCloudと一緒に機能させるには少し余分なことがあることに注意してください...
これは私のために働いた。これはAndreaTosoによるこの回答の修正であり、このブログに触発されています。答えの唯一の問題は、FileManagerでsqliteファイルを移動するときにデータが失われる可能性があることです。FileManager.default.copyItemの代わりにreplacePersistentStoreを使用して、約500行のデータを保存しました
ステップ
1CoreDataを別のアプリに入力し、次のコードを使用してファイルのパスを取得します。
let paths = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)
let documentsDirectory = paths[0]
print(documentsDirectory)
ステップ
2.sqlite拡張子を持つ3つのファイルをxCodeプロジェクトにドラッグします。(必ず[ターゲットに追加]オプションを選択してください)。
ステップ3AppDelegate.swift
でアプリの最初の実行を確認する関数を作成します
func isFirstLaunch() -> Bool {
let hasBeenLaunchedBeforeFlag = "hasBeenLaunchedBeforeFlag"
let isFirstLaunch = !UserDefaults.standard.bool(forKey: hasBeenLaunchedBeforeFlag)
if (isFirstLaunch) {
UserDefaults.standard.set(true, forKey: hasBeenLaunchedBeforeFlag)
UserDefaults.standard.synchronize()
}
return isFirstLaunch
}
ステップ4この関数をAppDelegate.swiftにコピーして、sqliteデータベースを移動する必要のあるURLを取得します。
func getDocumentsDirectory()-> URL {
let paths = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)
let documentsDirectory = paths[0]
return documentsDirectory
}
ステップ
5persistentContainerの宣言を次の宣言に置き換えます。
// MARK: - Core Data stack
lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "ProjectName")
let storeUrl = self.getDocumentsDirectory().appendingPathComponent("FileName.sqlite")
if isFirstLaunch() {
let seededDataUrl = Bundle.main.url(forResource: "FileName", withExtension: "sqlite")
try! container.persistentStoreCoordinator.replacePersistentStore(at: storeUrl, destinationOptions: nil, withPersistentStoreFrom: seededDataUrl!, sourceOptions: nil, ofType: NSSQLiteStoreType)
}
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
この答えは、
Androidアプリ用に事前入力されたSQLiteデータベースを作成しました。それから、iOSバージョンのアプリを作成しているときに、CoreDataを使用するのが最善だと思いました。そのため、私はかなり長い時間をかけてCore Dataを学習し、コードを書き直してデータベースに事前入力しました。両方のプラットフォームですべてのステップを実行する方法を学ぶには、多くの調査と試行錯誤が必要でした。思っていたよりもずっとオーバーラップが少なかった。
結局、Androidプロジェクトから同じSQLiteデータベースを使用することにしました。次に、FMDBラッパーを使用して、iOSのデータベースに直接アクセスしました。メリット:
Core Dataを学んだことを後悔していませんが、それをやり直すとしたら、SQLiteに固執するだけで多くの時間を節約できたはずです。
iOSで開始し、Androidへの移行を計画している場合でも、FMDBやその他のソフトウェアなどのSQLiteラッパーを使用してデータベースに事前入力します。Core Dataで事前入力したSQLiteデータベースを技術的に抽出することはできますが、スキーマ(テーブル名や列名など)の名前は奇妙になります。
ちなみに、事前入力されたデータベースを変更する必要がない場合は、アプリのインストール後にデータベースをドキュメントディレクトリにコピーしないでください。バンドルから直接アクセスするだけです。
// get url reference to databaseName.sqlite in the bundle
let databaseURL: NSURL = NSBundle.mainBundle().URLForResource("databaseName", withExtension: "sqlite")!
// convert the url to a path so that FMDB can use it
let database = FMDatabase(path: databaseURL.path)
これにより、2つのコピーがなくなります。
アップデート
現在、FMDBではなくSQLite.swiftを使用しています。これは、Swiftプロジェクトとの統合が優れているためです。
そこで、辞書(おそらくJSONから)からロードしてデータベースにデータを取り込む一般的なメソッドを開発しました。(安全なチャネルからの)信頼できるデータでのみ使用する必要があり、循環参照を処理できず、スキーマの移行が問題になる可能性があります...しかし、私のような単純なユースケースでは問題ありません
ここに行きます
- (void)populateDBWithDict:(NSDictionary*)dict
withContext:(NSManagedObjectContext*)context
{
for (NSString* entitieName in dict) {
for (NSDictionary* objDict in dict[entitieName]) {
NSManagedObject* obj = [NSEntityDescription insertNewObjectForEntityForName:entitieName inManagedObjectContext:context];
for (NSString* fieldName in objDict) {
NSString* attName, *relatedClass, *relatedClassKey;
if ([fieldName rangeOfString:@">"].location == NSNotFound) {
//Normal attribute
attName = fieldName; relatedClass=nil; relatedClassKey=nil;
} else {
NSArray* strComponents = [fieldName componentsSeparatedByString:@">"];
attName = (NSString*)strComponents[0];
relatedClass = (NSString*)strComponents[1];
relatedClassKey = (NSString*)strComponents[2];
}
SEL selector = NSSelectorFromString([NSString stringWithFormat:@"set%@:", attName ]);
NSMethodSignature* signature = [obj methodSignatureForSelector:selector];
NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:signature];
[invocation setTarget:obj];
[invocation setSelector:selector];
//Lets set the argument
if (relatedClass) {
//It is a relationship
//Fetch the object
NSFetchRequest* query = [NSFetchRequest fetchRequestWithEntityName:relatedClass];
query.sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:relatedClassKey ascending:YES]];
query.predicate = [NSPredicate predicateWithFormat:@"%K = %@", relatedClassKey, objDict[fieldName]];
NSError* error = nil;
NSArray* matches = [context executeFetchRequest:query error:&error];
if ([matches count] == 1) {
NSManagedObject* relatedObject = [matches lastObject];
[invocation setArgument:&relatedObject atIndex:2];
} else {
NSLog(@"Error! %@ = %@ (count: %d)", relatedClassKey,objDict[fieldName],[matches count]);
}
} else if ([objDict[fieldName] isKindOfClass:[NSString class]]) {
//It is NSString
NSString* argument = objDict[fieldName];
[invocation setArgument:&argument atIndex:2];
} else if ([objDict[fieldName] isKindOfClass:[NSNumber class]]) {
//It is NSNumber, get the type
NSNumber* argument = objDict[fieldName];
[invocation setArgument:&argument atIndex:2];
}
[invocation invoke];
}
NSError *error;
if (![context save:&error]) {
NSLog(@"%@",[error description]);
}
}
}
}
そして、jsonからロードします...
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"initialDB" ofType:@"json"];
NSData *jsonData = [NSData dataWithContentsOfFile:filePath];
NSError* error;
NSDictionary *initialDBDict = [NSJSONSerialization JSONObjectWithData:jsonData
options:NSJSONReadingMutableContainers error:&error];
[ self populateDBWithDict:initialDBDict withContext: [self managedObjectContext]];
JSONの例
{
"EntitieA": [ {"Att1": 1 }, {"Att1": 2} ],
"EntitieB": [ {"Easy":"AS ABC", "Aref>EntitieA>Att1": 1} ]
}
と
{
"Country": [{"Code": 55, "Name": "Brasil","Acronym": "BR"}],
"Region": [{"Country>Country>code": 55, "Code": 11, "Name": "Sao Paulo"},
{"Country>Country>code": 55, "Code": 31, "Name": "Belo Horizonte"}]
}
オブジェクトが存在するかどうかを確認し、存在しない場合は、データを使用してオブジェクトを作成しますか?
NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] initWithEntityName:@"Settings"];
_managedObjectSettings = [[managedObjectContext executeFetchRequest:fetchRequest error:nil] mutableCopy];
if ([_managedObjectSettings count] == 0) {
// first time, create some defaults
NSManagedObject *newDevice = [NSEntityDescription insertNewObjectForEntityForName:@"Settings" inManagedObjectContext:managedObjectContext];
[newDevice setValue:[NSNumber numberWithBool: YES ] forKey:@"speed"];
[newDevice setValue:[NSNumber numberWithBool: YES ] forKey:@"sound"];
[newDevice setValue:[NSNumber numberWithBool: NO ] forKey:@"aspect"];
[newDevice setValue:[NSNumber numberWithBool: NO ] forKey: @"useH264"];
[newDevice setValue:[NSNumber numberWithBool: NO ] forKey: @"useThumbnail"];
NSError *error = nil;
// Save the object to persistent store
if (![managedObjectContext save:&error]) {
NSLog(@"Can't Save! %@ %@", error, [error localizedDescription]);
}
}
デフォルトを保存する別の方法は、NSUserDefaultsを使用して見つけることができます。(驚きです!)そしてそれは簡単です。
一部の人から提案された、それをapplicationDidFinishLaunching
デフォルトが10の場合、Airport0から9まで
設定
NSUserDefaults *nud = [NSUserDefaults standardUserDefaults];
[nud setString:@"MACADDRESSORWHY" forKey:@"Airport0"];
...
[nud setString:@"MACADDRESSORWHY" forKey:@"Airport9"];
[nud synchronize];
また
[[NSUserDefaults standardUserDefaults] setString:@"MACADDRESSORWHY" forKey:@"Airport9"]];
...
[[NSUserDefaults standardUserDefaults] synchronize];
そして、デフォルトを取得します。
NSString *air0 = [[NSUserDefaults standardUserDefaults] stringForKey:@"Airport0"];
ほとんどの回答はかなり古いので、次のチュートリアルをお勧めします。それがどのように行われるかを説明します。