9

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

nathan
  • 9,329
  • 4
  • 37
  • 51
Jan
  • 12,992
  • 9
  • 53
  • 89

2 Answers2

25

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")
      }
 }
nathan
  • 9,329
  • 4
  • 37
  • 51
Jan
  • 12,992
  • 9
  • 53
  • 89
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
    }
}
Diego
  • 69
  • 5