Pergunta

Estou criando um aplicativo de lista e apoiando-o com dados principais.

Eu gostaria de ter uma lista padrão de, digamos, 10 itens do aeroporto, para que o usuário não precise começar do zero.

Há alguma maneira de fazer isso?

Qualquer ajuda é apreciada.Desde já, obrigado.

Foi útil?

Solução

Aqui está a melhor maneira (e não requer conhecimento SQL):
Crie um aplicativo de iPhone de dados principal rápido (ou mesmo aplicativo Mac) usando o mesmo modelo de objeto que o seu aplicativo de lista. Escreva algumas linhas de código para salvar os objetos gerenciados padrão que você deseja na loja. Em seguida, execute esse aplicativo no simulador. Agora, acesse ~/biblioteca/suporte de aplicativo/simulador/user/aplicativos/aplicativos. Encontre seu aplicativo entre os GUIDs e, em seguida, copie a loja SQLite para a pasta do projeto do aplicativo da sua lista.

Em seguida, carregue essa loja como eles fazem no exemplo de Coredatabooks.

Outras dicas

Sim, de fato, o exemplo do COREDATABOOKS faz isso, você pode fazer o download do código aqui: Código de amostra

O que você faz é criar o armazenamento interno (banco de dados) usando o procedimento normal para inicializar sua loja como faria em qualquer outra loja, então você simplesmente executa seu código e deixa executar o código, conforme descrito no Exemplo de Coredatabooks (snippet de código abaixo ). Depois que a loja for inicializada, você desejará criar um NSManagedObjectContext e inicialize com a loja persistente criada, insira todas as entidades necessárias e salve o contexto.

Depois que o contexto for salvo com sucesso, você pode interromper seu aplicativo, depois vá para o Finder e vá para a pasta: ~/Library/Developer Digite a pesquisa .sqlite e procure /desenvolvedor, a classificação por data fornecerá o mais recente banco de dados .sqlite que deve corresponder ao tempo em que o código foi executado, você pode pegar esta loja e adicioná -lo como um recurso do seu projeto . Este arquivo pode ser lido por um coordenador persistente da loja.

- (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;
}

Espero que ajude.

-SCAR

Com esse método, você não precisa criar um aplicativo separado ou ter nenhum conhecimento SQL. Você só precisa ser capaz de criar um arquivo JSON para seus dados iniciais.

Uso um arquivo JSON que eu analisei em objetos e insira -os nos dados principais. Eu faço isso quando o aplicativo inicializa. Também faço uma entidade nos meus dados principais que indicam se esses dados iniciais já foram inseridos, depois de inserir os dados iniciais, defino essa entidade; portanto, na próxima vez que o script executar, ele ver que os dados iniciais já foram inicializados.

Para ler o arquivo JSON em objetos:

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();
}

Então você pode fazer objetos de entidade assim:

[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

}];

Se você deseja uma descrição mais detalhada sobre como fazer isso, confira este tutorial:http://www.raywenderlich.com/12170/core-data-tutorial-how-to-preloadimport-existing-data-updated

Para 10 itens, você pode simplesmente fazer isso dentro applicationDidFinishLaunching: no seu delegado de aplicativo.

Defina um método, digamos insertPredefinedObjects, isso cria e preenche as instâncias da entidade encarregada de gerenciar os itens do seu aeroporto e salvar seu contexto. Você pode ler os atributos de um arquivo ou simplesmente hardwire -os em seu código. Então, chame esse método dentro applicationDidFinishLaunching:.

Lembre -se de que, ao seguir o código de exemplo do COREDATABOOKS, que provavelmente quebra as diretrizes de armazenamento de dados do iOS:

https://developer.apple.com/icloud/documentation/data-storage/

Eu tive um aplicativo rejeitado para copiar o banco de dados (somente leitura) preenchido no diretório de documentos-à medida que ele é backup do iCloud-e a Apple só quer que isso aconteça com arquivos gerados pelo usuário.

As diretrizes acima oferecem algumas soluções, mas elas se resumem principalmente a:

  • Armazene o banco de dados no diretório de caches e lide com as situações graciosas em que o sistema operacional elimina os caches - você terá que reconstruir o banco de dados, o que provavelmente o descarta para a maioria de nós.

  • Defina um atributo 'não cache' no arquivo db, que é um pouco misterioso, pois ele precisa ser feito de maneira diferente para diferentes versões do sistema operacional.

Eu não acho que seja muito complicado, mas esteja ciente de que você tem um pouco mais para fazer para fazer com que esse código de exemplo funcione ao lado do iCloud ...

