Jas*_*ker 10 core-data ios ios8-today-widget
我是Today扩展的新手,并使用嵌入式框架.
我们的应用程序目前使用sqlite支持的核心数据.如果我想在应用程序和今天的扩展程序之间共享此内容,我是否应将其移至框架以供两者共享?
如何在应用商店中迁移当前版本以便能够升级到新结构?
Tom*_*ton 12
您需要确保应用程序和扩展程序都可以使用模型和持久性存储文件.
对于模型,将其移动到框架是一个好主意,因为它意味着模型文件只有一个副本.只要应用程序和扩展程序都链接到框架,它们都可以使用.如果你这样做,那么在框架中设置核心数据堆栈的代码也是一个好主意,因为它在两种情况下都是相同的.
您当然可以将模型包含在两个目标中.这意味着你将拥有该文件的两个副本,这会浪费空间.可能不是很多空间.
对于持久性存储,您必须设置应用程序组并在组目录中使用存储文件.应用程序组是应用程序和扩展程序"功能"中的设置之一 - 打开它并创建组名称.然后将持久性存储文件放在组目录中,您可以使用类似的代码找到它
NSURL *groupURL = [[NSFileManager defaultManager]
containerURLForSecurityApplicationGroupIdentifier:
@"GROUP NAME HERE"];
Run Code Online (Sandbox Code Playgroud)
[我在博客中更详细地介绍了其中一些内容].
如果您有现有数据,则必须将其移至新商店文件.这看起来像
migratePersistentStore:toURL:options:withType:error:将其移动到新位置.然后删除旧副本.对于旧数据部分的迁移,我做了一些工作.
如何检查旧数据库是否存在,我使用下面的代码进行检查.
if ([fileManager fileExistsAtPath:[storeURL path]]) {
NSLog(@"old single app db exist.");
targetURL = storeURL;
needMigrate = true;
}
// storeURL is the store url return by:
- (NSURL *)applicationDocumentsDirectory
{
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}
Run Code Online (Sandbox Code Playgroud)将旧数据迁移到新数据存储位置.
如果旧数据存储存在,并且组数据存储不存在,我使用下面的代码执行迁移:
if (needMigrate) {
NSError *error = nil;
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[context setPersistentStoreCoordinator:__persistentStoreCoordinator];
[__persistentStoreCoordinator migratePersistentStore:store toURL:groupURL options:options withType:NSSQLiteStoreType error:&error];
if (error != nil) {
NSLog(@"Error when migration to groupd url %@, %@", error, [error userInfo]);
abort();
}
}
Run Code Online (Sandbox Code Playgroud)
通过检查是否存在旧数据来设置needMigrate标志.
供您参考,我在这里粘贴完整代码:
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
if (__persistentStoreCoordinator != nil)
{
return __persistentStoreCoordinator;
}
bool needMigrate = false;
bool needDeleteOld = false;
NSString *kDbName = @"xxx.sqlite";
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:kDbName];
NSURL *groupURL = [[self applicationGroupDocumentDirectory] URLByAppendingPathComponent:kDbName];
NSURL *targetURL = nil;
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:[storeURL path]]) {
NSLog(@"old single app db exist.");
targetURL = storeURL;
needMigrate = true;
}
if ([fileManager fileExistsAtPath:[groupURL path]]) {
NSLog(@"group db exist");
needMigrate = false;
targetURL = groupURL;
if ([fileManager fileExistsAtPath:[storeURL path]]) {
needDeleteOld = true;
}
}
if (targetURL == nil)
targetURL = groupURL;
NSDictionary *options = @{NSMigratePersistentStoresAutomaticallyOption: @(YES),
NSInferMappingModelAutomaticallyOption: @(YES)};
NSError *error = nil;
__persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
NSPersistentStore *store;
store = [__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:targetURL options:options error:&error];
if (!store)
{
/*
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 persistent store is not accessible;
* The schema for the persistent store is incompatible with current managed object model.
Check the error message to determine what the actual problem was.
If the persistent store is not accessible, there is typically something wrong with the file path. Often, a file URL is pointing into the application's resources directory instead of a writeable directory.
If you encounter schema incompatibility errors during development, you can reduce their frequency by:
* Simply deleting the existing store:
[[NSFileManager defaultManager] removeItemAtURL:storeURL error:nil]
* Performing automatic lightweight migration by passing the following dictionary as the options parameter:
[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
Lightweight migration will only work for a limited set of schema changes; consult "Core Data Model Versioning and Data Migration Programming Guide" for details.
*/
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}
// do the migrate job from local store to a group store.
if (needMigrate) {
NSError *error = nil;
NSManagedObjectContext *context = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[context setPersistentStoreCoordinator:__persistentStoreCoordinator];
[__persistentStoreCoordinator migratePersistentStore:store toURL:groupURL options:options withType:NSSQLiteStoreType error:&error];
if (error != nil) {
NSLog(@"Error when migration to groupd url %@, %@", error, [error userInfo]);
abort();
}
}
return __persistentStoreCoordinator;
}
/**
Returns the URL to the application's Documents directory.
*/
- (NSURL *)applicationDocumentsDirectory
{
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}
- (NSURL *)applicationGroupDocumentDirectory
{
return [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.com.kzjeef.shitf.scheduler"];
}
Run Code Online (Sandbox Code Playgroud)
如果有人想要快速解决方案,只需在 didFinishLaunchingWithOptions 中添加以下函数。
func migratePersistentStore(){
let coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
var storeOptions = [AnyHashable : Any]()
storeOptions[NSMigratePersistentStoresAutomaticallyOption] = true
storeOptions[NSInferMappingModelAutomaticallyOption] = true
let oldStoreUrl = self.applicationDocumentsDirectory.appendingPathComponent("YourApp.sqlite")!
let newStoreUrl = self.applicationGroupDirectory.appendingPathComponent("YourApp.sqlite")!
var targetUrl : URL? = nil
var needMigrate = false
var needDeleteOld = false
if FileManager.default.fileExists(atPath: oldStoreUrl.path){
needMigrate = true
targetUrl = oldStoreUrl
}
if FileManager.default.fileExists(atPath: newStoreUrl.path){
needMigrate = false
targetUrl = newStoreUrl
if FileManager.default.fileExists(atPath: oldStoreUrl.path){
needDeleteOld = true
}
}
if targetUrl == nil {
targetUrl = newStoreUrl
}
if needMigrate {
do {
try coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: targetUrl!, options: storeOptions)
if let store = coordinator.persistentStore(for: targetUrl!)
{
do {
try coordinator.migratePersistentStore(store, to: newStoreUrl, options: storeOptions, withType: NSSQLiteStoreType)
} catch let error {
print("migrate failed with error : \(error)")
}
}
} catch let error {
CrashlyticsHelper.reportCrash(err: error as NSError, strMethodName: "migrateStore")
}
}
if needDeleteOld {
DBHelper.deleteDocumentAtUrl(url: oldStoreUrl)
guard let shmDocumentUrl = self.applicationDocumentsDirectory.appendingPathComponent("NoddApp.sqlite-shm") else { return }
DBHelper.deleteDocumentAtUrl(url: shmDocumentUrl)
guard let walDocumentUrl = self.applicationDocumentsDirectory.appendingPathComponent("NoddApp.sqlite-wal") else { return }
DBHelper.deleteDocumentAtUrl(url: walDocumentUrl)
}
}
Run Code Online (Sandbox Code Playgroud)
我的 PersistentStoreCoordinator 看起来像这样:
lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = {
let coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
let url = self.applicationGroupDirectory.appendingPathComponent("YourApp.sqlite")
var storeOptions = [AnyHashable : Any]()
storeOptions[NSMigratePersistentStoresAutomaticallyOption] = true
storeOptions[NSInferMappingModelAutomaticallyOption] = true
var failureReason = "There was an error creating or loading the application's saved data."
do {
try coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: url, options:storeOptions)
} catch {
// Report any error we got.
var dict = [String: AnyObject]()
dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data" as AnyObject?
dict[NSLocalizedFailureReasonErrorKey] = failureReason as AnyObject?
dict[NSUnderlyingErrorKey] = error as NSError
let wrappedError = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict)
NSLog("Unresolved error \(wrappedError), \(wrappedError.userInfo)")
abort()
}
return coordinator
}()
Run Code Online (Sandbox Code Playgroud)
当 appstore 中已经有一个应用程序并且您想要将 coreData 持久存储文件从您的默认存储位置迁移到您的应用程序组位置时,就会出现这种情况。
编辑:对于从旧位置删除文件,建议我们使用 NSFileCoordinator 来执行任务。
static func deleteDocumentAtUrl(url: URL){
let fileCoordinator = NSFileCoordinator(filePresenter: nil)
fileCoordinator.coordinate(writingItemAt: url, options: .forDeleting, error: nil, byAccessor: {
(urlForModifying) -> Void in
do {
try FileManager.default.removeItem(at: urlForModifying)
}catch let error {
print("Failed to remove item with error: \(error.localizedDescription)")
}
})
}
Run Code Online (Sandbox Code Playgroud)
请注意使用 NSFileCoordinator 删除文件的原因是因为 NSFileCoordinator 允许我们确保文件相关任务(例如打开读写)以不会干扰系统上尝试使用同一文件的任何其他任务的方式完成.例如,如果您想打开一个文件并同时将其删除,您不希望这两个操作同时发生。
请在商店迁移成功后调用上述函数。
| 归档时间: |
|
| 查看次数: |
3697 次 |
| 最近记录: |