SwiftyUserDefaults icon indicating copy to clipboard operation
SwiftyUserDefaults copied to clipboard

Attempt to insert non-property list object

Open pepejeria opened this issue 4 years ago • 8 comments

I just updated to version 5 from version 3 and I'm getting an "Attempt to insert non-property list object"-error.

Code used in version 3:

extension DefaultsKeys {
    static let userColors = DefaultsKey<[String: UIColor]>("userColors")
}

extension UserDefaults {

    subscript(key: DefaultsKey<[String: UIColor]>) -> [String: UIColor] {
        get { return unarchive(key) ?? [:] }
        set { archive(key, newValue) }
    }

}

Code used for version 5:

extension UIColor: DefaultsSerializable {}

extension DefaultsKeys {
    var userColors: DefaultsKey<[String: UIColor]> { .init("userColors", defaultValue: [:]) }
}

This gives me the error mentioned above though, am I missing something?

pepejeria avatar Apr 01 '20 11:04 pepejeria

Hey @pepejeria, we do not support dictionary objects other than the [String: WhateverIsAbleToBeStoredByPlists]. So basically UIColor is not ready yet to be stored in a dictionary in User Defaults. There is a ground work for it being implemented there, but it's not implemented yet. It would require checking the type of the value and use serializing (similar to how we observe keys). Though I'm not sure if it will be implemented soon since I don't have much time for new additions to the library - maybe you would like to contribute?

For now you can create a custom object that holds the dictionary and specify archive bridges for encoding/decoding the type.

sunshinejr avatar Apr 01 '20 12:04 sunshinejr

I created a custom object that holds the dictionary, based on the example provided in the documentation:

struct UserColors: DefaultsSerializable {

    static var _defaults: DefaultsBridge<UserColors> { DefaultsKeyedArchiverBridge() }
    static var _defaultsArray: DefaultsBridge<[UserColors]> { DefaultsKeyedArchiverBridge() }

    let colorsDic: [String: UIColor] = [:]
}

But this won't compile, nor does the FrogCustomSerializable example compile.

pepejeria avatar Apr 01 '20 19:04 pepejeria

@pepejeria what’s the compile error?

sunshinejr avatar Apr 01 '20 20:04 sunshinejr

Screen Shot 2020-04-01 at 22 13 34

pepejeria avatar Apr 01 '20 20:04 pepejeria

@pepejeria ah sorry about that, seems like some parts of the docs are not updated. Here’s a source code for one of our test cases, you would specify a specific type and return it like that: https://github.com/sunshinejr/SwiftyUserDefaults/blob/38441153885b660da10d3ff742b7e4fe8e8f11cc/Tests/SwiftyUserDefaultsTests/TestHelpers/TestHelper.swift#L106

Also here’s a archive bridge you would probably use for your type.

Let me know if it helps.

sunshinejr avatar Apr 01 '20 21:04 sunshinejr

How would I be able to support something like this? (I'm upgrading from version 3)

subscript(key: DefaultsKey<[CKRecordZone.ID: CKServerChangeToken?]>) -> [CKRecordZone.ID: CKServerChangeToken?]  {
        get { return unarchive(key) ?? [:] }
        set { archive(key, newValue) }
    }

Would it look something like this?

struct CKServerchangeTokenSerializable: DefaultsSerializable {
    static var _defaults: DefaultsKeyedArchiverBridge<[CKRecord.ID: CKServerChangeToken?]> { return DefaultsKeyedArchiverBridge() }
    static var _defaultsArray: DefaultsKeyedArchiverBridge<[[CKRecord.ID: CKServerChangeToken?]]> { return DefaultsKeyedArchiverBridge() }
    
    let changeTokens: [CKRecord.ID: CKServerChangeToken] = [:]
}

and instead of accessing it like a dictionary from user defaults, It would be like Defaults[\.serverChangeTokens].changeTokens[record.ID]?

MaxHasADHD avatar Apr 30 '20 20:04 MaxHasADHD

hey @MaxHasADHD, if you used archiving then yes, you could use the DefaultsKeyedArchiverBridge 👍 in terms of usage you're correct (given . serverChangeTokens is a DefaultsKey<CKServerchangeTokenSerializable>), depending on your Swift version you could also try using Defaults.serverChangeTokens since we have the support for this syntax too.

sunshinejr avatar May 03 '20 10:05 sunshinejr

import UIKit

class ViewController: UIViewController {

@IBOutlet var notesTextView: UITextView!

 

@IBOutlet var titleTextField: UITextField!


override func viewDidLoad() {
    
    super.viewDidLoad()
}




@IBAction func saveButton(_ sender: Any) {
   
    
    UserDefaults.standard.set(titleTextField, forKey: "Title")
    UserDefaults.standard.set(notesTextView, forKey: "Body")
    

    
}

@IBAction func loadNote(_ sender: Any) {

    let title = titleTextField.text
   
    print("my title is: \(title!)")
    
    let notes = notesTextView.text
    print("my body is: \(notes!)")
    
    titleTextField.text =
        UserDefaults.standard.object(forKey:
        "Title") as? String
    
    notesTextView.text =
        UserDefaults.standard.object(forKey:
        "Body") as? String

}

} i am having the same issue please help

williamso1234 avatar Mar 12 '23 23:03 williamso1234