Search code examples
iosobjective-ciphoneswiftipad

Save images in NSUserDefaults?


Is it possible to save images into NSUserDefaults as an object and then retrieve for further use?


Solution

  • ATTENTION! IF YOU'RE WORKING UNDER iOS8/XCODE6 SEE MY UPDATE BELOW

    For those who still looking for answer here is code of "advisable" way to save image in NSUserDefaults. You SHOULD NOT save image data directly into NSUserDefaults!

    Write data:

    // Get image data. Here you can use UIImagePNGRepresentation if you need transparency
    NSData *imageData = UIImageJPEGRepresentation(image, 1);
    
    // Get image path in user's folder and store file with name image_CurrentTimestamp.jpg (see documentsPathForFileName below)
    NSString *imagePath = [self documentsPathForFileName:[NSString stringWithFormat:@"image_%f.jpg", [NSDate timeIntervalSinceReferenceDate]]];
    
    // Write image data to user's folder
    [imageData writeToFile:imagePath atomically:YES];
    
    // Store path in NSUserDefaults
    [[NSUserDefaults standardUserDefaults] setObject:imagePath forKey:kPLDefaultsAvatarUrl];
    
    // Sync user defaults
    [[NSUserDefaults standardUserDefaults] synchronize];
    

    Read data:

    NSString *imagePath = [[NSUserDefaults standardUserDefaults] objectForKey:kPLDefaultsAvatarUrl];
    if (imagePath) {
        self.avatarImageView.image = [UIImage imageWithData:[NSData dataWithContentsOfFile:imagePath]];
    }
    

    documentsPathForFileName:

    - (NSString *)documentsPathForFileName:(NSString *)name {
        NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
        NSString *documentsPath = [paths objectAtIndex:0];
    
        return [documentsPath stringByAppendingPathComponent:name];
    }
    

    For iOS8/XCODE6 As tmr and DevC mentioned in comments below there is a problem with xcode6/ios8. The difference between xcode5 and xcode 6 installation process is that xcode6 changes apps UUID after each run in xcode (see hightlighted part in path: /var/mobile/Containers/Data/Application/B0D49CF5-8FBE-4F14-87AE-FA8C16A678B1/Documents/image.jpg).

    So there are 2 workarounds:

    1. Skip that problem, as once app installed on real device it's never changes UUID (in fact it does, but it is new app)
    2. Save relative path to required folder (in our case to app's root)

    Here is swift version of code as a bonus (with 2nd approach):

    Write data:

    let imageData = UIImageJPEGRepresentation(image, 1)
    let relativePath = "image_\(NSDate.timeIntervalSinceReferenceDate()).jpg"
    let path = self.documentsPathForFileName(relativePath)
    imageData.writeToFile(path, atomically: true)
    NSUserDefaults.standardUserDefaults().setObject(relativePath, forKey: "path")
    NSUserDefaults.standardUserDefaults().synchronize()
    

    Read data:

    let possibleOldImagePath = NSUserDefaults.standardUserDefaults().objectForKey("path") as String?
    if let oldImagePath = possibleOldImagePath {
        let oldFullPath = self.documentsPathForFileName(oldImagePath)
        let oldImageData = NSData(contentsOfFile: oldFullPath)
        // here is your saved image:
        let oldImage = UIImage(data: oldImageData)
    }
    

    documentsPathForFileName:

    func documentsPathForFileName(name: String) -> String {
        let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true);
        let path = paths[0] as String;
        let fullPath = path.stringByAppendingPathComponent(name)
    
        return fullPath
    }