Exclude from iCloud backup: NSURLIsExcludedFromBac

2019-03-16 16:50发布

My app had been rejected the 2nd times and I lost 3 weeks :(

The first submit, I excluded ONLY DIRECTORIES from being backed-up in iCloud. Apple rejected...

The second submit, I excluded DIRECTORIES & PICTURES downloaded from being backed-up in iCloud. Apple again rejected... Apple also complaint that I have no "Restore" feature for my In-App purchase, while in fact, I do have a "Restore" button and it worked when I tested it.

I've done as Apple had suggested by excluding the file from being backedup using NSURLIsExcludedFromBackupKey. There was an interesting comment made by Macmade's on stackoverflow here:

sometimes Apple reviewers think your data can be re-generated, when it's not. Then you'll have to explain why the data has to be backed-up

How often do the reviewer misunderstood and we have to explain to them that the content is required offline & not re-generateable?

Here is the code I used to exclude my files & directories from iCloud. Do you spot any problems?

- (BOOL)addSkipBackupAttributeToItemAtURL:(NSURL *)URL
{
    // There's a chance the download failed, but don't assert here
    //assert([[NSFileManager defaultManager] fileExistsAtPath: [URL path]]);

    NSError *error = nil;
    BOOL success = [URL setResourceValue:[NSNumber numberWithBool:YES]
                                  forKey:NSURLIsExcludedFromBackupKey
                                   error: &error];
    if(!success){
        NSLog(@"Error excluding %@ from backup %@", [URL lastPathComponent], error);
    }
    return success;
}

//Download picture from Google and exclude it from being backed-up in iCloud
- (void)downloadFetcher:(GTMHTTPFetcher *)fetcher
       finishedWithData:(NSData *)data
                  error:(NSError *)error
{

    if (error == nil) {

        // successfully retrieved this photo's data; save it to disk
        GDataEntryPhoto *photoEntry = [fetcher propertyForKey:@"photo entry"];

        // Create album directory if it doesn't already exist
        NSString *path = [self findOrCreateApplicationSupportSubPath:[photoEntry albumTitle]];
        path = [path stringByAppendingPathComponent:[[photoEntry title] stringValue]];

        if (path != nil) {

            // Write to disk
            BOOL didSave = [data writeToFile:path
                                     options:NSDataWritingAtomic
                                       error:&error];
            if (didSave) {

                // Exclude file from being backed up in iCloud
                NSURL *url = [NSURL fileURLWithPath:path];
                BOOL excludeBackupResult = [self addSkipBackupAttributeToItemAtURL:url];

                if (excludeBackupResult == NO) {
                    NSLog(@"Error excluding FILE from iCloud: %@", path);
                }

                // Update the download progress bar
                _downloadedFileCounter = _downloadedFileCounter + 1;
                float progress = _downloadedFileCounter / kMaleWireframeImagesTotal;
                [self updateProgress:progress];

                // The download completed. -2 just incase a package is lost, but let the user move on...
                if (_downloadedFileCounter >= _downloadableFilesTotal -2) {
                    [_panel6 downloadCompleted];
                }

            } else {
                // error saving file.  Perhaps out of space?  Write permissions error?
                NSLog(@"Save anatomy picture failed: %@", error.localizedDescription);
            }

        } else {
            NSLog(@"downloadFetcher: Cannot create directory");
        }
    } else {
        NSLog(@"downloadFetcher failed: %@", error);
    }
}

//Create directory and exclude it from being backed-up in iCloud
-(NSString*)findOrCreateApplicationSupportSubPath:(NSString*)subPath
{

    NSString *resolvedPath;
    NSArray *appSupportDir = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);

    if ([appSupportDir count] != 0) {

        resolvedPath = [appSupportDir objectAtIndex:0];

        // Append the name of this application
        NSString *executableName = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleExecutable"];
        resolvedPath = [resolvedPath stringByAppendingPathComponent:executableName];
        resolvedPath = [resolvedPath stringByAppendingPathComponent:subPath];

        NSFileManager *manager = [NSFileManager defaultManager];
        if (![manager fileExistsAtPath:resolvedPath]) {

            // Path doesn't exist, creates it
            NSError *error;
            BOOL successful = [manager createDirectoryAtPath:resolvedPath withIntermediateDirectories:YES attributes:nil error:&error];

            if(!successful) {
                NSLog(@"ERROR creating APP Support Sub-Directory: %@", error.localizedDescription);
                return nil;
            } else {

                // Exclude path from backing-up in iCloud
                NSURL *url = [NSURL fileURLWithPath:resolvedPath];
                BOOL excludeBackupResult = [self addSkipBackupAttributeToItemAtURL:url];

                if(!excludeBackupResult){
                    NSLog(@"Error excluding DIRECTORY from iCloud backup. This is a violation to their guideline.");
                    return  nil;
                }
            }
        }

    } else {
        NSLog(@"No Application Support Path available");
        return  nil;
    }

    return resolvedPath;
}

