Attempt to set a non-property-list object as an NSUserDefaults

前端 未结 11 1629
闹比i
闹比i 2020-11-22 15:00

I thought I knew what was causing this error, but I can\'t seem to figure out what I did wrong.

Here is the full error message I am getting:

Attempt to se         


        
相关标签:
11条回答
  • 2020-11-22 15:34

    Swift with @propertyWrapper

    Save Codable object to UserDefault

    @propertyWrapper
        struct UserDefault<T: Codable> {
            let key: String
            let defaultValue: T
    
            init(_ key: String, defaultValue: T) {
                self.key = key
                self.defaultValue = defaultValue
            }
    
            var wrappedValue: T {
                get {
    
                    if let data = UserDefaults.standard.object(forKey: key) as? Data,
                        let user = try? JSONDecoder().decode(T.self, from: data) {
                        return user
    
                    }
    
                    return  defaultValue
                }
                set {
                    if let encoded = try? JSONEncoder().encode(newValue) {
                        UserDefaults.standard.set(encoded, forKey: key)
                    }
                }
            }
        }
    
    
    
    
    enum GlobalSettings {
    
        @UserDefault("user", defaultValue: User(name:"",pass:"")) static var user: User
    }
    

    Example User model confirm Codable

    struct User:Codable {
        let name:String
        let pass:String
    }
    

    How to use it

    //Set value 
     GlobalSettings.user = User(name: "Ahmed", pass: "Ahmed")
    
    //GetValue
    print(GlobalSettings.user)
    
    0 讨论(0)
  • 2020-11-22 15:35

    It seems rather wasteful to me to run through the array and encode the objects into NSData yourself. Your error BC_Person is a non-property-list object is telling you that the framework doesn't know how to serialize your person object.

    So all that is needed is to ensure that your person object conforms to NSCoding then you can simply convert your array of custom objects into NSData and store that to defaults. Heres a playground:

    Edit: Writing to NSUserDefaults is broken on Xcode 7 so the playground will archive to data and back and print an output. The UserDefaults step is included in case its fixed at a later point

    //: Playground - noun: a place where people can play
    
    import Foundation
    
    class Person: NSObject, NSCoding {
        let surname: String
        let firstname: String
    
        required init(firstname:String, surname:String) {
            self.firstname = firstname
            self.surname = surname
            super.init()
        }
    
        //MARK: - NSCoding -
        required init(coder aDecoder: NSCoder) {
            surname = aDecoder.decodeObjectForKey("surname") as! String
            firstname = aDecoder.decodeObjectForKey("firstname") as! String
        }
    
        func encodeWithCoder(aCoder: NSCoder) {
            aCoder.encodeObject(firstname, forKey: "firstname")
            aCoder.encodeObject(surname, forKey: "surname")
        }
    }
    
    //: ### Now lets define a function to convert our array to NSData
    
    func archivePeople(people:[Person]) -> NSData {
        let archivedObject = NSKeyedArchiver.archivedDataWithRootObject(people as NSArray)
        return archivedObject
    }
    
    //: ### Create some people
    
    let people = [Person(firstname: "johnny", surname:"appleseed"),Person(firstname: "peter", surname: "mill")]
    
    //: ### Archive our people to NSData
    
    let peopleData = archivePeople(people)
    
    if let unarchivedPeople = NSKeyedUnarchiver.unarchiveObjectWithData(peopleData) as? [Person] {
        for person in unarchivedPeople {
            print("\(person.firstname), you have been unarchived")
        }
    } else {
        print("Failed to unarchive people")
    }
    
    //: ### Lets try use NSUserDefaults
    let UserDefaultsPeopleKey = "peoplekey"
    func savePeople(people:[Person]) {
        let archivedObject = archivePeople(people)
        let defaults = NSUserDefaults.standardUserDefaults()
        defaults.setObject(archivedObject, forKey: UserDefaultsPeopleKey)
        defaults.synchronize()
    }
    
    func retrievePeople() -> [Person]? {
        if let unarchivedObject = NSUserDefaults.standardUserDefaults().objectForKey(UserDefaultsPeopleKey) as? NSData {
            return NSKeyedUnarchiver.unarchiveObjectWithData(unarchivedObject) as? [Person]
        }
        return nil
    }
    
    if let retrievedPeople = retrievePeople() {
        for person in retrievedPeople {
            print("\(person.firstname), you have been unarchived")
        }
    } else {
        print("Writing to UserDefaults is still broken in playgrounds")
    }
    

    And Voila, you have stored an array of custom objects into NSUserDefaults

    0 讨论(0)
  • 2020-11-22 15:36

    Swift 5 Very Easy way

    //MARK:- First you need to encoded your arr or what ever object you want to save in UserDefaults
    //in my case i want to save Picture (NMutableArray) in the User Defaults in
    //in this array some objects are UIImage & Strings
    
    //first i have to encoded the NMutableArray 
    let encodedData = NSKeyedArchiver.archivedData(withRootObject: yourArrayName)
    //MARK:- Array save in UserDefaults
    defaults.set(encodedData, forKey: "YourKeyName")
    
    //MARK:- When you want to retreive data from UserDefaults
    let decoded  = defaults.object(forKey: "YourKeyName") as! Data
    yourArrayName = NSKeyedUnarchiver.unarchiveObject(with: decoded) as! NSMutableArray
    
    //MARK: Enjoy this arrry "yourArrayName"
    
    0 讨论(0)
  • 2020-11-22 15:38

    First off, rmaddy's answer (above) is right: implementing NSCoding doesn't help. However, you need to implement NSCoding to use NSKeyedArchiver and all that, so it's just one more step... converting via NSData.

    Example methods

    - (NSUserDefaults *) defaults {
        return [NSUserDefaults standardUserDefaults];
    }
    
    - (void) persistObj:(id)value forKey:(NSString *)key {
        [self.defaults setObject:value  forKey:key];
        [self.defaults synchronize];
    }
    
    - (void) persistObjAsData:(id)encodableObject forKey:(NSString *)key {
        NSData *data = [NSKeyedArchiver archivedDataWithRootObject:encodableObject];
        [self persistObj:data forKey:key];
    }    
    
    - (id) objectFromDataWithKey:(NSString*)key {
        NSData *data = [self.defaults objectForKey:key];
        return [NSKeyedUnarchiver unarchiveObjectWithData:data];
    }
    

    So you can wrap your NSCoding objects in an NSArray or NSDictionary or whatever...

    0 讨论(0)
  • 2020-11-22 15:38

    Swift 5: The Codable protocol can be used instead of NSKeyedArchiever.

    struct User: Codable {
        let id: String
        let mail: String
        let fullName: String
    }
    

    The Pref struct is custom wrapper around the UserDefaults standard object.

    struct Pref {
        static let keyUser = "Pref.User"
        static var user: User? {
            get {
                if let data = UserDefaults.standard.object(forKey: keyUser) as? Data {
                    do {
                        return try JSONDecoder().decode(User.self, from: data)
                    } catch {
                        print("Error while decoding user data")
                    }
                }
                return nil
            }
            set {
                if let newValue = newValue {
                    do {
                        let data = try JSONEncoder().encode(newValue)
                        UserDefaults.standard.set(data, forKey: keyUser)
                    } catch {
                        print("Error while encoding user data")
                    }
                } else {
                    UserDefaults.standard.removeObject(forKey: keyUser)
                }
            }
        }
    }
    

    So you can use it this way:

    Pref.user?.name = "John"
    
    if let user = Pref.user {...
    
    0 讨论(0)
  • 2020-11-22 15:40

    To save:

    NSUserDefaults *currentDefaults = [NSUserDefaults standardUserDefaults];
    NSData *data = [NSKeyedArchiver archivedDataWithRootObject:yourObject];
    [currentDefaults setObject:data forKey:@"yourKeyName"];
    

    To Get:

    NSData *data = [currentDefaults objectForKey:@"yourKeyName"];
    yourObjectType * token = [NSKeyedUnarchiver unarchiveObjectWithData:data];
    

    For Remove

    [currentDefaults removeObjectForKey:@"yourKeyName"];
    
    0 讨论(0)
提交回复
热议问题