Есть какой-нибудь способ предварительного заполнения основных данных?

StackOverflow https://stackoverflow.com/questions/2230354

Вопрос

Я создавал приложение list и поддерживал его с помощью core data.

Я хотел бы иметь список по умолчанию, скажем, из 10 элементов airport, чтобы пользователю не приходилось начинать с нуля.

Есть ли какой-нибудь способ сделать это?

Любая помощь приветствуется.Заранее благодарю.

Это было полезно?

Решение

Вот лучший способ (и не требует знаний SQL):
Создайте быстрое приложение Core Data для iPhone (или даже приложение для Mac), используя ту же объектную модель, что и ваше приложение List.Напишите несколько строк кода, чтобы сохранить в хранилище нужные управляемые объекты по умолчанию.Затем запустите это приложение в симуляторе.Теперь перейдите в ~/Library/Application Support/iPhone Simulator/User/Applications.Найдите свое приложение среди GUID, а затем просто скопируйте хранилище sqlite в папку проекта вашего приложения List.

Затем загрузите это хранилище, как в примере с CoreDataBooks.

Другие советы

Да, на самом деле пример CoreDataBooks это делает, код можно скачать здесь: образец кода

Что вы делаете, так это создаете внутреннее хранилище (базу данных), используя обычную процедуру для инициализации вашего хранилища, как и в случае с любым другим хранилищем, затем вы просто запускаете свой код и позволяете ему выполнять код, как описано в примере CoreDataBooks (фрагмент кода ниже). ).После инициализации хранилища вам потребуется создать NSManagedObjectContext и инициализируйте его с помощью созданного постоянного хранилища, вставьте все необходимые вам сущности и сохраните контекст.

После успешного сохранения контекста вы можете остановить приложение, затем перейти в Finder и перейти в папку: ~/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, который разбираю на объекты, а затем вставляю их в Core Data.Я делаю это при инициализации приложения.Я также создаю один объект в своих основных данных, который указывает, вставлены ли уже эти исходные данные. После вставки исходных данных я устанавливаю этот объект, чтобы при следующем запуске сценария он видел, что исходные данные уже инициализированы.

Чтобы прочитать файл 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 хочет, чтобы это происходило только с файлами, созданными пользователем.

Приведенные выше рекомендации предлагают некоторые решения, но в основном они сводятся к следующему:

  • храните БД в каталоге кешей и корректно обрабатывайте ситуации, когда ОС очищает кеши - вам придется пересобирать БД, что, вероятно, исключает это для большинства из нас.

  • установите атрибут «не кэшировать» в файле БД, что немного загадочно, поскольку для разных версий ОС это нужно делать по-разному.

Я не думаю, что это слишком сложно, но имейте в виду, что вам нужно сделать еще кое-что, чтобы этот пример кода работал вместе с iCloud...

Поэтому я разработал универсальный метод, который загружает данные из словаря (возможно, из 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]);
    }
}

Этот ответ только для тех, кто

  • включение предварительно заполненной базы данных в ваше приложение
  • создание приложения для нескольких платформ (iOS, Android и т. д.)

Я создал предварительно заполненную базу данных SQLite для приложения Android.Затем, когда я создавал версию приложения для iOS, я подумал, что лучше всего будет использовать Core Data.Поэтому я потратил довольно много времени на изучение Core Data, а затем переписал код для предварительного заполнения базы данных.Чтобы научиться выполнять каждый шаг на обеих платформах, потребовалось множество исследований, проб и ошибок.Совпадений было гораздо меньше, чем я надеялся.

В конце концов я просто решил использовать ту же базу данных SQLite из моего проекта Android.Затем я использовал оболочку FMDB для прямого доступа к базе данных в iOS.Выгоды:

  • Предварительно заполненную базу данных нужно создать только один раз.
  • Не требует смены парадигмы.Синтаксис Android и FMDB хоть и различен, но все же довольно похож.
  • Получите гораздо больше контроля над тем, как выполняются запросы.
  • Позволяет полнотекстовый поиск.

Хотя я не сожалею об изучении Core Data, если бы мне пришлось сделать это заново, я мог бы сэкономить много времени, просто придерживаясь SQLite.

Если вы начинаете с iOS, а затем планируете перейти на Android, я бы все равно использовал оболочку SQLite, например FMDB, или другое программное обеспечение для предварительного заполнения базы данных.Хотя технически вы можете извлечь базу данных 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)

Благодаря этому у вас не будет двух копий.

Обновлять

теперь я использую SQLite.swift а не FMDB, поскольку он лучше интегрируется с проектами Swift.

Другой способ хранения значений по умолчанию можно найти с помощью 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"];

Это сработало для меня.Это модификация этого отвечать к Андреа Тосо и вдохновлен этим блог.Единственная проблема с ответом заключается в том, что существует вероятность потери данных при перемещении файлов sqlite с помощью FileManager.Я сохранил около 500 строк данных, используя replacePersistentStore вместо FileManager.default.copyItem.

Шаг 1
Заполните свои основные данные в другом приложении и получите путь к файлам, используя этот код:

let paths = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)
let documentsDirectory = paths[0]
print(documentsDirectory)

Шаг 2
Перетащите 3 файла с расширением .sqlite в свой проект xCode.(Обязательно выберите параметр «Добавить к целям»).

Шаг 3
Создайте функцию для проверки первого запуска приложения в AppDelegate.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, чтобы получить URL-адрес, куда следует переместить базу данных sqlite:

func getDocumentsDirectory()-> URL {
    let paths = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)
    let documentsDirectory = paths[0]
    return documentsDirectory
}

Шаг 5
Замените объявление persistContainer следующим:

// MARK: - Core Data stack

lazy var persistentContainer: NSPersistentContainer = {
    let container = NSPersistentContainer(name: "ProjectName")

    let storeUrl = self.getDocumentsDirectory().appendingPathComponent("FileName.sqlite")

    if UserDefaults.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
}()
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top