标签: ios icloud
2条回答
我只想做你的唯一
2楼-- · 2019-03-16 16:55

I think the trick is to add the NSURLIsExcludedFromBackupKey OR make sure the directory is outside the documents directory. I did this by moving my documents to the Library/Application Support folder (since it didn't make sense in the /tmp or /Caches folders):

- (void)createNewRefFolder
{
    NSError *error;

    // store in /Library/Application Support/BUNDLE_IDENTIFIER/Reference
    // make sure Application Support folder exists
    NSURL *applicationSupportDirectory = [[NSFileManager defaultManager] URLForDirectory:NSApplicationSupportDirectory
                                                                                inDomain:NSUserDomainMask
                                                                       appropriateForURL:nil
                                                                                  create:YES
                                                                                   error:&error];

    if (error) {
        NSLog(@"KCDM: Could not create application support directory. %@", error);
    }

    NSURL *referenceFolder = [applicationSupportDirectory URLByAppendingPathComponent:@"Reference" isDirectory:YES];
    if (![[NSFileManager defaultManager] createDirectoryAtPath:[referenceFolder path]
                                   withIntermediateDirectories:YES
                                                    attributes:nil
                                                         error:&error]) {
        NSLog(@"KCDM: Error creating Reference folder: %@ ...", error);
    }

    BOOL success = [referenceFolder setResourceValue:@YES forKey: NSURLIsExcludedFromBackupKey error: &error];
    if(!success){
        NSLog(@"KCDM: Error excluding %@ from backup %@", referenceFolder, error);
    }
}
查看更多
唯我独甜
3楼-- · 2019-03-16 16:57

Little UPDATE from previous answers.

You have to check the existence of the file. Otherwise, you will get this error,

Error excluding [FileName] from backup: 
Error Domain=NSCocoaErrorDomain Code=4 "The file “[FileName]” doesn’t exist." 
 ... 

I am not sure if we should check for the value is already updated or not. e.g. if the API reset already set value or not. If it tries to update the file system again for a set value that is more time consuming, I guess.

Updated method...

+ (BOOL)addSkipBackupAttributeToURLAtPath:(NSURL *)url
{
    if (!url) return NO;
    if (![[NSFileManager defaultManager] fileExistsAtPath:url.path]) return NO;

    NSError *error = nil;
    NSNumber *value = nil;
    BOOL success = [url getResourceValue:&value forKey:NSURLIsExcludedFromBackupKey error:&error];
    if (value.boolValue == YES) return YES;

    success = [url setResourceValue:[NSNumber numberWithBool:YES]
                                  forKey:NSURLIsExcludedFromBackupKey error:&error];
    if(!success){
        NSLog(@"Error excluding %@ from backup: %@", [url lastPathComponent], error);
    }
    return success;
}

+ (BOOL)addSkipBackupAttributeToFileAtPath:(NSString *)path
{
    if (!path) return NO;
    return [self addSkipBackupAttributeToURLAtPath:[NSURL fileURLWithPath:path]];
}
查看更多
登录 后发表回答