Então desenvolvi um método genérico que carrega de um dicionário (possivelmente de JSON) e preenche o banco de dados.Deve ser usado SOMENTE com dados confiáveis ​​(de um canal seguro), não pode lidar com referências circulares e migrações de esquema podem ser problemáticas...Mas para casos de uso simples como o meu, tudo bem

Aqui vai

- (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]);
            }
        }
    }   
}

E muito do 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]];

Exemplos JSON

    {
    "EntitieA": [ {"Att1": 1 }, {"Att1": 2} ],
    "EntitieB": [ {"Easy":"AS ABC", "Aref>EntitieA>Att1": 1} ]
    }

e

{
    "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"}]
}

Que tal verificar se existem objetos e, se não, crie um com alguns dados?

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]);
    }
}

Esta resposta é apenas para pessoas que são

  • incluindo um banco de dados pré-populado em seu aplicativo
  • Fazendo um aplicativo para várias plataformas (iOS, Android, etc.)

Eu tinha feito um banco de dados SQLite preparado para um aplicativo Android. Então, quando eu estava criando uma versão iOS do aplicativo, achei melhor usar dados principais. Por isso, passei muito tempo aprendendo dados principais e depois reescrevendo o código para prepopular o banco de dados. Aprender a fazer todas as etapas nas duas plataformas exigia muita pesquisa, tentativa e erro. Havia muito menos sobreposição do que eu esperava.

No final, decidi usar o mesmo banco de dados SQLite do meu projeto Android. Em seguida, usei o wrapper FMDB para acessar diretamente o banco de dados no iOS. Os benefícios:

  • Somente precisa fazer o banco de dados preparado uma vez.
  • Não requer uma mudança de paradigma. A sintaxe entre Android e FMDB, embora diferente, ainda é bastante semelhante.
  • Tenha muito mais controle sobre como as consultas são realizadas.
  • Permite pesquisa completa de texto.

Embora eu não me arrependa de aprender dados principais, se eu fizesse isso, eu poderia ter economizado muito tempo apenas mantendo o sqlite.

Se você está começando no iOS e planejando mudar para o Android, eu ainda usaria um invólucro sqlite como o FMDB ou algum outro software para preppliar o banco de dados. Embora você possa extrair tecnicamente o banco de dados SQLITE que você prepara com dados principais, o esquema (nomes de tabela e colunas etc.) será nomeado estranhamente.

A propósito, se você não precisar modificar seu banco de dados preparado, não o copie para o diretório de documentos após a instalação do aplicativo. Basta acessá -lo diretamente do pacote.

// 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)

Isso faz com que você não tenha duas cópias.

Atualizar

Eu agora uso Sqlite.swift Em vez de FMDB, porque se integra melhor aos projetos SWIFT.

Outro método para armazenar padrões é encontrado por meio de nsuserdefaults. (Surpresa!) E é fácil.

Sugerido por alguns, coloque isso no applicationDidFinishLaunching

No caso de 10 padrões, aeroporto0 a 9

Contexto

NSUserDefaults *nud = [NSUserDefaults standardUserDefaults];
[nud setString:@"MACADDRESSORWHY" forKey:@"Airport0"];
    ...
[nud setString:@"MACADDRESSORWHY" forKey:@"Airport9"];
[nud synchronize];

ou

[[NSUserDefaults standardUserDefaults] setString:@"MACADDRESSORWHY" forKey:@"Airport9"]];
     ...
[[NSUserDefaults standardUserDefaults] synchronize];

E então, obtendo os padrões.

NSString *air0 = [[NSUserDefaults standardUserDefaults] stringForKey:@"Airport0"];

Isso funcionou para mim. Esta é uma modificação disso responda por Andrea Toso e inspirado por isso blog. O único problema com a resposta é que há uma chance de perda de dados ao mover arquivos SQLite com o FileManager. Economizei cerca de 500 linhas de dados usando o SubstituirPerSistentStore em vez do FileManager.Default.copyItem

Passo 1
Preencha seus dados principais em outro aplicativo e obtenha o caminho dos arquivos usando este código:

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

Passo 2
Arraste seus 3 arquivos com a extensão .sqlite para o seu projeto Xcode. (Certifique -se de selecionar Adicionar a opção Targets).

Etapa 3
Crie função para verificar a primeira execução do aplicativo em 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
}

Passo 4
Copie esta função no appdelegate.swift para obter URL onde o banco de dados SQLite deve ser movido:

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

Etapa 5
Substitua a Declaração de PersistentContainer por este:

// 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
}()
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top