Persist the database in NSUserDefaults on tvOS

This commit is contained in:
Cameron Gutman
2018-08-28 17:42:59 -07:00
parent b5ea4c3e50
commit e2e8f0121d
+72 -9
View File
@@ -16,6 +16,14 @@
static NSOperationQueue* mainQueue; static NSOperationQueue* mainQueue;
#if TARGET_OS_TV
static NSString* DB_NAME = @"Moonlight_tvOS.bin";
#elif TARGET_OS_IPHONE
static NSString* DB_NAME = @"Limelight_iOS.sqlite";
#else
static NSString* DB_NAME = @"moonlight_mac.sqlite";
#endif
#if TARGET_OS_IPHONE #if TARGET_OS_IPHONE
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{ {
@@ -67,10 +75,18 @@ static NSOperationQueue* mainQueue;
NSManagedObjectContext *managedObjectContext = [self managedObjectContext]; NSManagedObjectContext *managedObjectContext = [self managedObjectContext];
if (managedObjectContext != nil) { if (managedObjectContext != nil) {
[managedObjectContext performBlock:^{ [managedObjectContext performBlock:^{
if (![managedObjectContext hasChanges]) {
return;
}
NSError *error = nil; NSError *error = nil;
if ([managedObjectContext hasChanges] && ![managedObjectContext save:&error]) { if (![managedObjectContext save:&error]) {
Log(LOG_E, @"Critical database error: %@, %@", error, [error userInfo]); Log(LOG_E, @"Critical database error: %@, %@", error, [error userInfo]);
} }
#if TARGET_OS_TV
NSData* dbData = [NSData dataWithContentsOfURL:[[[[NSFileManager defaultManager] URLsForDirectory:NSCachesDirectory inDomains:NSUserDomainMask] lastObject] URLByAppendingPathComponent:DB_NAME]];
[[NSUserDefaults standardUserDefaults] setObject:dbData forKey:DB_NAME];
#endif
}]; }];
} }
} }
@@ -112,19 +128,30 @@ static NSOperationQueue* mainQueue;
return _persistentStoreCoordinator; return _persistentStoreCoordinator;
} }
NSURL *storeURL = [self getStoreURL];
NSError *error = nil; NSError *error = nil;
_persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]]; _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption, [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil]; [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
if (![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error]) { NSString* storeType;
#if TARGET_OS_TV
// Use a binary store for tvOS since we will need exclusive access to the file
// to serialize into NSUserDefaults.
storeType = NSBinaryStoreType;
#else
storeType = NSSQLiteStoreType;
#endif
// We must ensure the persistent store is ready to opened
[self preparePersistentStore];
if (![_persistentStoreCoordinator addPersistentStoreWithType:storeType configuration:nil URL:[self getStoreURL] options:options error:&error]) {
// Log the error // Log the error
Log(LOG_E, @"Critical database error: %@, %@", error, [error userInfo]); Log(LOG_E, @"Critical database error: %@, %@", error, [error userInfo]);
// Drop the database // Drop the database
[[NSFileManager defaultManager] removeItemAtURL:storeURL error:nil]; [self dropDatabase];
// Try again // Try again
return [self persistentStoreCoordinator]; return [self persistentStoreCoordinator];
@@ -141,13 +168,49 @@ static NSOperationQueue* mainQueue;
return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject]; return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
} }
- (void) dropDatabase
{
// Delete the file on disk
[[NSFileManager defaultManager] removeItemAtURL:[self getStoreURL] error:nil];
#if TARGET_OS_TV
// Also delete the copy in the NSUserDefaults on tvOS
[[NSUserDefaults standardUserDefaults] removeObjectForKey:DB_NAME];
#endif
}
- (void) preparePersistentStore
{
#if TARGET_OS_TV
// On tvOS, we may need to inflate the DB from NSUserDefaults
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
NSString *cacheDirectory = [paths objectAtIndex:0];
NSString *dbPath = [cacheDirectory stringByAppendingPathComponent:DB_NAME];
// Always prefer the on disk version
if (![[NSFileManager defaultManager] fileExistsAtPath:dbPath]) {
// If that is unavailable, inflate it from NSUserDefaults
NSData* data = [[NSUserDefaults standardUserDefaults] dataForKey:DB_NAME];
if (data != nil) {
Log(LOG_I, @"Inflating database from NSUserDefaults");
[data writeToFile:dbPath atomically:YES];
}
else {
Log(LOG_I, @"No database on disk or in NSUserDefaults");
}
}
else {
Log(LOG_I, @"Using cached database");
}
#endif
}
- (NSURL*) getStoreURL { - (NSURL*) getStoreURL {
#if TARGET_OS_TV #if TARGET_OS_TV
return [[[[NSFileManager defaultManager] URLsForDirectory:NSCachesDirectory inDomains:NSUserDomainMask] lastObject] URLByAppendingPathComponent:@"Moonlight_tvOS.sqlite"]; // We use the cache folder to store our database on tvOS
#elif TARGET_OS_IPHONE return [[[[NSFileManager defaultManager] URLsForDirectory:NSCachesDirectory inDomains:NSUserDomainMask] lastObject] URLByAppendingPathComponent:DB_NAME];
return [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"Limelight_iOS.sqlite"];
#else #else
return [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"moonlight_mac.sqlite"]; return [[self applicationDocumentsDirectory] URLByAppendingPathComponent:DB_NAME];
#endif #endif
} }