有什么方法可以预填充核心数据吗?

发布于 2024-08-20 23:46:02 字数 122 浏览 11 评论 0原文

我一直在创建一个列表应用程序并用核心数据支持它。

我想要一个包含 10 个机场物品的默认列表,这样用户就不必从头开始。

有什么办法可以做到这一点吗?

任何帮助表示赞赏。 提前致谢。

I've been creating a list app and backing it with core data.

I would like to have a default list of say 10 airport's items, so that the user doesn't have to start from scratch.

Is there any way to do this?

Any help is appreciated.
Thanks in advance.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

阳光下的泡沫是彩色的 2024-08-27 23:46:02

这是最好的方法(并且不需要 SQL 知识):
使用与 List 应用程序相同的对象模型创建快速 Core Data iPhone 应用程序(甚至 Mac 应用程序)。编写几行代码将您想要的默认托管对象保存到存储中。然后,在模拟器中运行该应用程序。现在,转到 ~/Library/Application Support/iPhone Simulator/User/Applications。在 GUID 中找到您的应用程序,然后只需将 sqlite 存储复制到列表应用程序的项目文件夹中即可。

然后,像在 CoreDataBooks 示例中那样加载该存储。

Here's the best way (and doesn't require SQL knowledge):
Create a quick Core Data iPhone app (Or even Mac app) using the same object model as your List app. Write a few lines of code to save the default managed objects you want to the store. Then, run that app in the simulator. Now, go to ~/Library/Application Support/iPhone Simulator/User/Applications. Find your application among the GUIDs, then just copy the sqlite store out into your List app's project folder.

Then, load that store like they do in the CoreDataBooks example.

盗琴音 2024-08-27 23:46:02

是的,事实上 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;
}

希望有帮助。

-奥斯卡

Yes there is in fact the CoreDataBooks example does this, you can download the code here: sample code

What you do is create the internal store (database) using the normal procedure to initialize your store just like you would with any other store, then you simply run your code and let it execute the code as described in the CoreDataBooks example (code snippet below). Once the store has been initialized you will want to create a NSManagedObjectContext and initialize it with the created persistent store, insert all the entities you need, and save the context.

Once the context has been successfully saved, you can stop your application, then go to finder and go to folder: ~/Library/Developer type in the search .sqlite and look under /Developer, sorting by date will give you the most recent .sqlite database which should match the time that the code was executed, you can then take this store and add it as a resource of your project. This file then can be read by a persistent store coordinator.

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

Hope that helps.

-Oscar

尘世孤行 2024-08-27 23:46:02

使用这种方法,您不需要制作单独的应用程序或具备任何 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-数据教程如何预加载导入现有数据更新

With this method you don't need to make a separate app or have any SQL knowledge. You only need to be able to make a JSON file for your initial data.

I use a JSON file that I parse into objects, then insert them in Core Data. I do this when the app initializes. I also make one entity in my core data that indicates if this initial data is already inserted, after I insert the initial data I set this entity so the next time the script runs it sees that the initial data has already been initialized.

To read json file into objects:

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

Then you can make entity objects for it like this:

[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

}];

If you want a more detailed description on how to do this, check out this tutorial:
http://www.raywenderlich.com/12170/core-data-tutorial-how-to-preloadimport-existing-data-updated

放飞的风筝 2024-08-27 23:46:02

对于 10 个项目,您只需在应用委托中的 applicationDidFinishLaunching: 中执行此操作即可。

定义一个方法,例如 insertPredefinedObjects,该方法创建并填充负责管理机场项目的实体的实例,并保存您的上下文。您可以从文件中读取属性,也可以简单地将它们硬连接到代码中。然后,在 applicationDidFinishLaunching: 中调用此方法。

For 10 items, you can just do this within applicationDidFinishLaunching: in your app delegate.

Define a method, say insertPredefinedObjects, that creates and populates the instances of the entity in charge of managing your airport items, and save your context. You may either read the attributes from a file or simply hardwire them in your code. Then, call this method inside applicationDidFinishLaunching:.

花心好男孩 2024-08-27 23:46:02

请记住,在遵循 CoreDataBooks 示例代码时,它可能违反 iOS 数据存储指南:

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

我有一个应用程序因将(只读)预填充数据库复制到文档目录而被拒绝 - 就像当时一样被备份到 iCloud - Apple 只希望这种情况发生在用户生成的文件上。

上面的指南提供了一些解决方案,但它们主要归结为:

  • 将数据库存储在缓存目录中,并妥善处理操作系统清除缓存的情况 - 您将必须重建数据库,这可能会排除这种情况我们大多数人。

  • 在数据库文件上设置“不缓存属性”,这有点神秘,因为不同的操作系统版本需要以不同的方式完成。

我不认为这太棘手,但请注意,要使该示例代码与 iCloud 一起工作,您还需要做一些额外的工作...

Bear in mind, when following the CoreDataBooks example code, that it probably breaks the iOS Data Storage Guidelines:

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

I've had an app rejected for copying the (read-only) pre-populated database to the documents directory - as it then gets backed up to iCloud - and Apple only want that to happen to user-generated files.

The guidelines above offer some solutions, but they mostly boil down to:

  • store the DB in the caches directory, and gracefully handle situations where the OS purges the caches - you will have to rebuild the DB, which probably rules it out for most of us.

  • set a 'do not cache attribute' on the DB file, which is a little arcane, as it needs to be done differently for different OS versions.

I don't think it is too tricky, but be aware that you have a bit extra to do to make that example code work alongside iCloud...

染柒℉ 2024-08-27 23:46:02

