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