Wood has seen this detail, with Userdefaults is not able to save a more complex object. You may have seen a method of Userdefaults, which has been setObject: forKey:
stored in this way, and NSDictionary
NSArray
has been stored in a string.
Accidentally saved an inherited JSONModel
entity class directly, and then it was tragic. Later, I looked up the following Apple documents:
The value parameter can be only property list objects: NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary. For NSArray and NSDictionary objects, their contents must be property list objects.
In simple terms setObject:forKey:
, the method can be stored NSData
, NSString
what the object, even if NSDictionary
and NSArray
inside the element must also be the property list objects. Specifically what is the property list object look here. About JSONModel
can see here, also good.
Now that Apple's API has been limited to this point, think of nothing else to play out. Yes, you can save the file. But it's still about using Userdefaults.
The core idea of solving this problem is to convert an object to NSData
, or to be serialized NSData
. Serialization is not necessarily accurate, but there is such a process, the specific later to elaborate. When an object can be converted into NSData
a method that is also applicable NSUserDefaults
setObject: forKey:
. This is the usage:
Suppose there is a user entity class Usermodel { var userid:string = "" var accesstoken:string = ""}//then let Usermodel = Usermodel ()//formally start let Userdefaults = Nsuserdefaults.standarduserdefaults () Get encodedobject = Nskeyedarchiver.archiveddatawithrootobject (object) userdefaults.setobject (Encodedobject, ForKey: "UserInfoKey") Userdefaults.synchronize ()//finally don't forget this.
The general meaning is reflected in all the above code. But if you run the above code, it's definitely going to go wrong.
to uncaught exception ‘NSInvalidArgumentException‘, reason: ‘Attempt to insert non-property list object UserModel for key UserInfoKey‘
Because it is not a property list Object setObject:forKey
, the app directly crash when executing the method.
This problem seems to be on the property list object. But back to what is said, our idea is to convert the object of this custom entity class into a NSData
. This time will be used NSKeyedArchiver
and NSKeyedUnarchiver
, this also indirectly used the NSCoding
interface. Because if an entity class is not implemented NSCoding
then there will be errors on NSKeyedArchiver
and NSKeyedUnarchiver
off.
Make a small improvement to the above code:
Class Weibousermodel:nsobject, nscoding {//1 struct PropertyKey {static let Useridkey = "UserId" Stati C Let Accesstokenkey = ' accesstoken ' static let Expirationdatekey = ' expirationdate ' static Let Refreshtoken Key = "Refreshtoken"} var userid:string? var accesstoken:string? var expirationdate:nsdate? var refreshtoken:string? Func Encodewithcoder (acoder:nscoder) {//2 acoder.encodeobject (userId, ForKey:PropertyKey.userIdKey) acod Er.encodeobject (Accesstoken, ForKey:PropertyKey.accessTokenKey) acoder.encodeobject (ExpirationDate, Forkey:proper Tykey.expirationdatekey) Acoder.encodeobject (Refreshtoken, ForKey:PropertyKey.refreshTokenKey)} required in It? (Coder Adecoder:nscoder) {//3 UserId = Adecoder.decodeobjectforkey (Propertykey.useridkey) as? String Accesstoken = Adecoder.decodeobjectforkey (Propertykey.accesstokenkey) as? String expirationdate = adecoder.decodeobjectforKey (Propertykey.expirationdatekey) as? NSDate Refreshtoken = Adecoder.decodeobjectforkey (Propertykey.refreshtokenkey) as? String}}
Such a modification would allow them to run. The following is explained in turn:
1. Implementation NSObject
and NSCoding
. NSObject
you can do it without adding @objc
some methods to modify it. The NSCoding
interface provides a way to encode and decode objects as they are serialized and deserialized.
UserModel
The class name is modified to WeiboUserModel
. This part of the code is part of the entire project and will be filled in later.
2. Use the method encoding when serializing an object. func encodeWithCoder(aCoder: NSCoder)
3. Use the decoding method when deserializing. init?(coder aDecoder: NSCoder)
Under the condition that the general logic does not modify, we look at the complete code that can save the entity class object.
Then let Usermodel = Weibousermodel ()//formal start to get userdefaults = Nsuserdefaults.standarduserdefaults () to Encodedobject = Nskeyedarchiver.archiveddatawithrootobject (object) userdefaults.setobject (Encodedobject, ForKey: "UserInfoKey") Userdefaults.synchronize ()//finally don't forget this.
So you can run it. But we can't stop here. Because if there are too many places in the project that need to be saved, the calls are filled with (most likely, copy-and-paste) NSUserDefaults
instances. This kind of code is too rigid. And it's easy to forget the last userDefaults.synchronize ()
call. This can cause problems with the storage of objects.
So we're going to do some encapsulation of this part of the code:
Extension Nsuserdefaults {//1 func savecustomobject (customobject object:nscoding, key:string) {//2 let encode Dobject = Nskeyedarchiver.archiveddatawithrootobject (object) self.setobject (Encodedobject, Forkey:key) Self.synchronize () } func getcustomobject (Forkey key:string), Anyobject? {//3 let decodedobject = Self.objectforkey (key) as? NSData if let decoded = decodedobject {Let object = Nskeyedunarchiver.unarchiveobjectwithdata (decoded) Return Object } return nil }}
We put the methods of access in NSUserDefaults
the extension. This way the user can use the same as the NSUserDefaults
method of using itself. And the synchronize()
method is encapsulated inside, no longer have to worry about forgetting that the D object has not been saved. Take a look at a small detail of the call.
Userdefaults.savecustomobject (Customobject:usermodel, Key: "Userinfokey")//Save Userdefaults.getcustomobject (" Userinfokey ") as? Weibousermodel//Fetch
Okay, here it is.
To be Continued
Swift: Save complex objects with Userdefaults