这个答案仅适用于

  • 在应用程序中包含预填充数据库
  • 的人,为多个平台(iOS、Android 等)制作应用程序。

我为 Android 应用程序制作了预填充 SQLite 数据库。然后,当我制作 iOS 版本的应用程序时,我认为最好使用 Core Data。所以我花了相当长的时间学习Core Data,然后重写代码来预填充数据库。学习如何在两个平台上完成每一步都需要大量的研究和反复试验。重叠比我希望的要少得多。

最后我决定使用我的 Android 项目中的相同 SQLite 数据库。然后我使用FMDB包装器直接访问iOS中的数据库。好处:

  • 只需要预填充数据库一次。
  • 不需要范式转变。 Android 和 FMDB 之间的语法虽然不同,但仍然非常相似。
  • 对查询的执行方式有更多的控制。
  • 允许全文搜索。

虽然我并不后悔学习 Core Data,但如果我重新学习,我可以通过坚持使用 SQLite 来节省大量时间。

如果您从 iOS 开始,然后计划迁移到 Android,我仍然会使用 SQLite 包装器(例如 FMDB)或其他一些软件来预填充数据库。尽管您可以从技术上提取使用 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)

这样您就不会拥有两份副本。

更新

我现在使用 SQLite.swift 而不是 FMDB,因为它集成了Swift 项目效果更好。

This answer is only for people who are

  • including a pre-populated database in your app
  • making an app for multiple platforms (iOS, Android, etc.)

I had made a prepopulated SQLite database for an Android app. Then when I was making an iOS version of the app I thought it would be best to use Core Data. So I spent quite a long time learning Core Data and then rewriting the code to prepopulate the database. Learning how to do every single step in both platforms required lots of research and trial and error. There was a lot less overlap than I would have hoped.

In the end I just decided to use the same SQLite database from my Android project. Then I used the FMDB wrapper to directly access the database in iOS. The benefits:

  • Only need to make the prepopulated database once.
  • Doesn't require a paradigm shift. The syntax between Android and FMDB, while different, is still fairly similar.
  • Have a lot more control over how Queries are performed.
  • Allows full text search.

Although I don't regret learning Core Data, if I were to do it over I could have saved a lot of time by just sticking to SQLite.

If you are starting in iOS and then planning to move to Android, I would still use a SQLite wrapper like FMDB or some other software to prepopulate the database. Although you can technically extract the SQLite database that you prepopulate with Core Data, the schema (table and column names, etc.) will be strangely named.

By the way, if you don't need to modify your prepopulated database, then don't copy it to the documents directory after the app is installed. Just access it directly from the bundle.

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

This makes it so that you don't have two copies.

Update

I now use SQLite.swift rather than FMDB, because it integrates better with Swift projects.

哀由 2024-08-27 23:46:02

这对我有用。这是答案的修改,作者:Andrea Toso 并受到此博客的启发。答案的唯一问题是,使用 FileManager 移动 sqlite 文件时可能会丢失数据。我使用 ReplacePersistentStore 而不是 FileManager.default.copyItem 保存了大约 500 行数据

第 1 步
在另一个应用中填充您的核心数据并使用以下代码获取文件路径:

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

第 2 步
将扩展名为 .sqlite 的 3 个文件拖到 xCode 项目中。 (请务必选择“添加到目标”选项)。

第三步
在 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
}

Step4
在 AppDelegate.swift 中复制此函数以获取应将 sqlite 数据库移动到的 url:

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

This worked for me. This is a modification of this answer by Andrea Toso and inspired by this blog. The only issue with the answer is that there is a chance of data loss when moving sqlite files with FileManager. I saved around 500 rows of data by using replacePersistentStore instead of FileManager.default.copyItem

Step 1
Populate your Core Data in another app and get files' path using this code:

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

Step2
Drag your 3 files with .sqlite extension into your xCode project. (Be sure to select Add to targets option).

Step3
Create function to check app's first run in 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
}

Step4
Copy this function in AppDelegate.swift to get url where sqlite database should be moved:

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

Step 5
Replace declaration of persistentContainer with this one:

// 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
}()
絕版丫頭 2024-08-27 23:46:02

因此,我开发了一种通用方法,可以从字典(可能是 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]);
            }
        }
    }   
}

。 ..

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

So I have developed a generic method that loads from a dictionary (possibly from JSON) and populates the database.
It should be used ONLY with trusted data (from a safe channel), it can't handle circular references and schema migrations can be problematic... But for simple use cases like mine it should be fine

Here it goes

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

And loads from 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 examples

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

and

{
    "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"}]
}
七分※倦醒 2024-08-27 23:46:02

检查是否存在任何对象,如果不存在,则使用一些数据创建一个对象怎么样?

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

How about check if any objects exist and if not, create one with some data?

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]);
    }
}
堇年纸鸢 2024-08-27 23:46:02

另一种存储默认值的方法是通过 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"];

Another method for storing defaults is found by way of NSUserDefaults. (surprise!)
And its easy.

Suggested by some, put that into the applicationDidFinishLaunching

In the given case of 10 defaults, Airport0 thru 9

Setting

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

or

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

And then, getting the defaults.

NSString *air0 = [[NSUserDefaults standardUserDefaults] stringForKey:@"Airport0"];
始终不够爱げ你 2024-08-27 23:46:02

由于大多数答案都很旧,我推荐以下教程。它解释了如何做到这一点。

https://www.youtube.com/watch?v=xcV8Ow9nWFo

As most answers are quite old, I recommend the following tutorial. It explains how it can be done.

https://www.youtube.com/watch?v=xcV8Ow9nWFo

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文