12

My Object conforms to the new Swift 4 Codeable protocol. How to save an array of these Objects in UserDefaults?

struct MyObject: Codeable {
    var name: String
    var something: [String]
}

myObjectsArray = [MyObject]() // filled with objects
UserDefaults.standard.set(myObjectsArray, forKey: "user_filters")

Error

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attempt to insert non-property list object

0

2 Answers 2

29

Whew, I got it working:

Here is the Swift 4 Syntax to save an Array with Codeable Objects:

My solution is to encode it as a JSON Object and save that:

static var getAllObjects: [MyObject] {
      let defaultObject = MyObject(name: "My Object Name")
      if let objects = UserDefaults.standard.value(forKey: "user_objects") as? Data {
         let decoder = JSONDecoder()
         if let objectsDecoded = try? decoder.decode(Array.self, from: objects) as [MyObject] {
            return objectsDecoded
         } else {
            return [defaultObject]
         }
      } else {
         return [defaultObject]
      }
   }

 static func saveAllObjects(allObjects: [MyObject]) {
      let encoder = JSONEncoder()
      if let encoded = try? encoder.encode(allObjects){
         UserDefaults.standard.set(encoded, forKey: "user_objects")
      }
 }
Sign up to request clarification or add additional context in comments.

2 Comments

This is a nice solution - I was using property list encoder. Mark yourself correct!
What about KeyedArchiver?
0

You can use a more generic approach, using array with specific type:

(myObject = any custom codable object you like)

(myKey = a string constant key to be able to retrieve/set specific array)

//set
setObject(myArray, forKey: mykey)

//get
let myArray = getObject(forKey: mykey, castTo: Array<myObject>.self)

and generic functions also, for any type:

func setObject<Object>(_ object: Object, forKey: String) where Object: Encodable
{
    let encoder = JSONEncoder()
    do {
        let data = try encoder.encode(object)
        set(data, forKey: forKey)
        synchronize()
    } catch let encodeErr {
        print("Failed to encode object:", encodeErr)
    }
}
    
func getObject<Object>(forKey: String, castTo type: Object.Type) -> Object? where Object: Decodable
{
    guard let data = data(forKey: forKey) else { return nil }
    let decoder = JSONDecoder()
    do {
        let object = try decoder.decode(type, from: data)
        return object
    } catch let decodeError{
        print("Failed to decode object:" , decodeError)
        return nil
    }
}

1 Comment

how can I make a generic structure encodable?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.