Fixed CB-330 - localStorage / SQLDatabase no longer persistent after iOS 5.01 update Fixed CB-347 - iOS 5.1 localStorage / SQLDatabase error after upgrading an app
Project: http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/commit/20527867 Tree: http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/tree/20527867 Diff: http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/diff/20527867 Branch: refs/heads/master Commit: 20527867f4958c331e34aeb4dd83695756e13585 Parents: 1a9027e Author: Shazron Abdullah <shaz...@apache.org> Authored: Tue Mar 20 15:20:01 2012 -0700 Committer: Shazron Abdullah <shaz...@apache.org> Committed: Tue Mar 20 15:20:01 2012 -0700 ---------------------------------------------------------------------- CordovaLib/Classes/CDV.h | 1 + CordovaLib/Classes/CDVAvailability.h | 10 + CordovaLib/Classes/CDVCommandDelegate.h | 3 + CordovaLib/Classes/CDVLocalStorage.h | 41 +++ CordovaLib/Classes/CDVLocalStorage.m | 299 ++++++++++++++++++ CordovaLib/Classes/CDVViewController.m | 43 ++-- CordovaLib/CordovaLib.xcodeproj/project.pbxproj | 12 + 7 files changed, 389 insertions(+), 20 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/blob/20527867/CordovaLib/Classes/CDV.h ---------------------------------------------------------------------- diff --git a/CordovaLib/Classes/CDV.h b/CordovaLib/Classes/CDV.h index 414ac59..b4e4ecd 100644 --- a/CordovaLib/Classes/CDV.h +++ b/CordovaLib/Classes/CDV.h @@ -45,6 +45,7 @@ #import "CDVSound.h" #import "CDVSplashScreen.h" #import "CDVWhitelist.h" +#import "CDVLocalStorage.h" #import "NSData+Base64.h" #import "NSDictionary+Extensions.h" http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/blob/20527867/CordovaLib/Classes/CDVAvailability.h ---------------------------------------------------------------------- diff --git a/CordovaLib/Classes/CDVAvailability.h b/CordovaLib/Classes/CDVAvailability.h index 363d175..ea643ff 100644 --- a/CordovaLib/Classes/CDVAvailability.h +++ b/CordovaLib/Classes/CDVAvailability.h @@ -38,3 +38,13 @@ #ifndef CORDOVA_VERSION_MIN_REQUIRED #define CORDOVA_VERSION_MIN_REQUIRED __CORDOVA_1_5_0 #endif + +/* + Returns YES if it is at least version specified as NSString(X) + Usage: + if (IsiOSVersion(@"5.1")) { + // do something for iOS 5.1 or greater + } + */ +#define IsiOSVersion(X) ([[[UIDevice currentDevice] systemVersion] compare:X options:NSNumericSearch] != NSOrderedAscending) + http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/blob/20527867/CordovaLib/Classes/CDVCommandDelegate.h ---------------------------------------------------------------------- diff --git a/CordovaLib/Classes/CDVCommandDelegate.h b/CordovaLib/Classes/CDVCommandDelegate.h index 7b379b5..74aef38 100644 --- a/CordovaLib/Classes/CDVCommandDelegate.h +++ b/CordovaLib/Classes/CDVCommandDelegate.h @@ -19,10 +19,13 @@ #import "CDVInvokedUrlCommand.h" +@class CDVPlugin; + @protocol CDVCommandDelegate <NSObject> - (NSString*) pathForResource:(NSString*)resourcepath; - (id) getCommandInstance:(NSString*)pluginName; +- (void) registerPlugin:(CDVPlugin*)plugin withClassName:(NSString*)className; - (BOOL) execute:(CDVInvokedUrlCommand*)command; @end http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/blob/20527867/CordovaLib/Classes/CDVLocalStorage.h ---------------------------------------------------------------------- diff --git a/CordovaLib/Classes/CDVLocalStorage.h b/CordovaLib/Classes/CDVLocalStorage.h new file mode 100644 index 0000000..0abd43d --- /dev/null +++ b/CordovaLib/Classes/CDVLocalStorage.h @@ -0,0 +1,41 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#import "CDVPlugin.h" + +#define kCDVLocalStorageErrorDomain @"kCDVLocalStorageErrorDomain" +#define kCDVLocalStorageFileOperationError 1 + +@interface CDVLocalStorage : CDVPlugin < UIWebViewDelegate > + +@property (nonatomic, readonly, retain) NSMutableArray* backupInfo; + +- (void) backup:(NSArray*)arguments withDict:(NSMutableDictionary*)options; +- (void) restore:(NSArray*)arguments withDict:(NSMutableDictionary*)options; +- (void) verifyAndFixDatabaseLocations:(NSArray*)arguments withDict:(NSMutableDictionary*)options; + +@end + +@interface CDVBackupInfo : NSObject + +@property (nonatomic, copy) NSString* original; +@property (nonatomic, copy) NSString* backup; +@property (nonatomic, copy) NSString* label; + +@end http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/blob/20527867/CordovaLib/Classes/CDVLocalStorage.m ---------------------------------------------------------------------- diff --git a/CordovaLib/Classes/CDVLocalStorage.m b/CordovaLib/Classes/CDVLocalStorage.m new file mode 100644 index 0000000..fbd5d1c --- /dev/null +++ b/CordovaLib/Classes/CDVLocalStorage.m @@ -0,0 +1,299 @@ +/* + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. + */ + +#import "CDVLocalStorage.h" +#import "CDV.h" + +@interface CDVLocalStorage () + +@property (nonatomic, readwrite, retain) NSMutableArray* backupInfo; // array of CDVBackupInfo objects +@property (nonatomic, readwrite, assign) id<UIWebViewDelegate> webviewDelegate; + +@end + + +@implementation CDVLocalStorage + +@synthesize backupInfo, webviewDelegate; + + +- (CDVPlugin*) initWithWebView:(UIWebView*)theWebView +{ + self = (CDVLocalStorage*)[super initWithWebView:theWebView]; + if (self) + { + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onResignActive) + name:UIApplicationWillResignActiveNotification object:nil]; + + NSString *original, *backup; + self.backupInfo = [NSMutableArray arrayWithCapacity:3]; + + // set up common folders + NSString* appLibraryFolder = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES)objectAtIndex:0]; + NSString* appDocumentsFolder = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]; + NSString* backupsFolder = [appDocumentsFolder stringByAppendingPathComponent:@"Backups"]; + + // create the backups folder + [[NSFileManager defaultManager] createDirectoryAtPath:backupsFolder withIntermediateDirectories:YES attributes:nil error:nil]; + + //////////// LOCALSTORAGE + + original = [[appLibraryFolder stringByAppendingPathComponent: + (IsiOSVersion(@"5.1")) ? @"Caches" : @"WebKit/LocalStorage"] + stringByAppendingPathComponent:@"file__0.localstorage"]; + + backup = [backupsFolder stringByAppendingPathComponent:@"localstorage.appdata.db"]; + + CDVBackupInfo* backupItem = [[[CDVBackupInfo alloc] init] autorelease]; + backupItem.backup = backup; + backupItem.original = original; + backupItem.label = @"localStorage database"; + + [self.backupInfo addObject:backupItem]; + + //////////// WEBSQL MAIN DB + + original = [[appLibraryFolder stringByAppendingPathComponent: + (IsiOSVersion(@"5.1")) ? @"Caches" : @"WebKit/Databases"] + stringByAppendingPathComponent:@"Databases.db"]; + + backup = [backupsFolder stringByAppendingPathComponent:@"websqlmain.appdata.db"]; + + backupItem = [[[CDVBackupInfo alloc] init] autorelease]; + backupItem.backup = backup; + backupItem.original = original; + backupItem.label = @"websql main database"; + + [self.backupInfo addObject:backupItem]; + + //////////// WEBSQL DATABASES + + original = [[appLibraryFolder stringByAppendingPathComponent: + (IsiOSVersion(@"5.1")) ? @"Caches" : @"WebKit/Databases"] + stringByAppendingPathComponent:@"file__0"]; + + backup = [backupsFolder stringByAppendingPathComponent:@"websqldbs.appdata.db"]; + + backupItem = [[[CDVBackupInfo alloc] init] autorelease]; + backupItem.backup = backup; + backupItem.original = original; + backupItem.label = @"websql databases"; + + [self.backupInfo addObject:backupItem]; + + //////////// + + // over-ride current webview delegate (for restore reasons) + self.webviewDelegate = theWebView.delegate; + theWebView.delegate = self; + + // verify the and fix the iOS 5.1 database locations once + [self verifyAndFixDatabaseLocations:nil withDict:nil]; + } + + return self; +} + +#pragma mark - +#pragma mark Plugin interface methods + +- (BOOL) copyFrom:(NSString*)src to:(NSString*)dest error:(NSError**)error +{ + NSFileManager* fileManager = [NSFileManager defaultManager]; + + if (![fileManager fileExistsAtPath:src]) { + + NSString* errorString = [NSString stringWithFormat:@"%@ file does not exist.", src]; + if (error != NULL) { + (*error) = [NSError errorWithDomain:kCDVLocalStorageErrorDomain + code:kCDVLocalStorageFileOperationError + userInfo:[NSDictionary dictionaryWithObject:errorString + forKey:NSLocalizedDescriptionKey]]; + } + return NO; + } + + // generate unique filepath in temp directory + CFUUIDRef uuidRef = CFUUIDCreate(kCFAllocatorDefault); + CFStringRef uuidString = CFUUIDCreateString(kCFAllocatorDefault, uuidRef); + NSString* tempBackup = [[NSTemporaryDirectory() stringByAppendingPathComponent:(NSString*)uuidString] stringByAppendingPathExtension:@"bak"]; + CFRelease(uuidString); + CFRelease(uuidRef); + + BOOL destExists = [fileManager fileExistsAtPath:dest]; + + // backup the dest + if (destExists && ![fileManager copyItemAtPath:dest toPath:tempBackup error:error]) { + return NO; + } + + // remove the dest + if (destExists && ![fileManager removeItemAtPath:dest error:error]) { + return NO; + } + + // copy src to dest + if ([fileManager copyItemAtPath:src toPath:dest error:error]) { + // success - cleanup - delete the backup to the dest + if ([fileManager fileExistsAtPath:tempBackup]) { + [fileManager removeItemAtPath:tempBackup error:error]; + } + return YES; + + } else { + // failure - we restore the temp backup file to dest + [fileManager copyItemAtPath:tempBackup toPath:dest error:error]; + // cleanup - delete the backup to the dest + if ([fileManager fileExistsAtPath:tempBackup]) { + [fileManager removeItemAtPath:tempBackup error:error]; + } + return NO; + } +} + +/* copy from webkitDbLocation to persistentDbLocation */ +- (void) backup:(NSArray*)arguments withDict:(NSMutableDictionary*)options; +{ + NSError* error = nil; + + for (CDVBackupInfo* info in self.backupInfo) + { + [self copyFrom:info.original to:info.backup error:&error]; + + if (error == nil) { + NSLog(@"Backed up: %@", info.label); + } else { + NSLog(@"Error in CDVLocalStorage (%@) backup: %@", info.label, [error localizedDescription]); + } + } +} + +/* copy from persistentDbLocation to webkitDbLocation */ +- (void) restore:(NSArray*)arguments withDict:(NSMutableDictionary*)options; +{ + NSError* error = nil; + + for (CDVBackupInfo* info in self.backupInfo) + { + [self copyFrom:info.backup to:info.original error:&error]; + + if (error == nil) { + NSLog(@"CDVLocalStorage restored: %@", info.label); + } else { + NSLog(@"Error in CDVLocalStorage (%@) restore: %@", info.label, [error localizedDescription]); + } + } +} + +- (void) verifyAndFixDatabaseLocations:(NSArray*)arguments withDict:(NSMutableDictionary*)options +{ + NSUserDefaults* appPreferences = [NSUserDefaults standardUserDefaults]; + NSBundle* mainBundle = [NSBundle mainBundle]; + + NSString* bundlePath = [[mainBundle bundlePath] stringByDeletingLastPathComponent]; + NSString* bundleIdentifier = [[mainBundle infoDictionary] objectForKey:@"CFBundleIdentifier"]; + + NSString* appPlistPath = [[bundlePath stringByAppendingPathComponent:@"Library/Preferences"] stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.plist", bundleIdentifier]]; + NSMutableDictionary* appPlistDict = [NSMutableDictionary dictionaryWithContentsOfFile:appPlistPath]; + + NSArray* keysToCheck = [NSArray arrayWithObjects: + @"WebKitLocalStorageDatabasePathPreferenceKey", @"WebDatabaseDirectory", nil]; + + + BOOL dirty = NO; + NSString* pathSuffix = (IsiOSVersion(@"5.1")) ? @"Library/Caches" : @"Library/WebKit"; + + for (NSString* key in keysToCheck) + { + NSString* value = [appPlistDict objectForKey:key]; + // verify path is in app bundle, if not - fix + if (![value hasPrefix:bundlePath]) { + [appPlistDict setValue:[bundlePath stringByAppendingPathComponent:pathSuffix] forKey:key]; + dirty = YES; + } + } + + if (dirty) + { + BOOL ok = [appPlistDict writeToFile:appPlistPath atomically:YES]; + NSLog(@"Fix applied for database locations?: %@", ok? @"YES":@"NO"); + + [appPreferences synchronize]; + } +} + +#pragma mark - +#pragma mark Notification handlers + +- (void) onResignActive +{ + [self backup:nil withDict:nil]; +} + +- (void) onAppTerminate +{ + [self backup:nil withDict:nil]; +} + +#pragma mark - +#pragma mark UIWebviewDelegate implementation and forwarding + +- (void) webViewDidStartLoad:(UIWebView*)theWebView +{ + [self restore:nil withDict:nil]; + + return [self.webviewDelegate webViewDidStartLoad:theWebView]; +} + +- (void) webViewDidFinishLoad:(UIWebView*)theWebView +{ + return [self.webviewDelegate webViewDidFinishLoad:theWebView]; +} + +- (void) webView:(UIWebView*)theWebView didFailLoadWithError:(NSError*)error +{ + return [self.webviewDelegate webView:theWebView didFailLoadWithError:error]; +} + +- (BOOL) webView:(UIWebView*)theWebView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType +{ + return [self.webviewDelegate webView:theWebView shouldStartLoadWithRequest:request navigationType:navigationType]; +} + +#pragma mark - +#pragma mark Over-rides + +- (void) dealloc +{ + self.backupInfo = nil; + + [super dealloc]; +} + +@end + + +#pragma mark - +#pragma mark CDVBackupInfo implementation + +@implementation CDVBackupInfo + +@synthesize original, backup, label; + +@end \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/blob/20527867/CordovaLib/Classes/CDVViewController.m ---------------------------------------------------------------------- diff --git a/CordovaLib/Classes/CDVViewController.m b/CordovaLib/Classes/CDVViewController.m index 6058e5b..f94814b 100644 --- a/CordovaLib/Classes/CDVViewController.m +++ b/CordovaLib/Classes/CDVViewController.m @@ -17,11 +17,7 @@ under the License. */ -#import "CDVViewController.h" -#import "CDVPlugin.h" -#import "CDVLocation.h" -#import "CDVConnection.h" -#import "NSDictionary+Extensions.h" +#import "CDV.h" #define SYMBOL_TO_NSSTRING_HELPER(x) @#x #define SYMBOL_TO_NSSTRING(x) SYMBOL_TO_NSSTRING_HELPER(x) @@ -70,6 +66,7 @@ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onAppDidEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil]; + self.commandDelegate = self; self.wwwFolderName = @"www"; self.startPage = @"index.html"; [self setWantsFullScreenLayout:YES]; @@ -170,6 +167,11 @@ } /* + * Fire up CDVLocalStorage to work-around iOS 5.1 WebKit storage bug limitations + */ + [self.commandDelegate registerPlugin:[[[CDVLocalStorage alloc] initWithWebView:self.webView] autorelease] withClassName:NSStringFromClass([CDVLocalStorage class])]; + + /* * This is for iOS 4.x, where you can allow inline <video> and <audio>, and also autoplay them */ if ([allowInlineMediaPlayback boolValue] && [self.webView respondsToSelector:@selector(allowsInlineMediaPlayback)]) { @@ -204,9 +206,6 @@ NSString* html = [NSString stringWithFormat:@"<html><body> %@ </body></html>", loadErr]; [self.webView loadHTMLString:html baseURL:nil]; } - - self.commandDelegate = self; - } - (NSArray*) parseInterfaceOrientations:(NSArray*)orientations @@ -487,7 +486,7 @@ */ else { - NSLog(@"PGAppDelegate::shouldStartLoadWithRequest: Received Unhandled URL %@", url); + NSLog(@"AppDelegate::shouldStartLoadWithRequest: Received Unhandled URL %@", url); if ([[UIApplication sharedApplication] canOpenURL:url]) { [[UIApplication sharedApplication] openURL:url]; @@ -693,7 +692,6 @@ BOOL gSplashScreenShown = NO; // Parse the returned JSON array. - //PG_SBJsonParser* jsonParser = [[[PG_SBJsonParser alloc] init] autorelease]; NSArray* queuedCommands = [queuedCommandsJSON objectFromJSONString]; @@ -769,6 +767,19 @@ BOOL gSplashScreenShown = NO; return retVal; } +- (void) registerPlugin:(CDVPlugin*)plugin withClassName:(NSString*)className +{ + if ([plugin respondsToSelector:@selector(setViewController:)]) { + [plugin setViewController:self]; + } + + if ([plugin respondsToSelector:@selector(setCommandDelegate:)]) { + [plugin setCommandDelegate:self.commandDelegate]; + } + + [self.pluginObjects setObject:plugin forKey:className]; +} + /** Returns an instance of a CordovaCommand object, based on its name. If one exists already, it is returned. */ @@ -796,16 +807,8 @@ BOOL gSplashScreenShown = NO; obj = [[NSClassFromString(className) alloc] initWithWebView:webView]; } - if ([obj isKindOfClass:[CDVPlugin class]] && [obj respondsToSelector:@selector(setViewController:)]) { - [obj setViewController:self]; - } - - if ([obj isKindOfClass:[CDVPlugin class]] && [obj respondsToSelector:@selector(setCommandDelegate:)]) { - [obj setCommandDelegate:self.commandDelegate]; - } - - if (obj != nil) { - [self.pluginObjects setObject:obj forKey:className]; + if (obj != nil && [obj isKindOfClass:[CDVPlugin class]]) { + [self registerPlugin:obj withClassName:className]; [obj release]; } else { NSLog(@"CDVPlugin class %@ (pluginName: %@) does not exist.", className, pluginName); http://git-wip-us.apache.org/repos/asf/incubator-cordova-ios/blob/20527867/CordovaLib/CordovaLib.xcodeproj/project.pbxproj ---------------------------------------------------------------------- diff --git a/CordovaLib/CordovaLib.xcodeproj/project.pbxproj b/CordovaLib/CordovaLib.xcodeproj/project.pbxproj index 9be7be1..ae4fdf0 100644 --- a/CordovaLib/CordovaLib.xcodeproj/project.pbxproj +++ b/CordovaLib/CordovaLib.xcodeproj/project.pbxproj @@ -69,6 +69,10 @@ 303259C9136B326300982B63 /* CDVPluginResult.h in Headers */ = {isa = PBXBuildFile; fileRef = 1F92F49E1314023E0046367C /* CDVPluginResult.h */; settings = {ATTRIBUTES = (Public, ); }; }; 303259CA136B326300982B63 /* CDVFileTransfer.h in Headers */ = {isa = PBXBuildFile; fileRef = C937A4541337599E002C4C79 /* CDVFileTransfer.h */; settings = {ATTRIBUTES = (Public, ); }; }; 30325A0C136B343700982B63 /* VERSION in Resources */ = {isa = PBXBuildFile; fileRef = 30325A0B136B343700982B63 /* VERSION */; }; + 3034979C1513D56A0090E688 /* CDVLocalStorage.h in Headers */ = {isa = PBXBuildFile; fileRef = 3034979A1513D56A0090E688 /* CDVLocalStorage.h */; }; + 3034979D1513D56A0090E688 /* CDVLocalStorage.h in Headers */ = {isa = PBXBuildFile; fileRef = 3034979A1513D56A0090E688 /* CDVLocalStorage.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 3034979E1513D56A0090E688 /* CDVLocalStorage.m in Sources */ = {isa = PBXBuildFile; fileRef = 3034979B1513D56A0090E688 /* CDVLocalStorage.m */; }; + 3034979F1513D56A0090E688 /* CDVLocalStorage.m in Sources */ = {isa = PBXBuildFile; fileRef = 3034979B1513D56A0090E688 /* CDVLocalStorage.m */; }; 3035621714104C34006C2D43 /* CDVWhitelistTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 30356213141049E1006C2D43 /* CDVWhitelistTests.m */; }; 30383DE01385F65600E37E22 /* CDVConnection.m in Sources */ = {isa = PBXBuildFile; fileRef = 307A8F9D1385A2EC00E43782 /* CDVConnection.m */; }; 30392E4E14F4FCAB00B9E0B8 /* CDVAvailability.h in Headers */ = {isa = PBXBuildFile; fileRef = 30392E4D14F4FCAB00B9E0B8 /* CDVAvailability.h */; }; @@ -180,6 +184,8 @@ 302965BB13A94E9D007046C5 /* CDVDebug.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDVDebug.h; path = Classes/CDVDebug.h; sourceTree = "<group>"; }; 303258D8136B2C9400982B63 /* Cordova.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Cordova.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 30325A0B136B343700982B63 /* VERSION */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = VERSION; sourceTree = "<group>"; }; + 3034979A1513D56A0090E688 /* CDVLocalStorage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDVLocalStorage.h; path = Classes/CDVLocalStorage.h; sourceTree = "<group>"; }; + 3034979B1513D56A0090E688 /* CDVLocalStorage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CDVLocalStorage.m; path = Classes/CDVLocalStorage.m; sourceTree = "<group>"; }; 30356212141049E1006C2D43 /* CDVWhitelistTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CDVWhitelistTests.h; sourceTree = "<group>"; }; 30356213141049E1006C2D43 /* CDVWhitelistTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CDVWhitelistTests.m; sourceTree = "<group>"; }; 30392E4D14F4FCAB00B9E0B8 /* CDVAvailability.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CDVAvailability.h; path = Classes/CDVAvailability.h; sourceTree = "<group>"; }; @@ -382,6 +388,8 @@ 888700D710922F56009987E8 /* Commands */ = { isa = PBXGroup; children = ( + 3034979A1513D56A0090E688 /* CDVLocalStorage.h */, + 3034979B1513D56A0090E688 /* CDVLocalStorage.m */, 30392E4D14F4FCAB00B9E0B8 /* CDVAvailability.h */, 30F5EBA914CA26E700987760 /* CDVCommandDelegate.h */, 30C684921407044A004C1A8E /* CDVURLProtocol.h */, @@ -503,6 +511,7 @@ 30F5EBAC14CA26E700987760 /* CDVCommandDelegate.h in Headers */, 30392E5014F502C200B9E0B8 /* CDVAvailability.h in Headers */, 301F2F2B14F3C9CA003FE9FC /* CDV.h in Headers */, + 3034979D1513D56A0090E688 /* CDVLocalStorage.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -541,6 +550,7 @@ 30F5EBAB14CA26E700987760 /* CDVCommandDelegate.h in Headers */, 301F2F2A14F3C9CA003FE9FC /* CDV.h in Headers */, 30392E4E14F4FCAB00B9E0B8 /* CDVAvailability.h in Headers */, + 3034979C1513D56A0090E688 /* CDVLocalStorage.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -745,6 +755,7 @@ 30C684831406CB38004C1A8E /* CDVWhitelist.m in Sources */, 30C684971407044B004C1A8E /* CDVURLProtocol.m in Sources */, 30A90B9414588697006178D3 /* JSONKit.m in Sources */, + 3034979F1513D56A0090E688 /* CDVLocalStorage.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -790,6 +801,7 @@ 30A90B9314588697006178D3 /* JSONKit.m in Sources */, 8852C43C14B65FD800F0E735 /* CDVViewController.m in Sources */, 8852C44114B65FD800F0E735 /* CDVCordovaView.m in Sources */, + 3034979E1513D56A0090E688 /* CDVLocalStorage.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; };