samvermette
samvermette

Reputation: 40437

Use NSURLIsExcludedFromBackupKey without crashing on iOS 5.0

The check for availability seems to be working fine but I can't seem to set the NSURLIsExcludedFromBackupKey key without getting this crash on launch:

dyld: Symbol not found: _NSURLIsExcludedFromBackupKey Referenced from: /Users/sam/Library/Application Support/iPhone Simulator/5.0/Applications/B0872A19-3230-481C-B5CE-D4BDE264FBDF/Transit.app/Transit Expected in: /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator5.0.sdk/System/Library/Frameworks/Foundation.framework/Foundation in /Users/sam/Library/Application Support/iPhone Simulator/5.0/Applications/B0872A19-3230-481C-B5CE-D4BDE264FBDF/Transit.app/Transit

Here's my method:

- (BOOL)addSkipBackupAttributeToItemAtURL:(NSURL *)URL {    
    if (&NSURLIsExcludedFromBackupKey == nil)
        return NO;

    NSError *error;
    [URL setResourceValue:[NSNumber numberWithBool:YES] forKey:NSURLIsExcludedFromBackupKey error:&error];
    return (error != nil);
}

Crash goes away if I comment out this line:

[URL setResourceValue:[NSNumber numberWithBool:YES] forKey:NSURLIsExcludedFromBackupKey error:&error];

Do I have to weak-link Foundation?

EDIT: not sure if it makes a difference, but this method is put inside an NSFileManager category.

Upvotes: 13

Views: 14973

Answers (6)

Matjan
Matjan

Reputation: 3607

Depending on what you're doing, this quick fix may work for you. It did for me.

Add the CoreFoundation framework to your project and mark it as OPTIONAL (not required).

Upvotes: 2

samvermette
samvermette

Reputation: 40437

This seems to be a bug with the iPhone 5.0 Simulator. I tried running the code on a 5.0 device and no crash. Reported this bug as rdar://11017158.

EDIT: this has been fixed in Xcode 4.5 DP2 (not sure if it is in 4.4).

Upvotes: 5

Hal
Hal

Reputation: 1145

I had this same thing after updating ShareKit and redoing a project to target iOS 5.1 I would either get an error at compilation or at linking related to NSURLIsExcludedFromBackupKey. The ShareKit people seem to recommend that you can solve the problem by making sure your project links with the CoreFoundation framework and set it to "Optional" rather than "Required". However this didn't work for me.

Eventually i got around it by using the preprocessor:

- (BOOL)addSkipBackupAttributeToItemAtURL:(NSURL *)URL
{
    #ifndef NSURLIsExcludedFromBackupKey                   
    // iOS <= 5.0.1.
    const char* filePath = [[URL path] fileSystemRepresentation];
    const char* attrName = "com.apple.MobileBackup";
    u_int8_t attrValue = 1;    
    int result = setxattr(filePath, attrName, &attrValue, sizeof(attrValue), 0, 0);
    return result == 0;
    #else                                                                       
    // iOS >= 5.1
    // First try and remove the extended attribute if it is present
    int result = getxattr(filePath, attrName, NULL, sizeof(u_int8_t), 0, 0);
    if (result != -1) {
        // The attribute exists, we need to remove it
        int removeResult = removexattr(filePath, attrName, 0);
        if (removeResult == 0) {
            NSLog(@"Removed extended attribute on file %@", URL);
        }
    }
    return [URL setResourceValue:[NSNumber numberWithBool:YES] forKey:NSURLIsExcludedFromBackupKey error:nil];
    #endif
}

Upvotes: 0

HungryTenor
HungryTenor

Reputation: 51

Add this line to force the symbol to be a weak import:

extern NSString * const NSURLIsExcludedFromBackupKey __attribute__((weak_import));

Upvotes: 4

tim
tim

Reputation: 1682

Here's code for iOS <= 5.0.1 and >= 5.1 and includes the migration technique that @Cocoanetics mentioned.

- (BOOL)addSkipBackupAttributeToItemAtURL:(NSURL *)URL
{
    const char* filePath = [[URL path] fileSystemRepresentation];
    const char* attrName = "com.apple.MobileBackup";
    if (&NSURLIsExcludedFromBackupKey == nil) {
        // iOS 5.0.1 and lower
        u_int8_t attrValue = 1;
        int result = setxattr(filePath, attrName, &attrValue, sizeof(attrValue), 0, 0);
        return result == 0;
    } else {
        // First try and remove the extended attribute if it is present
        int result = getxattr(filePath, attrName, NULL, sizeof(u_int8_t), 0, 0);
        if (result != -1) {
            // The attribute exists, we need to remove it
            int removeResult = removexattr(filePath, attrName, 0);
            if (removeResult == 0) {
                NSLog(@"Removed extended attribute on file %@", URL);
            }
        }

        // Set the new key
        return [URL setResourceValue:[NSNumber numberWithBool:YES] forKey:NSURLIsExcludedFromBackupKey error:nil];
    }
}

Upvotes: 19

Cocoanetics
Cocoanetics

Reputation: 8245

The problem is that this key is only present on 5.1 and above. For 5.0.1 you need to set the extended file attribute instead. The only backwards-compatible method would be to find out the NSString value of this key and set this instead below 5.1.

Upvotes: 1

Related Questions