代码之家  ›  专栏  ›  技术社区  ›  SolidSnake4444

iOS 11+如何将现有核心数据迁移到共享应用程序组以用于扩展?

  •  3
  • SolidSnake4444  · 技术社区  · 7 年前

    当我使用核心数据模板创建ios11应用程序时,它在AppDelete.m中自动生成了以下代码。

    synthesize persistentContainer = _persistentContainer;
    
    - (NSPersistentContainer *)persistentContainer {
        // The persistent container for the application. This implementation creates and returns a container, having loaded the store for the application to it.
        @synchronized (self) {
            if (_persistentContainer == nil) {
                _persistentContainer = [[NSPersistentContainer alloc] initWithName:@"My_History"];
                [_persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription *storeDescription, NSError *error) {
                    if (error != nil) {
                        // Replace this implementation with code to handle the error appropriately.
                        // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
    
                        /*
                         Typical reasons for an error here include:
                         * The parent directory does not exist, cannot be created, or disallows writing.
                         * The persistent store is not accessible, due to permissions or data protection when the device is locked.
                         * The device is out of space.
                         * The store could not be migrated to the current model version.
                         Check the error message to determine what the actual problem was.
                        */
                        NSLog(@"Unresolved error %@, %@", error, error.userInfo);
                        abort();
                    }
                }];
            }
        }
    
        return _persistentContainer;
    }
    
    - (void)saveContext {
    NSManagedObjectContext *context = self.persistentContainer.viewContext;
    NSError *error = nil;
    if ([context hasChanges] && ![context save:&error]) {
        // Replace this implementation with code to handle the error appropriately.
        // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
        NSLog(@"Unresolved error %@, %@", error, error.userInfo);
        abort();
    }
    

    代码在目标C中。

    我读过其他与此相关的问题,但所有这些问题似乎都是在苹果改变核心数据工作方式以使其更简单之前提出的。正如您在我的代码中看到的,我从未指定数据存储的确切文件名。我看到的每个例子都有“我的_历史.sqllite". 我甚至不知道我的是不是一个SQLLite数据库,它只是由那个代码创建的。

    2 回复  |  直到 7 年前
        1
  •  15
  •   mrfour    6 年前

    solidsnake4444 answer 拯救了我的一天。这是swift5.0版本。

    lazy var persistentContainer: NSPersistentContainer = {
        let container = NSPersistentContainer(name: "MyApp")
        let storeURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.my.app")!.appendingPathComponent("MyApp.sqlite")
    
        var defaultURL: URL?
        if let storeDescription = container.persistentStoreDescriptions.first, let url = storeDescription.url {
            defaultURL = FileManager.default.fileExists(atPath: url.path) ? url : nil
        }
    
        if defaultURL == nil {
            container.persistentStoreDescriptions = [NSPersistentStoreDescription(url: storeURL)]
        }
        container.loadPersistentStores(completionHandler: { [unowned container] (storeDescription, error) in
            if let error = error as NSError? {
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
    
            if let url = defaultURL, url.absoluteString != storeURL.absoluteString {
                let coordinator = container.persistentStoreCoordinator
                if let oldStore = coordinator.persistentStore(for: url) {
                    do {
                        try coordinator.migratePersistentStore(oldStore, to: storeURL, options: nil, withType: NSSQLiteStoreType)
                    } catch {
                        print(error.localizedDescription)
                    }
    
                    // delete old store
                    let fileCoordinator = NSFileCoordinator(filePresenter: nil)
                    fileCoordinator.coordinate(writingItemAt: url, options: .forDeleting, error: nil, byAccessor: { url in
                        do {
                            try FileManager.default.removeItem(at: url)
                        } catch {
                            print(error.localizedDescription)
                        }
                    })
                }
            }
        })
        return container
    }()
    
        2
  •  7
  •   SolidSnake4444    7 年前

    + (NSPersistentContainer*) GetPersistentContainer {
        //Init the store.
        NSPersistentContainer *_persistentContainer = [[NSPersistentContainer alloc] initWithName:@"Test_App"];
    
        //Define the store url that is located in the shared group.
        NSURL* storeURL = [[[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.Test_App"] URLByAppendingPathComponent:@"Test_App.sqlite"];
    
        //Determine if we already have a store saved in the default app location.
        BOOL hasDefaultAppLocation = [[NSFileManager defaultManager] fileExistsAtPath: _persistentContainer.persistentStoreDescriptions[0].URL.path];
    
        //Check if the store needs migration.
        BOOL storeNeedsMigration = hasDefaultAppLocation && ![_persistentContainer.persistentStoreDescriptions[0].URL.absoluteString isEqualToString:storeURL.absoluteString];
    
        //Check if the store in the default location does not exist.
        if (!hasDefaultAppLocation) {
            //Create a description to use for the app group store.
            NSPersistentStoreDescription *description = [[NSPersistentStoreDescription alloc] init];
    
            //set the automatic properties for the store.
            description.shouldMigrateStoreAutomatically = true;
            description.shouldInferMappingModelAutomatically = true;
    
            //Set the url for the store.
            description.URL = storeURL;
    
            //Replace the coordinator store description with this description.
            _persistentContainer.persistentStoreDescriptions = [NSArray arrayWithObjects:description, nil];
        }
    
        //Load the store.
        [_persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription *storeDescription, NSError *error) {
            //Check that we do not have an error.
            if (error == nil) {
                //Check if we need to migrate the store.
                if (storeNeedsMigration) {
                    //Create errors to track migration and deleting errors.
                    NSError *migrateError;
                    NSError *deleteError;
    
                    //Store the old location URL.
                    NSURL *oldStoreURL = storeDescription.URL;
    
                    //Get the store we want to migrate.
                    NSPersistentStore *store = [_persistentContainer.persistentStoreCoordinator persistentStoreForURL: oldStoreURL];
    
                    //Set the store options.
                    NSDictionary *storeOptions = @{ NSSQLitePragmasOption : @{ @"journal_mode" : @"WAL" } };
    
                    //Migrate the store.
                    NSPersistentStore *newStore = [_persistentContainer.persistentStoreCoordinator migratePersistentStore: store toURL:storeURL options:storeOptions withType:NSSQLiteStoreType error:&migrateError];
    
                    //Check that the store was migrated.
                    if (newStore && !migrateError) {
                        //Remove the old SQLLite database.
                        [[[NSFileCoordinator alloc] init] coordinateWritingItemAtURL: oldStoreURL options: NSFileCoordinatorWritingForDeleting error: &deleteError byAccessor: ^(NSURL *urlForModifying) {
                            //Create a remove error.
                            NSError *removeError;
    
                            //Delete the file.
                            [[NSFileManager defaultManager] removeItemAtURL: urlForModifying error: &removeError];
    
                            //If there was an error. Output it.
                            if (removeError) {
                                NSLog(@"%@", [removeError localizedDescription]);
                            }
                        }
                         ];
    
                        //If there was an error. Output it.
                        if (deleteError) {
                            NSLog(@"%@", [deleteError localizedDescription]);
                        }
                    }
                }
            } else {
                // Replace this implementation with code to handle the error appropriately.
                // abort() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
    
                /*
                 Typical reasons for an error here include:
                 * The parent directory does not exist, cannot be created, or disallows writing.
                 * The persistent store is not accessible, due to permissions or data protection when the device is locked.
                 * The device is out of space.
                 * The store could not be migrated to the current model version.
                 Check the error message to determine what the actual problem was.
                 */
                NSLog(@"Unresolved error %@, %@", error, error.userInfo);
                abort();
            }
        }];
    
        //Return the container.
        return _persistentContainer;
    }
    
        3
  •  0
  •   easeout    6 年前

    更新:

    要迁移现有的持久性存储,需要 NSPersistentContainer persistentStoreCoordinator ,的实例 NSPersistentStoreCoordinator . 这暴露了方法 migratePersistentStore:toURL:options:withType:error: 迁移持久存储。

    // Get the reference to the persistent store coordinator
    let coordinator = persistentContainer.persistentStoreCoordinator
    // Get the URL of the persistent store
    let oldURL = persistentContainer.persistentStoreDescriptions.url
    // Get the URL of the new App Group location
    let newURL = NSFileManager.defaultManager().containerURLForSecurityApplicationGroupIdentifier("YOUR_APP_GROUP")
    // Get the reference to the current persistent store
    let oldStore = coordinator.persistentStore(for: oldURL)
    // Migrate the persistent store
    do {
       try coordinator.migratePersistentStore(oldStore, to: newURL, options: nil, withType: NSSQLiteStoreType)
    } catch {
       // ERROR
    }
    

    原件:

    下面概述了如何创建 NSpersistent容器 连接到非默认位置的持久存储。

    这个 NSpersistent容器 曝光 defaultDirectoryURL ,并声明:

    NSURL 在这个时刻 商店将位于或当前位于。这种方法可以 NSpersistent容器 .

    并定义 要成为应用程序组目录,请使用 containerURLForSecurityApplicationGroupIdentifier ,则应该能够访问应用程序和扩展之间的容器(假设它们具有相同的应用程序组权限)。

    NSpersistent容器 也暴露了 persistentStoreDescriptions loadPersistentStoresWithCompletionHandler: .

    请注意,我没有使用 ,并且不知道此共享是否会导致任何并发问题。