├── PFFileSaveEventuallyExample ├── .DS_Store ├── nyancat.jpg ├── AppDelegate.h ├── main.m ├── Images.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── PFFileManager.h ├── Info.plist ├── Main.storyboard ├── Base.lproj │ └── LaunchScreen.xib ├── AppDelegate.m └── PFFileManager.m ├── Podfile ├── PFFileSaveEventuallyExample.xcworkspace ├── xcuserdata │ └── thibauddavid.xcuserdatad │ │ ├── UserInterfaceState.xcuserstate │ │ └── WorkspaceSettings.xcsettings └── contents.xcworkspacedata ├── .gitignore ├── PFFileEventuallySaver ├── PFFileEventuallySaver.h └── PFFileEventuallySaver.m ├── README.md └── PFFileSaveEventuallyExample.xcodeproj └── project.pbxproj /PFFileSaveEventuallyExample/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thibauddavid/PFFileSaveEventually/HEAD/PFFileSaveEventuallyExample/.DS_Store -------------------------------------------------------------------------------- /PFFileSaveEventuallyExample/nyancat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thibauddavid/PFFileSaveEventually/HEAD/PFFileSaveEventuallyExample/nyancat.jpg -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '7.0' 3 | 4 | target 'PFFileSaveEventuallyExample' do 5 | pod 'Parse' 6 | pod 'Reachability', '~> 3.2' 7 | end 8 | 9 | -------------------------------------------------------------------------------- /PFFileSaveEventuallyExample.xcworkspace/xcuserdata/thibauddavid.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thibauddavid/PFFileSaveEventually/HEAD/PFFileSaveEventuallyExample.xcworkspace/xcuserdata/thibauddavid.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /PFFileSaveEventuallyExample.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | .DS_Store 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | *.xcworkspace 13 | !default.xcworkspace 14 | xcuserdata 15 | profile 16 | *.moved-aside 17 | DerivedData 18 | .idea/ 19 | # Pods - for those of you who use CocoaPods 20 | Pods 21 | -------------------------------------------------------------------------------- /PFFileSaveEventuallyExample/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // PFFileSaveEventuallyExample 4 | // 5 | // Created by Thibaud David on 10/09/2015. 6 | // Copyright (c) 2015 Thibaud David. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /PFFileSaveEventuallyExample/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // PFFileSaveEventuallyExample 4 | // 5 | // Created by Thibaud David on 10/09/2015. 6 | // Copyright (c) 2015 Thibaud David. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /PFFileSaveEventuallyExample.xcworkspace/xcuserdata/thibauddavid.xcuserdatad/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | HasAskedToTakeAutomaticSnapshotBeforeSignificantChanges 6 | 7 | SnapshotAutomaticallyBeforeSignificantChanges 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /PFFileSaveEventuallyExample/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /PFFileEventuallySaver/PFFileEventuallySaver.h: -------------------------------------------------------------------------------- 1 | // 2 | // PFFileEventuallySaver.h 3 | // PFFileSaveEventuallyExample 4 | // 5 | // Created by Thibaud David on 10/09/2015. 6 | // Copyright (c) 2015 Thibaud David. All rights reserved. 7 | // 8 | 9 | #import 10 | @class PFFile; 11 | 12 | #define kPFFILE_MANAGER_OBJECT_FILE_KEY @"file" 13 | #define kPFFILE_MANAGER_TMP_DIRECTORY @"tmpPFFiles" 14 | #define kPFFILE_MANAGER_PENDING_FILES @"PFPendingFiles" 15 | #define kPFFILE_CONTAINER_OBJECT_CLASSNAME @"MyObjectClass" 16 | 17 | @interface PFFileEventuallySaver : NSObject 18 | 19 | +(instancetype)getInstance; 20 | 21 | -(void)trySaveobjectAtURL:(NSURL *)URL associatedObjects:(NSArray *)objects withBlock:(void(^)(PFFile *file, NSError *error))block progressBlock:(void(^)(int percentDone))progressBlock; 22 | +(NSURL *)fileURLInTmpWithName:(NSString *)filename; 23 | -(BOOL)hasPendingFiles; 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /PFFileSaveEventuallyExample/PFFileManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // PFFileManager.h 3 | // PFFileSaveEventuallyExample 4 | // 5 | // Created by Thibaud David on 10/09/2015. 6 | // Copyright (c) 2015 Thibaud David. All rights reserved. 7 | // 8 | 9 | #import 10 | @class PFFile; 11 | 12 | #define kPFFILE_MANAGER_OBJECT_FILE_KEY @"file" 13 | #define kPFFILE_MANAGER_TMP_DIRECTORY @"tmpPFFiles" 14 | #define kPFFILE_MANAGER_PENDING_FILES @"PFPendingFiles" 15 | 16 | @interface PFFileManager : NSObject 17 | 18 | +(instancetype)getInstance; 19 | 20 | -(void)trySaveobjectAtURL:(NSURL *)URL associatedObjects:(NSArray *)objects withBlock:(void(^)(PFFile *file, NSError *error))block progressBlock:(void(^)(int percentDone))progressBlock; 21 | +(NSURL *)fileURLInTmpWithName:(NSString *)filename extension:(NSString *)extension; 22 | +(NSString *)filePathInTmpWithName:(NSString *)filename extension:(NSString *)extension; 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /PFFileSaveEventuallyExample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | com.thibauddavid.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### PFFileEventuallySaver 2 | 3 | This is a sample class to save a PFFile eventually. 4 | 5 | As you might know, that feature isn't available within ParseSDK. 6 | 7 | That's only a working PoC with limitations such as only working for a single Parse class to associate saved PFFile on. 8 | 9 | It requires Reachability `pod 'Reachability', '~> 3.2'` 10 | 11 | How to use it ? Well, I guess the sample projects describes it well, but here is a piece of code to understand how it works : 12 | 13 | (Remember to run `pod install` to resolve dependencies before running example) 14 | 15 | /* 16 | This example uses an UIImage, but this works with any file writable as NSData 17 | We begin by writing this image in our tmp directory with an uuid as name. 18 | */ 19 | UIImage *nyancat = [UIImage imageNamed:@"nyancat.jpg"]; 20 | NSData *imageData = UIImageJPEGRepresentation(nyancat, 0.5); 21 | 22 | NSString *filename = [[NSUUID UUID] UUIDString]; 23 | NSURL *fileUrl = [PFFileEventuallySaver fileURLInTmpWithName:filename]; 24 | 25 | [imageData writeToURL:fileUrl atomically:YES]; 26 | 27 | 28 | /* 29 | We create a PFObject (you can pass an array to below function if you need your file to be saved on several objects). If upload works on first time, do what you want with your file, like linking it on your PFobject. 30 | 31 | If saving fails, it'll be retried as soon as network is available, on this session or nexts launches of app. 32 | In that case, the pointer at key kPFFILE_MANAGER_OBJECT_FILE_KEY of your PFFObject will be set with the PFFile, then saved eventually within PFFileEventuallySaver 33 | */ 34 | PFObject *object = [PFObject objectWithClassName:kPFFILE_CONTAINER_OBJECT_CLASSNAME]; 35 | 36 | [[PFFileEventuallySaver getInstance] trySaveobjectAtURL:fileUrl associatedObjects:@[object] withBlock:^(PFFile *file, NSError *error) { 37 | if(!error) 38 | { 39 | NSLog(@"[First try, network is fine] File saved, saving PFObject"); 40 | 41 | object[kPFFILE_MANAGER_OBJECT_FILE_KEY] = file; 42 | [object saveEventually]; 43 | 44 | NSLog(@"Try again disabling your network connection"); 45 | } 46 | else 47 | { 48 | NSLog(@"No network, connect back your wifi, or relaunch app. Your file will be sent"); 49 | } 50 | } progressBlock:^(int percentDone) { 51 | NSLog(@"[First try, network is fine] Sending file %d/100%%", percentDone); 52 | }]; 53 | 54 | 55 | This could be greatly improved, but I thought you guys might found that useful, as I would've wanted to find a similar working example. 56 | -------------------------------------------------------------------------------- /PFFileSaveEventuallyExample/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /PFFileSaveEventuallyExample/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /PFFileSaveEventuallyExample/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // PFFileSaveEventuallyExample 4 | // 5 | // Created by Thibaud David on 10/09/2015. 6 | // Copyright (c) 2015 Thibaud David. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import "Parse.h" 11 | #import "PFFileEventuallySaver.h" 12 | 13 | @interface AppDelegate () 14 | 15 | @end 16 | 17 | @implementation AppDelegate 18 | 19 | 20 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 21 | // Override point for customization after application launch. 22 | 23 | NSString *parseClientID = nil; 24 | NSString *parseClientKey = nil; 25 | 26 | 27 | [Parse enableLocalDatastore]; 28 | if(parseClientID && parseClientKey) 29 | { 30 | [Parse setApplicationId:parseClientID clientKey:parseClientKey]; 31 | 32 | /* 33 | On PFFileEventuallySave init, a tmp directory will be created to store data of unsaved PFfiles"); 34 | */ 35 | [PFFileEventuallySaver getInstance]; 36 | 37 | if(![[PFFileEventuallySaver getInstance] hasPendingFiles]) 38 | { // Avoid adding several time this sample to queue 39 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^ 40 | { 41 | [self startExample]; 42 | }); 43 | } 44 | } 45 | else 46 | { 47 | NSLog(@"To test this example, you must setup a valid parseClientID and parseClientKey in AppDelegate.h"); 48 | } 49 | 50 | 51 | return YES; 52 | } 53 | 54 | -(void)startExample 55 | { 56 | /* 57 | This example uses an UIImage, but this works with any file writable as NSData 58 | We begin by writing this image in our tmp directory with an uuid as name. 59 | */ 60 | UIImage *nyancat = [UIImage imageNamed:@"nyancat.jpg"]; 61 | NSData *imageData = UIImageJPEGRepresentation(nyancat, 0.5); 62 | 63 | NSString *filename = [[NSUUID UUID] UUIDString]; 64 | NSURL *fileUrl = [PFFileEventuallySaver fileURLInTmpWithName:filename]; 65 | 66 | [imageData writeToURL:fileUrl atomically:YES]; 67 | 68 | 69 | /* 70 | We create a PFObject (you can pass an array to below function if you need your file to be saved on several objects). If upload works on first time, do what you want with your file, like linking it on your PFobject. 71 | 72 | If saving fails, it'll be retried as soon as network is available, on this session or nexts launches of app. 73 | In that case, the pointer at key kPFFILE_MANAGER_OBJECT_FILE_KEY of your PFFObject will be set with the PFFile, then saved eventually within PFFileEventuallySaver 74 | */ 75 | PFObject *object = [PFObject objectWithClassName:kPFFILE_CONTAINER_OBJECT_CLASSNAME]; 76 | 77 | [[PFFileEventuallySaver getInstance] trySaveobjectAtURL:fileUrl associatedObjects:@[object] withBlock:^(PFFile *file, NSError *error) { 78 | if(!error) 79 | { 80 | NSLog(@"[First try, network is fine] File saved, saving PFObject"); 81 | 82 | object[kPFFILE_MANAGER_OBJECT_FILE_KEY] = file; 83 | [object saveEventually]; 84 | 85 | NSLog(@"Try again disabling your network connection"); 86 | } 87 | else 88 | { 89 | NSLog(@"No network, connect back your wifi, or relaunch app. Your file will be sent"); 90 | } 91 | } progressBlock:^(int percentDone) { 92 | NSLog(@"[First try, network is fine] Sending file %d/100%%", percentDone); 93 | }]; 94 | } 95 | 96 | 97 | - (void)applicationWillResignActive:(UIApplication *)application { 98 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 99 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 100 | } 101 | 102 | - (void)applicationDidEnterBackground:(UIApplication *)application { 103 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 104 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 105 | } 106 | 107 | - (void)applicationWillEnterForeground:(UIApplication *)application { 108 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 109 | } 110 | 111 | - (void)applicationDidBecomeActive:(UIApplication *)application { 112 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 113 | } 114 | 115 | - (void)applicationWillTerminate:(UIApplication *)application { 116 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 117 | } 118 | 119 | @end 120 | -------------------------------------------------------------------------------- /PFFileEventuallySaver/PFFileEventuallySaver.m: -------------------------------------------------------------------------------- 1 | // 2 | // PFFileEventuallySaver.m 3 | // PFFileSaveEventuallyExample 4 | // 5 | // Created by Thibaud David on 10/09/2015. 6 | // Copyright (c) 2015 Thibaud David. All rights reserved. 7 | // 8 | 9 | #import "PFFileEventuallySaver.h" 10 | #import "Reachability.h" 11 | #import "Parse.h" 12 | 13 | @implementation PFFileEventuallySaver 14 | { 15 | NSMutableArray *pendingUploads; 16 | } 17 | 18 | +(instancetype)getInstance 19 | { 20 | static PFFileEventuallySaver *INSTANCE; 21 | 22 | static dispatch_once_t onceToken; 23 | dispatch_once(&onceToken, ^{ 24 | INSTANCE = [[self alloc] init]; 25 | }); 26 | return INSTANCE; 27 | } 28 | 29 | -(instancetype)init 30 | { 31 | self = [super init]; 32 | 33 | if(self) 34 | { 35 | Reachability* reach = [Reachability reachabilityWithHostname:@"www.google.com"]; 36 | [reach startNotifier]; 37 | 38 | [self createTmpFolderIfNotExisting]; 39 | 40 | NSArray *previousPending = [[NSArray alloc] initWithContentsOfFile:[PFFileEventuallySaver pendingFile]]; 41 | pendingUploads = previousPending ? [previousPending mutableCopy] : [NSMutableArray array]; 42 | NSLog(@"Pending uploads :\n%@", pendingUploads); 43 | 44 | [self handleNetworkReachable]; 45 | } 46 | return self; 47 | } 48 | -(BOOL)hasPendingFiles 49 | { 50 | return pendingUploads.count > 0; 51 | } 52 | 53 | -(void)handleNetworkReachable 54 | { 55 | [[NSNotificationCenter defaultCenter] addObserverForName:kReachabilityChangedNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) 56 | { 57 | Reachability * reach = [note object]; 58 | 59 | if([reach isReachable]) 60 | { 61 | if(pendingUploads.count > 0) 62 | { 63 | NSLog(@"[PFFileManager] Handling %lu pending uploads", (unsigned long)pendingUploads.count); 64 | for(NSString *objectName in [pendingUploads copy]) 65 | { 66 | [self localobjectsForURL:objectName withBlock:^(NSArray *objects, NSError *error) 67 | { 68 | if(!error) 69 | { 70 | NSURL *fileURL = [PFFileEventuallySaver fileURLInTmpWithName:objectName]; 71 | [self trySaveobjectAtURL:fileURL associatedObjects:objects withBlock:^(PFFile *file, NSError *error) 72 | { 73 | if(!error) 74 | { 75 | for(PFObject *object in objects) 76 | { 77 | object[kPFFILE_MANAGER_OBJECT_FILE_KEY] = file; 78 | [object saveEventually]; 79 | } 80 | } 81 | else 82 | { 83 | NSLog(@"[PFFileManager] Still an error, will try again on next reachability %@", error); 84 | } 85 | } progressBlock:^(int percentDone) { 86 | 87 | }]; 88 | } 89 | else 90 | { 91 | NSLog(@"[PFFileManager] Couldn't retrieve objects for filename %@", objectName); 92 | } 93 | }]; 94 | } 95 | } 96 | } 97 | }]; 98 | } 99 | 100 | -(void)localobjectsForURL:(NSString *)URL withBlock:(void(^)(NSArray *objects, NSError *error))block 101 | { 102 | PFQuery *query = [PFQuery queryWithClassName:kPFFILE_CONTAINER_OBJECT_CLASSNAME]; 103 | [query fromPinWithName:URL]; 104 | [query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) 105 | { 106 | if(!error) 107 | block(objects, nil); 108 | else 109 | block(nil, error); 110 | }]; 111 | } 112 | 113 | -(void)createTmpFolderIfNotExisting 114 | { 115 | NSError *error; 116 | NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 117 | NSString *documentsDirectory = [paths firstObject]; 118 | NSString *dataPath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:@"/%@", kPFFILE_MANAGER_TMP_DIRECTORY]]; 119 | if (![[NSFileManager defaultManager] fileExistsAtPath:dataPath]) 120 | { 121 | [[NSFileManager defaultManager] createDirectoryAtPath:dataPath withIntermediateDirectories:NO attributes:nil error:&error]; 122 | 123 | if(error) 124 | NSLog(@"Error creating tmp folder %@", error); 125 | } 126 | else 127 | NSLog(@"Tmp folder already exists"); 128 | } 129 | 130 | +(NSString *)pendingFile 131 | { 132 | NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 133 | NSString *path = [paths objectAtIndex:0]; 134 | return [path stringByAppendingPathComponent:kPFFILE_MANAGER_PENDING_FILES]; 135 | } 136 | 137 | +(NSURL *)fileURLInTmpWithName:(NSString *)filename 138 | { 139 | NSURL *applicationDocumentsDirectoryy =[[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory 140 | inDomains:NSUserDomainMask] lastObject]; 141 | return [[applicationDocumentsDirectoryy URLByAppendingPathComponent:kPFFILE_MANAGER_TMP_DIRECTORY] URLByAppendingPathComponent:filename]; 142 | } 143 | 144 | +(void)deleteFileAtURL:(NSURL *)url 145 | { 146 | NSError *error; 147 | [[NSFileManager defaultManager] removeItemAtURL:url error:&error]; 148 | if(error) 149 | NSLog(@"Error deleting file with name %@ --> %@", [url lastPathComponent], error); 150 | } 151 | 152 | -(void)trySaveobjectAtURL:(NSURL *)URL associatedObjects:(NSArray *)objects withBlock:(void(^)(PFFile *file, NSError *error))block progressBlock:(void(^)(int percentDone))progressBlock 153 | { 154 | PFFile *file = [PFFile fileWithData:[NSData dataWithContentsOfURL:URL]]; 155 | 156 | [self addUrlToQueue:URL withobjects:objects]; 157 | 158 | [file saveInBackgroundWithBlock:^(BOOL success, NSError *error) 159 | { 160 | if(!error) 161 | { 162 | [PFFileEventuallySaver deleteFileAtURL:URL]; 163 | [self removeURLFromQueue:URL withobjects:objects]; 164 | block(file, nil); 165 | } 166 | else{ 167 | block(nil, error); 168 | } 169 | } 170 | progressBlock:progressBlock]; 171 | } 172 | -(void)persistPendingUploads 173 | { 174 | [pendingUploads writeToFile:[PFFileEventuallySaver pendingFile] atomically:YES]; 175 | } 176 | 177 | -(void)addUrlToQueue:(NSURL *)URL withobjects:(NSArray *)objects 178 | { 179 | NSLog(@"[PFFileManager] Adding file with name %@ to queue with %lu associated objects", [URL lastPathComponent], (unsigned long)objects.count); 180 | [pendingUploads addObject:[URL lastPathComponent]]; 181 | [self persistPendingUploads]; 182 | [PFObject pinAllInBackground:objects withName:[URL lastPathComponent]]; 183 | } 184 | -(void)removeURLFromQueue:(NSURL *)URL withobjects:(NSArray *)objects 185 | { 186 | NSLog(@"[PFFileManager] Removing file with name %@ to queue with %lu associated objects", [URL lastPathComponent], (unsigned long)objects.count); 187 | [pendingUploads removeObject:[URL lastPathComponent]]; 188 | [self persistPendingUploads]; 189 | [PFObject unpinAllInBackground:objects withName:[URL lastPathComponent]]; 190 | } 191 | 192 | @end 193 | -------------------------------------------------------------------------------- /PFFileSaveEventuallyExample/PFFileManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // PFFileManager.m 3 | // PFFileSaveEventuallyExample 4 | // 5 | // Created by Thibaud David on 10/09/2015. 6 | // Copyright (c) 2015 Thibaud David. All rights reserved. 7 | // 8 | 9 | #import "PFFileManager.h" 10 | #import "Reachability.h" 11 | #import "Parse.h" 12 | 13 | @implementation PFFileManager 14 | { 15 | NSMutableArray *pendingUploads; 16 | } 17 | 18 | +(instancetype)getInstance 19 | { 20 | static PFFileManager *INSTANCE; 21 | 22 | static dispatch_once_t onceToken; 23 | dispatch_once(&onceToken, ^{ 24 | INSTANCE = [[self alloc] init]; 25 | }); 26 | return INSTANCE; 27 | } 28 | 29 | -(instancetype)init 30 | { 31 | self = [super init]; 32 | 33 | if(self) 34 | { 35 | Reachability* reach = [Reachability reachabilityWithHostname:@"www.google.com"]; 36 | [reach startNotifier]; 37 | 38 | [self createTmpFolderIfNotExisting]; 39 | 40 | NSArray *previousPending = [[NSArray alloc] initWithContentsOfFile:[PFFileManager pendingFile]]; 41 | pendingUploads = previousPending ? [previousPending mutableCopy] : [NSMutableArray array]; 42 | NSLog(@"Pending uploads :\n%@", pendingUploads); 43 | 44 | [self handleNetworkReachable]; 45 | } 46 | return self; 47 | } 48 | 49 | -(void)handleNetworkReachable 50 | { 51 | [[NSNotificationCenter defaultCenter] addObserverForName:kReachabilityChangedNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) 52 | { 53 | Reachability * reach = [note object]; 54 | 55 | if([reach isReachable]) 56 | { 57 | if(pendingUploads.count > 0) 58 | { 59 | NSLog(@"[PFFileManager] Handling %lu pending uploads", (unsigned long)pendingUploads.count); 60 | for(NSString *objectName in [pendingUploads copy]) 61 | { 62 | [self localobjectsForURL:objectName withBlock:^(NSArray *objects, NSError *error) 63 | { 64 | if(!error) 65 | { 66 | NSURL *fileURL = [PFFileManager fileURLInTmpWithName:objectName]; 67 | [self trySaveobjectAtURL:fileURL associatedObjects:objects withBlock:^(PFFile *file, NSError *error) 68 | { 69 | if(!error) 70 | { 71 | for(PFObject *object in objects) 72 | { 73 | object[kPFFILE_MANAGER_OBJECT_FILE_KEY] = file; 74 | [object saveEventually]; 75 | } 76 | } 77 | else 78 | { 79 | NSLog(@"[PFFileManager] Still an error, will try again on next reachability %@", error); 80 | } 81 | } progressBlock:^(int percentDone) { 82 | NSLog(@"[PFFileManager] File %@, upload %d/100 %%", objectName, percentDone); 83 | }]; 84 | } 85 | else 86 | { 87 | NSLog(@"[PFFileManager] Couldn't retrieve objects for URL %@", objectName); 88 | } 89 | }]; 90 | } 91 | } 92 | } 93 | }]; 94 | } 95 | 96 | -(void)localobjectsForURL:(NSString *)URL withBlock:(void(^)(NSArray *objects, NSError *error))block 97 | { 98 | PFQuery *query = [PFObject query]; 99 | [query fromPinWithName:URL]; 100 | [query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) 101 | { 102 | if(!error) 103 | block(objects, nil); 104 | else 105 | block(nil, error); 106 | }]; 107 | } 108 | 109 | -(void)createTmpFolderIfNotExisting 110 | { 111 | NSError *error; 112 | NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 113 | NSString *documentsDirectory = [paths firstObject]; 114 | NSString *dataPath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:@"/%@", kPFFILE_MANAGER_TMP_DIRECTORY]]; 115 | if (![[NSFileManager defaultManager] fileExistsAtPath:dataPath]) 116 | { 117 | [[NSFileManager defaultManager] createDirectoryAtPath:dataPath withIntermediateDirectories:NO attributes:nil error:&error]; 118 | 119 | if(error) 120 | NSLog(@"Error creating tmp folder %@", error); 121 | } 122 | else 123 | NSLog(@"Tmp folder already exists"); 124 | } 125 | 126 | +(NSString *)pendingFile 127 | { 128 | NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 129 | NSString *path = [paths objectAtIndex:0]; 130 | return [path stringByAppendingPathComponent:kPFFILE_MANAGER_TMP_DIRECTORY]; 131 | } 132 | +(NSString *)filePathInTmpWithName:(NSString *)filename 133 | { 134 | NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); 135 | NSString *tmpPath = [[paths objectAtIndex:0] stringByAppendingPathComponent:kPFFILE_MANAGER_TMP_DIRECTORY]; 136 | return [tmpPath stringByAppendingPathComponent:filename]; 137 | } 138 | +(NSString *)filePathInTmpWithName:(NSString *)filename extension:(NSString *)extension 139 | { 140 | return [[PFFileManager filePathInTmpWithName:filename] stringByAppendingPathExtension:extension]; 141 | } 142 | 143 | +(NSURL *)fileURLInTmpWithName:(NSString *)filename 144 | { 145 | NSURL *applicationDocumentsDirectoryy =[[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory 146 | inDomains:NSUserDomainMask] lastObject]; 147 | return [[applicationDocumentsDirectoryy URLByAppendingPathComponent:kPFFILE_MANAGER_TMP_DIRECTORY] URLByAppendingPathComponent:filename]; 148 | } 149 | +(NSURL *)fileURLInTmpWithName:(NSString *)filename extension:(NSString *)extension 150 | { 151 | return [[PFFileManager fileURLInTmpWithName:filename] URLByAppendingPathExtension:extension]; 152 | } 153 | +(void)deleteFileAtURL:(NSURL *)url 154 | { 155 | NSError *error; 156 | [[NSFileManager defaultManager] removeItemAtURL:url error:&error]; 157 | if(error) 158 | NSLog(@"Error deleting file at url %@", url); 159 | } 160 | 161 | -(void)trySaveobjectAtURL:(NSURL *)URL associatedObjects:(NSArray *)objects withBlock:(void(^)(PFFile *file, NSError *error))block progressBlock:(void(^)(int percentDone))progressBlock 162 | { 163 | PFFile *file = [PFFile fileWithData:[NSData dataWithContentsOfURL:URL]]; 164 | 165 | [self addUrlToQueue:URL withobjects:objects]; 166 | 167 | [file saveInBackgroundWithBlock:^(BOOL success, NSError *error) 168 | { 169 | if(!error) 170 | { 171 | [PFFileManager deleteFileAtURL:URL]; 172 | [self removeURLFromQueue:URL withobjects:objects]; 173 | block(file, nil); 174 | } 175 | else{ 176 | block(nil, error); 177 | } 178 | } 179 | progressBlock:progressBlock]; 180 | } 181 | -(void)persistPendingUploads 182 | { 183 | [pendingUploads writeToFile:[PFFileManager pendingFile] atomically:YES]; 184 | } 185 | 186 | -(void)addUrlToQueue:(NSURL *)URL withobjects:(NSArray *)objects 187 | { 188 | NSLog(@"[PFFileManager] addUrlToQueue %@ with %lu objects", URL, (unsigned long)objects.count); 189 | [pendingUploads addObject:[URL lastPathComponent]]; 190 | [self persistPendingUploads]; 191 | [PFObject pinAllInBackground:objects withName:[URL lastPathComponent]]; 192 | } 193 | -(void)removeURLFromQueue:(NSURL *)URL withobjects:(NSArray *)objects 194 | { 195 | NSLog(@"[PFFileManager] removeURLFromQueue %@ with %lu objects associated", URL, (unsigned long)objects.count); 196 | [pendingUploads removeObject:[URL lastPathComponent]]; 197 | [self persistPendingUploads]; 198 | [PFObject unpinAllInBackground:objects withName:[URL lastPathComponent]]; 199 | } 200 | 201 | @end 202 | -------------------------------------------------------------------------------- /PFFileSaveEventuallyExample.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 5FC3EE6157B69A2631C437A0 /* libPods-PFFileSaveEventuallyExample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A1F895086F71C615536AC2E /* libPods-PFFileSaveEventuallyExample.a */; }; 11 | A0B0DEAD1BA165A200492DEC /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = A0B0DEAC1BA165A200492DEC /* main.m */; }; 12 | A0B0DEB01BA165A200492DEC /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = A0B0DEAF1BA165A200492DEC /* AppDelegate.m */; }; 13 | A0B0DEB81BA165A200492DEC /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A0B0DEB71BA165A200492DEC /* Images.xcassets */; }; 14 | A0B0DEBB1BA165A200492DEC /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = A0B0DEB91BA165A200492DEC /* LaunchScreen.xib */; }; 15 | A0B0DED21BA167BC00492DEC /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A0B0DED11BA167BC00492DEC /* Main.storyboard */; }; 16 | A0B0DEDD1BA16D2D00492DEC /* PFFileEventuallySaver.m in Sources */ = {isa = PBXBuildFile; fileRef = A0B0DEDC1BA16D2D00492DEC /* PFFileEventuallySaver.m */; }; 17 | A0B0DEE11BA16F1800492DEC /* nyancat.jpg in Resources */ = {isa = PBXBuildFile; fileRef = A0B0DEE01BA16F1800492DEC /* nyancat.jpg */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXFileReference section */ 21 | 67EB8D59D179FCCA2C45E7FE /* Pods-PFFileSaveEventuallyExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PFFileSaveEventuallyExample.debug.xcconfig"; path = "Pods/Target Support Files/Pods-PFFileSaveEventuallyExample/Pods-PFFileSaveEventuallyExample.debug.xcconfig"; sourceTree = ""; }; 22 | 9A1F895086F71C615536AC2E /* libPods-PFFileSaveEventuallyExample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-PFFileSaveEventuallyExample.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 23 | 9F4E95863643226063B5C386 /* Pods-PFFileSaveEventuallyExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-PFFileSaveEventuallyExample.release.xcconfig"; path = "Pods/Target Support Files/Pods-PFFileSaveEventuallyExample/Pods-PFFileSaveEventuallyExample.release.xcconfig"; sourceTree = ""; }; 24 | A0B0DEA71BA165A200492DEC /* PFFileSaveEventuallyExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = PFFileSaveEventuallyExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 25 | A0B0DEAB1BA165A200492DEC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 26 | A0B0DEAC1BA165A200492DEC /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 27 | A0B0DEAE1BA165A200492DEC /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 28 | A0B0DEAF1BA165A200492DEC /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 29 | A0B0DEB71BA165A200492DEC /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 30 | A0B0DEBA1BA165A200492DEC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 31 | A0B0DED11BA167BC00492DEC /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = Main.storyboard; path = PFFileSaveEventuallyExample/Main.storyboard; sourceTree = ""; }; 32 | A0B0DEDB1BA16D2D00492DEC /* PFFileEventuallySaver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PFFileEventuallySaver.h; sourceTree = ""; }; 33 | A0B0DEDC1BA16D2D00492DEC /* PFFileEventuallySaver.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PFFileEventuallySaver.m; sourceTree = ""; }; 34 | A0B0DEE01BA16F1800492DEC /* nyancat.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = nyancat.jpg; sourceTree = ""; }; 35 | /* End PBXFileReference section */ 36 | 37 | /* Begin PBXFrameworksBuildPhase section */ 38 | A0B0DEA41BA165A200492DEC /* Frameworks */ = { 39 | isa = PBXFrameworksBuildPhase; 40 | buildActionMask = 2147483647; 41 | files = ( 42 | 5FC3EE6157B69A2631C437A0 /* libPods-PFFileSaveEventuallyExample.a in Frameworks */, 43 | ); 44 | runOnlyForDeploymentPostprocessing = 0; 45 | }; 46 | /* End PBXFrameworksBuildPhase section */ 47 | 48 | /* Begin PBXGroup section */ 49 | 658B0258E65352F7CDAA5B7C /* Pods */ = { 50 | isa = PBXGroup; 51 | children = ( 52 | 67EB8D59D179FCCA2C45E7FE /* Pods-PFFileSaveEventuallyExample.debug.xcconfig */, 53 | 9F4E95863643226063B5C386 /* Pods-PFFileSaveEventuallyExample.release.xcconfig */, 54 | ); 55 | name = Pods; 56 | sourceTree = ""; 57 | }; 58 | 67FB92E19B4923BF2102BD3B /* Frameworks */ = { 59 | isa = PBXGroup; 60 | children = ( 61 | 9A1F895086F71C615536AC2E /* libPods-PFFileSaveEventuallyExample.a */, 62 | ); 63 | name = Frameworks; 64 | sourceTree = ""; 65 | }; 66 | A0B0DE9E1BA165A200492DEC = { 67 | isa = PBXGroup; 68 | children = ( 69 | A0B0DED11BA167BC00492DEC /* Main.storyboard */, 70 | A0B0DEA91BA165A200492DEC /* PFFileSaveEventuallyExample */, 71 | A0B0DEA81BA165A200492DEC /* Products */, 72 | 658B0258E65352F7CDAA5B7C /* Pods */, 73 | 67FB92E19B4923BF2102BD3B /* Frameworks */, 74 | ); 75 | sourceTree = ""; 76 | }; 77 | A0B0DEA81BA165A200492DEC /* Products */ = { 78 | isa = PBXGroup; 79 | children = ( 80 | A0B0DEA71BA165A200492DEC /* PFFileSaveEventuallyExample.app */, 81 | ); 82 | name = Products; 83 | sourceTree = ""; 84 | }; 85 | A0B0DEA91BA165A200492DEC /* PFFileSaveEventuallyExample */ = { 86 | isa = PBXGroup; 87 | children = ( 88 | A0B0DEDA1BA16D2D00492DEC /* PFFileEventuallySaver */, 89 | A0B0DEAE1BA165A200492DEC /* AppDelegate.h */, 90 | A0B0DEAF1BA165A200492DEC /* AppDelegate.m */, 91 | A0B0DEE01BA16F1800492DEC /* nyancat.jpg */, 92 | A0B0DEB71BA165A200492DEC /* Images.xcassets */, 93 | A0B0DEB91BA165A200492DEC /* LaunchScreen.xib */, 94 | A0B0DEAA1BA165A200492DEC /* Supporting Files */, 95 | ); 96 | path = PFFileSaveEventuallyExample; 97 | sourceTree = ""; 98 | }; 99 | A0B0DEAA1BA165A200492DEC /* Supporting Files */ = { 100 | isa = PBXGroup; 101 | children = ( 102 | A0B0DEAB1BA165A200492DEC /* Info.plist */, 103 | A0B0DEAC1BA165A200492DEC /* main.m */, 104 | ); 105 | name = "Supporting Files"; 106 | sourceTree = ""; 107 | }; 108 | A0B0DEDA1BA16D2D00492DEC /* PFFileEventuallySaver */ = { 109 | isa = PBXGroup; 110 | children = ( 111 | A0B0DEDB1BA16D2D00492DEC /* PFFileEventuallySaver.h */, 112 | A0B0DEDC1BA16D2D00492DEC /* PFFileEventuallySaver.m */, 113 | ); 114 | path = PFFileEventuallySaver; 115 | sourceTree = SOURCE_ROOT; 116 | }; 117 | /* End PBXGroup section */ 118 | 119 | /* Begin PBXNativeTarget section */ 120 | A0B0DEA61BA165A200492DEC /* PFFileSaveEventuallyExample */ = { 121 | isa = PBXNativeTarget; 122 | buildConfigurationList = A0B0DECA1BA165A200492DEC /* Build configuration list for PBXNativeTarget "PFFileSaveEventuallyExample" */; 123 | buildPhases = ( 124 | F1204DFAE064E08B8AD6FA0D /* Check Pods Manifest.lock */, 125 | A0B0DEA31BA165A200492DEC /* Sources */, 126 | A0B0DEA41BA165A200492DEC /* Frameworks */, 127 | A0B0DEA51BA165A200492DEC /* Resources */, 128 | 74176A962ED7B97150C7C05F /* Copy Pods Resources */, 129 | ); 130 | buildRules = ( 131 | ); 132 | dependencies = ( 133 | ); 134 | name = PFFileSaveEventuallyExample; 135 | productName = PFFileSaveEventuallyExample; 136 | productReference = A0B0DEA71BA165A200492DEC /* PFFileSaveEventuallyExample.app */; 137 | productType = "com.apple.product-type.application"; 138 | }; 139 | /* End PBXNativeTarget section */ 140 | 141 | /* Begin PBXProject section */ 142 | A0B0DE9F1BA165A200492DEC /* Project object */ = { 143 | isa = PBXProject; 144 | attributes = { 145 | LastUpgradeCheck = 0640; 146 | ORGANIZATIONNAME = "Thibaud David"; 147 | TargetAttributes = { 148 | A0B0DEA61BA165A200492DEC = { 149 | CreatedOnToolsVersion = 6.4; 150 | }; 151 | }; 152 | }; 153 | buildConfigurationList = A0B0DEA21BA165A200492DEC /* Build configuration list for PBXProject "PFFileSaveEventuallyExample" */; 154 | compatibilityVersion = "Xcode 3.2"; 155 | developmentRegion = English; 156 | hasScannedForEncodings = 0; 157 | knownRegions = ( 158 | en, 159 | Base, 160 | ); 161 | mainGroup = A0B0DE9E1BA165A200492DEC; 162 | productRefGroup = A0B0DEA81BA165A200492DEC /* Products */; 163 | projectDirPath = ""; 164 | projectRoot = ""; 165 | targets = ( 166 | A0B0DEA61BA165A200492DEC /* PFFileSaveEventuallyExample */, 167 | ); 168 | }; 169 | /* End PBXProject section */ 170 | 171 | /* Begin PBXResourcesBuildPhase section */ 172 | A0B0DEA51BA165A200492DEC /* Resources */ = { 173 | isa = PBXResourcesBuildPhase; 174 | buildActionMask = 2147483647; 175 | files = ( 176 | A0B0DEE11BA16F1800492DEC /* nyancat.jpg in Resources */, 177 | A0B0DED21BA167BC00492DEC /* Main.storyboard in Resources */, 178 | A0B0DEBB1BA165A200492DEC /* LaunchScreen.xib in Resources */, 179 | A0B0DEB81BA165A200492DEC /* Images.xcassets in Resources */, 180 | ); 181 | runOnlyForDeploymentPostprocessing = 0; 182 | }; 183 | /* End PBXResourcesBuildPhase section */ 184 | 185 | /* Begin PBXShellScriptBuildPhase section */ 186 | 74176A962ED7B97150C7C05F /* Copy Pods Resources */ = { 187 | isa = PBXShellScriptBuildPhase; 188 | buildActionMask = 2147483647; 189 | files = ( 190 | ); 191 | inputPaths = ( 192 | ); 193 | name = "Copy Pods Resources"; 194 | outputPaths = ( 195 | ); 196 | runOnlyForDeploymentPostprocessing = 0; 197 | shellPath = /bin/sh; 198 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-PFFileSaveEventuallyExample/Pods-PFFileSaveEventuallyExample-resources.sh\"\n"; 199 | showEnvVarsInLog = 0; 200 | }; 201 | F1204DFAE064E08B8AD6FA0D /* Check Pods Manifest.lock */ = { 202 | isa = PBXShellScriptBuildPhase; 203 | buildActionMask = 2147483647; 204 | files = ( 205 | ); 206 | inputPaths = ( 207 | ); 208 | name = "Check Pods Manifest.lock"; 209 | outputPaths = ( 210 | ); 211 | runOnlyForDeploymentPostprocessing = 0; 212 | shellPath = /bin/sh; 213 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; 214 | showEnvVarsInLog = 0; 215 | }; 216 | /* End PBXShellScriptBuildPhase section */ 217 | 218 | /* Begin PBXSourcesBuildPhase section */ 219 | A0B0DEA31BA165A200492DEC /* Sources */ = { 220 | isa = PBXSourcesBuildPhase; 221 | buildActionMask = 2147483647; 222 | files = ( 223 | A0B0DEB01BA165A200492DEC /* AppDelegate.m in Sources */, 224 | A0B0DEDD1BA16D2D00492DEC /* PFFileEventuallySaver.m in Sources */, 225 | A0B0DEAD1BA165A200492DEC /* main.m in Sources */, 226 | ); 227 | runOnlyForDeploymentPostprocessing = 0; 228 | }; 229 | /* End PBXSourcesBuildPhase section */ 230 | 231 | /* Begin PBXVariantGroup section */ 232 | A0B0DEB91BA165A200492DEC /* LaunchScreen.xib */ = { 233 | isa = PBXVariantGroup; 234 | children = ( 235 | A0B0DEBA1BA165A200492DEC /* Base */, 236 | ); 237 | name = LaunchScreen.xib; 238 | sourceTree = ""; 239 | }; 240 | /* End PBXVariantGroup section */ 241 | 242 | /* Begin XCBuildConfiguration section */ 243 | A0B0DEC81BA165A200492DEC /* Debug */ = { 244 | isa = XCBuildConfiguration; 245 | buildSettings = { 246 | ALWAYS_SEARCH_USER_PATHS = NO; 247 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 248 | CLANG_CXX_LIBRARY = "libc++"; 249 | CLANG_ENABLE_MODULES = YES; 250 | CLANG_ENABLE_OBJC_ARC = YES; 251 | CLANG_WARN_BOOL_CONVERSION = YES; 252 | CLANG_WARN_CONSTANT_CONVERSION = YES; 253 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 254 | CLANG_WARN_EMPTY_BODY = YES; 255 | CLANG_WARN_ENUM_CONVERSION = YES; 256 | CLANG_WARN_INT_CONVERSION = YES; 257 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 258 | CLANG_WARN_UNREACHABLE_CODE = YES; 259 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 260 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 261 | COPY_PHASE_STRIP = NO; 262 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 263 | ENABLE_STRICT_OBJC_MSGSEND = YES; 264 | GCC_C_LANGUAGE_STANDARD = gnu99; 265 | GCC_DYNAMIC_NO_PIC = NO; 266 | GCC_NO_COMMON_BLOCKS = YES; 267 | GCC_OPTIMIZATION_LEVEL = 0; 268 | GCC_PREPROCESSOR_DEFINITIONS = ( 269 | "DEBUG=1", 270 | "$(inherited)", 271 | ); 272 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 273 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 274 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 275 | GCC_WARN_UNDECLARED_SELECTOR = YES; 276 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 277 | GCC_WARN_UNUSED_FUNCTION = YES; 278 | GCC_WARN_UNUSED_VARIABLE = YES; 279 | IPHONEOS_DEPLOYMENT_TARGET = 8.4; 280 | MTL_ENABLE_DEBUG_INFO = YES; 281 | ONLY_ACTIVE_ARCH = YES; 282 | SDKROOT = iphoneos; 283 | }; 284 | name = Debug; 285 | }; 286 | A0B0DEC91BA165A200492DEC /* Release */ = { 287 | isa = XCBuildConfiguration; 288 | buildSettings = { 289 | ALWAYS_SEARCH_USER_PATHS = NO; 290 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 291 | CLANG_CXX_LIBRARY = "libc++"; 292 | CLANG_ENABLE_MODULES = YES; 293 | CLANG_ENABLE_OBJC_ARC = YES; 294 | CLANG_WARN_BOOL_CONVERSION = YES; 295 | CLANG_WARN_CONSTANT_CONVERSION = YES; 296 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 297 | CLANG_WARN_EMPTY_BODY = YES; 298 | CLANG_WARN_ENUM_CONVERSION = YES; 299 | CLANG_WARN_INT_CONVERSION = YES; 300 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 301 | CLANG_WARN_UNREACHABLE_CODE = YES; 302 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 303 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 304 | COPY_PHASE_STRIP = NO; 305 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 306 | ENABLE_NS_ASSERTIONS = NO; 307 | ENABLE_STRICT_OBJC_MSGSEND = YES; 308 | GCC_C_LANGUAGE_STANDARD = gnu99; 309 | GCC_NO_COMMON_BLOCKS = YES; 310 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 311 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 312 | GCC_WARN_UNDECLARED_SELECTOR = YES; 313 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 314 | GCC_WARN_UNUSED_FUNCTION = YES; 315 | GCC_WARN_UNUSED_VARIABLE = YES; 316 | IPHONEOS_DEPLOYMENT_TARGET = 8.4; 317 | MTL_ENABLE_DEBUG_INFO = NO; 318 | SDKROOT = iphoneos; 319 | VALIDATE_PRODUCT = YES; 320 | }; 321 | name = Release; 322 | }; 323 | A0B0DECB1BA165A200492DEC /* Debug */ = { 324 | isa = XCBuildConfiguration; 325 | baseConfigurationReference = 67EB8D59D179FCCA2C45E7FE /* Pods-PFFileSaveEventuallyExample.debug.xcconfig */; 326 | buildSettings = { 327 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 328 | INFOPLIST_FILE = PFFileSaveEventuallyExample/Info.plist; 329 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 330 | PRODUCT_NAME = "$(TARGET_NAME)"; 331 | }; 332 | name = Debug; 333 | }; 334 | A0B0DECC1BA165A200492DEC /* Release */ = { 335 | isa = XCBuildConfiguration; 336 | baseConfigurationReference = 9F4E95863643226063B5C386 /* Pods-PFFileSaveEventuallyExample.release.xcconfig */; 337 | buildSettings = { 338 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 339 | INFOPLIST_FILE = PFFileSaveEventuallyExample/Info.plist; 340 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 341 | PRODUCT_NAME = "$(TARGET_NAME)"; 342 | }; 343 | name = Release; 344 | }; 345 | /* End XCBuildConfiguration section */ 346 | 347 | /* Begin XCConfigurationList section */ 348 | A0B0DEA21BA165A200492DEC /* Build configuration list for PBXProject "PFFileSaveEventuallyExample" */ = { 349 | isa = XCConfigurationList; 350 | buildConfigurations = ( 351 | A0B0DEC81BA165A200492DEC /* Debug */, 352 | A0B0DEC91BA165A200492DEC /* Release */, 353 | ); 354 | defaultConfigurationIsVisible = 0; 355 | defaultConfigurationName = Release; 356 | }; 357 | A0B0DECA1BA165A200492DEC /* Build configuration list for PBXNativeTarget "PFFileSaveEventuallyExample" */ = { 358 | isa = XCConfigurationList; 359 | buildConfigurations = ( 360 | A0B0DECB1BA165A200492DEC /* Debug */, 361 | A0B0DECC1BA165A200492DEC /* Release */, 362 | ); 363 | defaultConfigurationIsVisible = 0; 364 | defaultConfigurationName = Release; 365 | }; 366 | /* End XCConfigurationList section */ 367 | }; 368 | rootObject = A0B0DE9F1BA165A200492DEC /* Project object */; 369 | } 370 | --------------------------------------------------------------------------------