diff --git a/calabash/Classes/Utils/LPUIAUserPrefsChannel.m b/calabash/Classes/Utils/LPUIAUserPrefsChannel.m index 4850f0dc8..366a6c717 100644 --- a/calabash/Classes/Utils/LPUIAUserPrefsChannel.m +++ b/calabash/Classes/Utils/LPUIAUserPrefsChannel.m @@ -200,66 +200,60 @@ - (NSDictionary *) requestForCommand:(NSString *) command { // Xcode 5 // ~/Library/Application Support/iPhone Simulator/[system version]/Library/Preferences/[bundle ID].plist // -// Xcode 6 +// Xcode 6.0* // ~/Library/Developer/CoreSimulator/Devices/[UDID]/data/Library/Preferences/[bundle ID].plist // see http://stackoverflow.com/questions/4977673/reading-preferences-set-by-uiautomations-uiaapplication-setpreferencesvaluefork // -// Also on Xcode 6 - When the app is first installed and launched, this plist -// file _does not exist_. However, if you ask the NSUserDefaults for its -// contents you will get back a non-empty string. It looks like NSUserDefaults -// is a concatenation of system level preferences (language, locale, newstand, -// etc.) and the application's own preferences. -// -// It is not clear whether or not the fact that file does not exist until -// the app tries to store something will influence UIA interactions. -// // Xcode 6.1 +// Xcode 6.1 + iOS 8.1 - all other simulator SDKs use Xcode 6.0* rules. // ~/Library/Developer/CoreSimulator/Devices/[Sim UDID]/data/Containers/Data/Application/[App UDID]/Library/Preferences/[bundle id].plist - (NSString *)simulatorPreferencesPath { static NSString *path = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ - // 1. Add a token to NSUserDefaults - // Xcode 6.0 and Xcode 6.1 have different locations for the preferences - // plist. It is impossible to know at runtime which to use a priori, so - // we set a token using NSUserDefaults and look for the plist that contains - // the token. - static NSString *const tokenKey = @"sh.calabash.preferences-token"; - CFUUIDRef udid = CFUUIDCreate(NULL); - NSString *tokenValue = (NSString *) CFUUIDCreateString(NULL, udid); - [[NSUserDefaults standardUserDefaults] setObject:tokenValue forKey:tokenKey]; - - if (udid != NULL) { CFRelease(udid); } - if (tokenValue) { [tokenValue release]; } - - [[NSUserDefaults standardUserDefaults] synchronize]; - NSString *plistName = [NSString stringWithFormat:@"%@.plist", [[NSBundle mainBundle] bundleIdentifier]]; - // 2. Find the app's Library directory so we can deduce the plist path. + // 1. Find the app's Library directory so we can deduce the plist path. NSArray *userLibDirURLs = [[NSFileManager defaultManager] URLsForDirectory:NSLibraryDirectory inDomains:NSUserDomainMask]; NSURL *userLibraryURL = [userLibDirURLs lastObject]; NSString *userLibraryPath = [userLibraryURL path]; - // 3. Use the the library path to deduce the simulator environment. + // 2. Use the the library path to deduce the simulator environment. + if ([userLibraryPath rangeOfString:@"CoreSimulator"].location == NSNotFound) { + // 3. Xcode < 6 environment. NSString *sandboxPath = [userLibraryPath substringToIndex:([userLibraryPath rangeOfString:@"Applications"].location)]; NSString *relativePlistPath = [NSString stringWithFormat:@"Library/Preferences/%@", plistName]; NSString *unsanitizedPlistPath = [sandboxPath stringByAppendingPathComponent:relativePlistPath]; path = [[unsanitizedPlistPath stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding] copy]; } else { - // First candidate. Xcode >= 6.1 CoreSimulator enviroments; preferences - // plist is in the application sandbox. - path = [[self stringForXcode61PreferencesPlistWithUserLibraryPath:userLibraryPath - plistName:plistName - tokenKey:tokenKey - tokenValue:tokenValue] copy]; - - // Second candidate. Xcode < 6.1 CoreSimulator environments; preferences - // plist is in the Simulator Library/Preferences. - if (!path) { - path = [[self stringForXcode60PreferencesPlistWithUserLibraryPath:userLibraryPath - plistName:plistName] copy]; + + /* + 3. CoreSimulator environments + + * In Xcode 6.1 + iOS >= 8.1, UIAutomation and NSUserDefaults do IO on + the same plist in the app's sandbox. + * In Xcode 6.1 and iOS < 8.1, UIAutomation does IO on the a file in + < SIM DIRECTORY >/data/Library/Preferences/ and NSUserDefaults does + IO on a plist in the app's sandbox. + * In Xcode 6.0*, NSUserDefaults and UIAutomation do IO on the same plist + in < SIM DIRECTORY >/data/Library/Preferences. + + Since iOS 8.1 only ships with Xcode 6.1, we can check the system version + at runtime and choose the correct plist. + */ + + NSString *systemVersion = [[UIDevice currentDevice] systemVersion]; + if ([systemVersion compare:@"8.1" options:NSNumericSearch] != NSOrderedAscending) { + NSString *relativePlistPath = [NSString stringWithFormat:@"Preferences/%@", plistName]; + NSString *unsanitizedPlistPath = [userLibraryPath stringByAppendingPathComponent:relativePlistPath]; + path = [[unsanitizedPlistPath stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding] copy]; + } else { + NSRange range = [userLibraryPath rangeOfString:@"data"]; + NSString *simulatorDataPath = [userLibraryPath substringToIndex:range.location + range.length]; + NSString *relativePlistPath = [NSString stringWithFormat:@"Library/Preferences/%@", plistName]; + NSString *unsanitizedPlistPath = [simulatorDataPath stringByAppendingPathComponent:relativePlistPath]; + path = [[unsanitizedPlistPath stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding] copy]; } } }); @@ -267,38 +261,6 @@ - (NSString *)simulatorPreferencesPath { return path; } -- (NSString *) stringForXcode61PreferencesPlistWithUserLibraryPath:(NSString *) aUserLibraryPath - plistName:(NSString *) aPlistName - tokenKey:(NSString *) aTokenKey - tokenValue:(NSString *) aTokenValue { - NSString *relativePlistPath = [NSString stringWithFormat:@"Preferences/%@", aPlistName]; - NSString *unsanitizedPlistPath = [aUserLibraryPath stringByAppendingPathComponent:relativePlistPath]; - NSString *path = [unsanitizedPlistPath stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; - - if (![[NSFileManager defaultManager] fileExistsAtPath:path]) { - return nil; - } - - NSDictionary *dictionary = [NSDictionary dictionaryWithContentsOfFile:path]; - if ([[dictionary objectForKey:aTokenKey] isEqualToString:aTokenValue]) { - return path; - } else { - return nil; - } -} - -- (NSString *) stringForXcode60PreferencesPlistWithUserLibraryPath:(NSString *) aUserLibraryPath - plistName:(NSString *) aPlistName { - // Xcode 6.0 and 6.0.1 have a buggy NSUserDefaults implementation. Values - // may or may not be written when [NSUserDefaults synchronize] is called, so - // it is not worth checking for the key/token pair. - NSRange range = [aUserLibraryPath rangeOfString:@"data"]; - NSString *simulatorDataPath = [aUserLibraryPath substringToIndex:range.location + range.length]; - NSString *relativePlistPath = [NSString stringWithFormat:@"Library/Preferences/%@", aPlistName]; - NSString *unsanitizedPlistPath = [simulatorDataPath stringByAppendingPathComponent:relativePlistPath]; - return [unsanitizedPlistPath stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; -} - #endif // TARGET_IPHONE_SIMULATOR @end