diff --git a/Limelight/Database/DataManager.h b/Limelight/Database/DataManager.h index da23ec9..0962363 100644 --- a/Limelight/Database/DataManager.h +++ b/Limelight/Database/DataManager.h @@ -11,6 +11,7 @@ #import "AppDelegate.h" #import "Host.h" #import "App.h" +#import "TemporaryApp.h" @interface DataManager : NSObject @@ -22,7 +23,7 @@ - (void) saveData; - (Host*) createHost; - (void) removeHost:(Host*)host; -- (App*) createApp; -- (void) removeApp:(App*)app; +- (App*) addAppFromTemporaryApp:(TemporaryApp*)tempApp; +- (void) removeAppFromHost:(App*)app; @end diff --git a/Limelight/Database/DataManager.m b/Limelight/Database/DataManager.m index ffea1ee..bb16b4d 100644 --- a/Limelight/Database/DataManager.m +++ b/Limelight/Database/DataManager.m @@ -7,9 +7,18 @@ // #import "DataManager.h" +#import "TemporaryApp.h" @implementation DataManager ++ (NSObject *)databaseLock { + static NSObject *lock = nil; + if (lock == nil) { + lock = [[NSObject alloc] init]; + } + return lock; +} + - (id) init { self = [super init]; self.appDelegate = [[UIApplication sharedApplication] delegate]; @@ -57,38 +66,65 @@ } - (void) saveData { - NSError* error; - if (![[self.appDelegate managedObjectContext] save:&error]) { - Log(LOG_E, @"Unable to save hosts to database: %@", error); + @synchronized([DataManager databaseLock]) { + NSError* error; + if (![[self.appDelegate managedObjectContext] save:&error]) { + Log(LOG_E, @"Unable to save hosts to database: %@", error); + } + [self.appDelegate saveContext]; } - [self.appDelegate saveContext]; } - (NSArray*) retrieveHosts { return [self fetchRecords:@"Host"]; } -- (App*) createApp { - NSEntityDescription* entity = [NSEntityDescription entityForName:@"App" inManagedObjectContext:[self.appDelegate managedObjectContext]]; - return [[App alloc] initWithEntity:entity insertIntoManagedObjectContext:[self.appDelegate managedObjectContext]]; +- (App*) addAppFromTemporaryApp:(TemporaryApp*)tempApp { + + App* managedApp; + + @synchronized([DataManager databaseLock]) { + NSEntityDescription* entity = [NSEntityDescription entityForName:@"App" inManagedObjectContext:[self.appDelegate managedObjectContext]]; + managedApp = [[App alloc] initWithEntity:entity insertIntoManagedObjectContext:[self.appDelegate managedObjectContext]]; + + assert(tempApp.host != nil); + + managedApp.id = tempApp.id; + managedApp.image = tempApp.image; + managedApp.name = tempApp.name; + managedApp.isRunning = tempApp.isRunning; + managedApp.host = tempApp.host; + + [managedApp.host addAppListObject:managedApp]; + } + + return managedApp; } -- (void) removeApp:(App*)app { - [[self.appDelegate managedObjectContext] deleteObject:app]; +- (void) removeAppFromHost:(App*)app { + @synchronized([DataManager databaseLock]) { + assert(app.host != nil); + + [app.host removeAppListObject:app]; + [[self.appDelegate managedObjectContext] deleteObject:app]; + } } - (NSArray*) fetchRecords:(NSString*)entityName { - NSFetchRequest* fetchRequest = [[NSFetchRequest alloc] init]; - NSEntityDescription* entity = [NSEntityDescription entityForName:entityName inManagedObjectContext:[self.appDelegate managedObjectContext]]; - [fetchRequest setEntity:entity]; - [fetchRequest setAffectedStores:[NSArray arrayWithObjects:[[self.appDelegate persistentStoreCoordinator] persistentStoreForURL:[self.appDelegate getStoreURL]], nil]]; + NSArray* fetchedRecords; - NSError* error; - NSArray* fetchedRecords = [[self.appDelegate managedObjectContext] executeFetchRequest:fetchRequest error:&error]; - //TODO: handle errors + @synchronized([DataManager databaseLock]) { + NSFetchRequest* fetchRequest = [[NSFetchRequest alloc] init]; + NSEntityDescription* entity = [NSEntityDescription entityForName:entityName inManagedObjectContext:[self.appDelegate managedObjectContext]]; + [fetchRequest setEntity:entity]; + [fetchRequest setAffectedStores:[NSArray arrayWithObjects:[[self.appDelegate persistentStoreCoordinator] persistentStoreForURL:[self.appDelegate getStoreURL]], nil]]; + + NSError* error; + fetchedRecords = [[self.appDelegate managedObjectContext] executeFetchRequest:fetchRequest error:&error]; + //TODO: handle errors + } return fetchedRecords; - } @end diff --git a/Limelight/Database/TemporaryApp.h b/Limelight/Database/TemporaryApp.h new file mode 100644 index 0000000..8a3e223 --- /dev/null +++ b/Limelight/Database/TemporaryApp.h @@ -0,0 +1,20 @@ +// +// TemporaryApp.h +// Moonlight +// +// Created by Cameron Gutman on 9/30/15. +// Copyright © 2015 Moonlight Stream. All rights reserved. +// + +#import +#import "Host.h" + +@interface TemporaryApp : NSObject + +@property (nullable, nonatomic, retain) NSString *id; +@property (nullable, nonatomic, retain) NSData *image; +@property (nullable, nonatomic, retain) NSString *name; +@property (nonatomic) BOOL isRunning; +@property (nullable, nonatomic, retain) Host *host; + +@end diff --git a/Limelight/Database/TemporaryApp.m b/Limelight/Database/TemporaryApp.m new file mode 100644 index 0000000..22ca599 --- /dev/null +++ b/Limelight/Database/TemporaryApp.m @@ -0,0 +1,13 @@ +// +// TemporaryApp.m +// Moonlight +// +// Created by Cameron Gutman on 9/30/15. +// Copyright © 2015 Moonlight Stream. All rights reserved. +// + +#import "TemporaryApp.h" + +@implementation TemporaryApp + +@end diff --git a/Limelight/Network/AppListResponse.m b/Limelight/Network/AppListResponse.m index 7c86cba..b4c222e 100644 --- a/Limelight/Network/AppListResponse.m +++ b/Limelight/Network/AppListResponse.m @@ -7,7 +7,7 @@ // #import "AppListResponse.h" -#import "App.h" +#import "TemporaryApp.h" #import "DataManager.h" #import @@ -60,7 +60,6 @@ static const char* TAG_APP_IS_RUNNING = "IsRunning"; self.statusMessage = statusMsg; node = node->children; - DataManager* dataMan = [[DataManager alloc] init]; while (node != NULL) { //Log(LOG_D, @"node: %s", node->name); @@ -96,7 +95,7 @@ static const char* TAG_APP_IS_RUNNING = "IsRunning"; appInfoNode = appInfoNode->next; } if (appId != nil) { - App* app = [dataMan createApp]; + TemporaryApp* app = [[TemporaryApp alloc] init]; app.name = appName; app.id = appId; app.isRunning = appIsRunning; diff --git a/Limelight/ViewControllers/MainFrameViewController.m b/Limelight/ViewControllers/MainFrameViewController.m index 9f670f8..b4bc662 100644 --- a/Limelight/ViewControllers/MainFrameViewController.m +++ b/Limelight/ViewControllers/MainFrameViewController.m @@ -23,6 +23,7 @@ #import "StreamFrameViewController.h" #import "LoadingFrameViewController.h" #import "ComputerScrollView.h" +#import "TemporaryApp.h" @implementation MainFrameViewController { NSOperationQueue* _opQueue; @@ -141,7 +142,7 @@ static NSMutableSet* hostList; - (void) mergeAppLists:(NSArray*) newList forHost:(Host*)host { DataManager* database = [[DataManager alloc] init]; - for (App* app in newList) { + for (TemporaryApp* app in newList) { BOOL appAlreadyInList = NO; for (App* savedApp in host.appList) { if ([app.id isEqualToString:savedApp.id]) { @@ -152,9 +153,7 @@ static NSMutableSet* hostList; } if (!appAlreadyInList) { app.host = host; - [host addAppListObject:app]; - } else { - [database removeApp:app]; + [database addAppFromTemporaryApp:app]; } } @@ -167,8 +166,7 @@ static NSMutableSet* hostList; } } if (appWasRemoved) { - [host removeAppListObject:app]; - [database removeApp:app]; + [database removeAppFromHost:app]; } } [database saveData]; diff --git a/Moonlight.xcodeproj/project.pbxproj b/Moonlight.xcodeproj/project.pbxproj index 30d8d9e..eb46b01 100644 --- a/Moonlight.xcodeproj/project.pbxproj +++ b/Moonlight.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 9832D1361BBCD5C50036EF48 /* TemporaryApp.m in Sources */ = {isa = PBXBuildFile; fileRef = 9832D1351BBCD5C50036EF48 /* TemporaryApp.m */; settings = {ASSET_TAGS = (); }; }; 987140041B04542F00AB57D5 /* libmoonlight-common.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FB1E43101AE8B0F200AFF679 /* libmoonlight-common.a */; }; 9E5D600B1A5A5A3900689918 /* Apache License.txt in Resources */ = {isa = PBXBuildFile; fileRef = 9E5D5FF81A5A5A3900689918 /* Apache License.txt */; }; 9E5D600C1A5A5A3900689918 /* Roboto-Black.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 9E5D5FF91A5A5A3900689918 /* Roboto-Black.ttf */; }; @@ -93,6 +94,8 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 9832D1341BBCD5C50036EF48 /* TemporaryApp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TemporaryApp.h; path = Database/TemporaryApp.h; sourceTree = ""; }; + 9832D1351BBCD5C50036EF48 /* TemporaryApp.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TemporaryApp.m; path = Database/TemporaryApp.m; sourceTree = ""; }; 98A03B4519F3514B00861ACA /* moonlight-common.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = "moonlight-common.xcodeproj"; path = "limelight-common-c/moonlight-common.xcodeproj"; sourceTree = ""; }; 9E5D5FF81A5A5A3900689918 /* Apache License.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "Apache License.txt"; sourceTree = ""; }; 9E5D5FF91A5A5A3900689918 /* Roboto-Black.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Roboto-Black.ttf"; sourceTree = ""; }; @@ -722,6 +725,8 @@ FBD3495D1A004412002D2A60 /* Settings.m */, FBD349601A0089F6002D2A60 /* DataManager.h */, FBD349611A0089F6002D2A60 /* DataManager.m */, + 9832D1341BBCD5C50036EF48 /* TemporaryApp.h */, + 9832D1351BBCD5C50036EF48 /* TemporaryApp.m */, ); name = Database; sourceTree = ""; @@ -847,6 +852,7 @@ FBD1C8E21A8AD71400C6703C /* Logger.m in Sources */, FB1D599A1BBCCD7E00F482CA /* AppCollectionView.m in Sources */, FB89463619F646E200339C8A /* StreamFrameViewController.m in Sources */, + 9832D1361BBCD5C50036EF48 /* TemporaryApp.m in Sources */, FB89462819F646E200339C8A /* CryptoManager.m in Sources */, FB89462E19F646E200339C8A /* PairManager.m in Sources */, FB9AFD371A7E02DB00872C98 /* HttpRequest.m in Sources */,