mirror of
https://github.com/moonlight-stream/moonlight-ios.git
synced 2026-02-16 10:31:02 +00:00
Use a temporary app object and database lock to avoid saving while the database is not in a consistent state
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
20
Limelight/Database/TemporaryApp.h
Normal file
20
Limelight/Database/TemporaryApp.h
Normal file
@@ -0,0 +1,20 @@
|
||||
//
|
||||
// TemporaryApp.h
|
||||
// Moonlight
|
||||
//
|
||||
// Created by Cameron Gutman on 9/30/15.
|
||||
// Copyright © 2015 Moonlight Stream. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#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
|
||||
13
Limelight/Database/TemporaryApp.m
Normal file
13
Limelight/Database/TemporaryApp.m
Normal file
@@ -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
|
||||
@@ -7,7 +7,7 @@
|
||||
//
|
||||
|
||||
#import "AppListResponse.h"
|
||||
#import "App.h"
|
||||
#import "TemporaryApp.h"
|
||||
#import "DataManager.h"
|
||||
#import <libxml2/libxml/xmlreader.h>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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 = "<group>"; };
|
||||
9832D1351BBCD5C50036EF48 /* TemporaryApp.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TemporaryApp.m; path = Database/TemporaryApp.m; sourceTree = "<group>"; };
|
||||
98A03B4519F3514B00861ACA /* moonlight-common.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = "moonlight-common.xcodeproj"; path = "limelight-common-c/moonlight-common.xcodeproj"; sourceTree = "<group>"; };
|
||||
9E5D5FF81A5A5A3900689918 /* Apache License.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "Apache License.txt"; sourceTree = "<group>"; };
|
||||
9E5D5FF91A5A5A3900689918 /* Roboto-Black.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Roboto-Black.ttf"; sourceTree = "<group>"; };
|
||||
@@ -722,6 +725,8 @@
|
||||
FBD3495D1A004412002D2A60 /* Settings.m */,
|
||||
FBD349601A0089F6002D2A60 /* DataManager.h */,
|
||||
FBD349611A0089F6002D2A60 /* DataManager.m */,
|
||||
9832D1341BBCD5C50036EF48 /* TemporaryApp.h */,
|
||||
9832D1351BBCD5C50036EF48 /* TemporaryApp.m */,
|
||||
);
|
||||
name = Database;
|
||||
sourceTree = "<group>";
|
||||
@@ -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 */,
|
||||
|
||||
Reference in New Issue
Block a user