rocket101
rocket101

Reputation: 7537

Persist Array of Images Swift

I want to be able to save an array, cardImages, that contains UIImages via SwiftyUserDefaults.

Desired Behavior

Here is the exact desired behavior:

Save an array of UIImages to NSUserDefaults via the SwiftyUserDefault library

Retrieve the images later

Code This is stripped down to very little code

    var newPhotoKey = DefaultsKey<NSArray>("image")//Setting up the SwiftyUserDefaults Persisted Array

        cardImages = [(UIImage(named: "MyImageName.jpg")!)] //This array contains the default value, and will fill up with more
        Defaults[theKeyForStoringThisArray] = cardImages //This is the persisted array in which the array full of images should be stored. WHERE THE ERROR HAPPENS

var arrayToRetreiveWith = Defaults[theKeyForStoringThisArray] as! [UIImage] //To Retreive

Error

I get the following error:

Attempt to set a non-property-list object ( ", {300, 300}" ) as an NSUserDefaults/CFPreferences value for key image *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attempt to insert non-property list object ( ", {300, 300}" ) for key image'

Thanks!

Upvotes: 2

Views: 675

Answers (2)

Lukas
Lukas

Reputation: 3433

The error message is clear actually. UIImage is not a propertylist so you need to change it to a row data first. I'll put example below but FYI saving big data like images using NSUserDefaults is really not recommended. I'd use NSFileManager and put it in the user documents directory. anyway

var newPhotoKey = DefaultsKey<NSArray>("image")
cardImages = [(UIImage(named: "MyImageName.jpg")!)] 
var cardImagesRowdataArray: NSData = []
for image in cardImages {
    let imageData = UIImageJPEGRepresentation(image, 1.0)
    cardImagesRowdataArray.append(imageData)
}
Defaults[theKeyForStoringThisArray] = cardImagesRowdataArray 

var arrayToRetreiveWith = Defaults[theKeyForStoringThisArray] as! [NSData]
// here you can use UIImage(data: data) to get it back

If you don't insist on using SwiftyUserDefaults, you can save it in the user documents directory, here is how to do it

func saveImage(image: UIImage){
    if let imageData = UIImageJPEGRepresentation(image, 1.0) {
         let manager = NSFileManager()
         if let docUrl = manager.URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first{
             let uniqueName = NSDate.timeIntervalSinceReferenceDate()
             let url = docUrl.URLByAppendingPathComponent("\(uniqueName).jpg")
             imageData.writeToURL(url, atomically: true)
         }
     }
 }

Upvotes: 3

rob mayoff
rob mayoff

Reputation: 386038

The value of a user default must be a property list. A property list is

  • a string (String or NSString),
  • an NSData,
  • a date (NSDate),
  • a number (NSNumber),
  • a boolean (also NSNumber),
  • an array of property lists,
  • or a dictionary whose keys are strings and whose values are property lists.

A UIImage is none of those, so a UIImage is not a property list and cannot be part of a property list.

You need to convert your image to an NSData to store it in a user default. Since a UIImage contains some properties (like scale and imageOrientation) in addition to raw pixel data, the easiest way to convert a UIImage to an NSData with no loss is by creating an archive:

let cardImage: UIImage? = someImage()
let cardImageArchive: NSData = NSKeyedArchiver.archivedDataWithRootObject(cardImage!)

Now you can store cardImageArchive in a larger property list that you can store as a user default.

Later, when you need to recreate the image from the data, do this:

let cardImageArchive: NSData = dataFromUserDefaults()
let cardImage: UIImage = NSKeyedUnarchiver.unarchiveObjectWithData(cardImageArchive) as! UIImage

Upvotes: 2

Related Questions