1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #if !defined(__has_feature) || !__has_feature(objc_arc) 6 #error "This file requires ARC support." 7 #endif 8 9 #import "remoting/ios/data_store.h" 10 11 @interface DataStore (Private) 12 - (NSString*)itemArchivePath; 13 @end 14 15 @implementation DataStore { 16 @private 17 NSMutableArray* _allHosts; 18 NSManagedObjectContext* _context; 19 NSManagedObjectModel* _model; 20 } 21 22 // Create or Get a static data store 23 + (DataStore*)sharedStore { 24 static DataStore* sharedStore = nil; 25 static dispatch_once_t onceToken; 26 dispatch_once(&onceToken, 27 ^{ sharedStore = [[super allocWithZone:nil] init]; }); 28 29 return sharedStore; 30 } 31 32 // General methods 33 + (id)allocWithZone:(NSZone*)zone { 34 return [self sharedStore]; 35 } 36 37 // Load data store from SQLLite backing store 38 - (id)init { 39 self = [super init]; 40 41 if (self) { 42 // Read in ChromotingModel.xdatamodeld 43 _model = [NSManagedObjectModel mergedModelFromBundles:nil]; 44 45 NSPersistentStoreCoordinator* psc = [[NSPersistentStoreCoordinator alloc] 46 initWithManagedObjectModel:_model]; 47 48 NSString* path = [self itemArchivePath]; 49 NSURL* storeUrl = [NSURL fileURLWithPath:path]; 50 51 NSError* error = nil; 52 53 NSDictionary* tryOptions = @{ 54 NSMigratePersistentStoresAutomaticallyOption : @YES, 55 NSInferMappingModelAutomaticallyOption : @YES 56 }; 57 NSDictionary* makeOptions = 58 @{NSMigratePersistentStoresAutomaticallyOption : @YES}; 59 60 if (![psc addPersistentStoreWithType:NSSQLiteStoreType 61 configuration:nil 62 URL:storeUrl 63 options:tryOptions 64 error:&error]) { 65 // An incompatible version of the store exists, delete it and start over 66 [[NSFileManager defaultManager] removeItemAtURL:storeUrl error:nil]; 67 68 [psc addPersistentStoreWithType:NSSQLiteStoreType 69 configuration:nil 70 URL:storeUrl 71 options:makeOptions 72 error:&error]; 73 [NSException raise:@"Open failed" 74 format:@"Reason: %@", [error localizedDescription]]; 75 } 76 77 // Create the managed object context 78 _context = [[NSManagedObjectContext alloc] init]; 79 [_context setPersistentStoreCoordinator:psc]; 80 81 // The managed object context can manage undo, but we don't need it 82 [_context setUndoManager:nil]; 83 84 _allHosts = nil; 85 } 86 return self; 87 } 88 89 // Committing to backing store 90 - (BOOL)saveChanges { 91 NSError* err = nil; 92 BOOL successful = [_context save:&err]; 93 return successful; 94 } 95 96 // Looking up the backing store path 97 - (NSString*)itemArchivePath { 98 NSArray* documentDirectories = NSSearchPathForDirectoriesInDomains( 99 NSDocumentDirectory, NSUserDomainMask, YES); 100 101 // Get one and only document directory from that list 102 NSString* documentDirectory = [documentDirectories objectAtIndex:0]; 103 104 return [documentDirectory stringByAppendingPathComponent:@"store.data"]; 105 } 106 107 // Return an array of all known hosts, if the list hasn't been loaded yet, then 108 // load it now 109 - (NSArray*)allHosts { 110 if (!_allHosts) { 111 NSFetchRequest* request = [[NSFetchRequest alloc] init]; 112 113 NSEntityDescription* e = 114 [[_model entitiesByName] objectForKey:@"HostPreferences"]; 115 116 [request setEntity:e]; 117 118 NSError* error; 119 NSArray* result = [_context executeFetchRequest:request error:&error]; 120 if (!result) { 121 [NSException raise:@"Fetch failed" 122 format:@"Reason: %@", [error localizedDescription]]; 123 } 124 _allHosts = [result mutableCopy]; 125 } 126 127 return _allHosts; 128 } 129 130 // Return a HostPreferences if it already exists, otherwise create a new 131 // HostPreferences to use 132 - (const HostPreferences*)createHost:(NSString*)hostId { 133 134 const HostPreferences* p = [self getHostForId:hostId]; 135 136 if (p == nil) { 137 p = [NSEntityDescription insertNewObjectForEntityForName:@"HostPreferences" 138 inManagedObjectContext:_context]; 139 p.hostId = hostId; 140 [_allHosts addObject:p]; 141 } 142 return p; 143 } 144 145 - (void)removeHost:(HostPreferences*)p { 146 [_context deleteObject:p]; 147 [_allHosts removeObjectIdenticalTo:p]; 148 } 149 150 // Search the store for any matching HostPreferences 151 // return the 1st match or nil 152 - (const HostPreferences*)getHostForId:(NSString*)hostId { 153 NSFetchRequest* request = [[NSFetchRequest alloc] init]; 154 155 NSEntityDescription* e = 156 [[_model entitiesByName] objectForKey:@"HostPreferences"]; 157 [request setEntity:e]; 158 159 NSPredicate* predicate = 160 [NSPredicate predicateWithFormat:@"(hostId = %@)", hostId]; 161 [request setPredicate:predicate]; 162 163 NSError* error; 164 NSArray* result = [_context executeFetchRequest:request error:&error]; 165 if (!result) { 166 [NSException raise:@"Fetch failed" 167 format:@"Reason: %@", [error localizedDescription]]; 168 } 169 170 for (HostPreferences* curHost in result) { 171 return curHost; 172 } 173 return nil; 174 } 175 176 @end 177