UserDefaults Custom Object of Custom Objects - swift

Part of my settings I want to save with UserDefaults.
I already found this solution here:
Save custom objects into NSUserDefaults
But I could not figure out how to save if I have custom objects in a custom object.
My classes look like this:
class ConfigLabelMainList: NSObject, NSCoding {
var labelMiddleFirst: StatsIntervalModel
var labelMiddleSecond: StatsIntervalModel
var labelMiddleThird: StatsIntervalModel
var labelRightFirst: StatsIntervalModel
var labelRightSecond: StatsIntervalModel
init(labelMiddleFirst: StatsIntervalModel, labelMiddleSecond: StatsIntervalModel, labelMiddleThird: StatsIntervalModel, labelRightFirst: StatsIntervalModel, labelRightSecond: StatsIntervalModel) {
self.labelMiddleFirst = labelMiddleFirst
self.labelMiddleSecond = labelMiddleSecond
self.labelMiddleThird = labelMiddleThird
self.labelRightFirst = labelRightFirst
self.labelRightSecond = labelRightSecond
}
func encode(with aCoder: NSCoder) {
}
required convenience init?(coder aDecoder: NSCoder) {
}
}
class StatsIntervalModel: NSObject, NSCoding {
var stat: String
var interval: String
init(stat: String, interval: String) {
self.stat = stat
self.interval = interval
}
required convenience init?(coder aDecoder: NSCoder) {
let stat = aDecoder.decodeObject(forKey: "stat") as! String
let interval = aDecoder.decodeObject(forKey: "interval") as! String
self.init(stat: stat, interval: interval)
}
func encode(with aCoder: NSCoder) {
aCoder.encode(stat, forKey: "stat")
aCoder.encode(interval, forKey: "interval")
}
}
How would a solution look like?

Add given code in your class ConfigLabelMainList
required convenience init?(coder aDecoder: NSCoder) {
let midFirst = aDecoder.decodeObject(forKey: "midFirst") as! StatsIntervalModel
let midSecond = aDecoder.decodeObject(forKey: "midSecond") as! StatsIntervalModel
let midThird = aDecoder.decodeObject(forKey: "midThird") as! StatsIntervalModel
let rightFirst = aDecoder.decodeObject(forKey: "rightFirst") as! StatsIntervalModel
let rightSecond = aDecoder.decodeObject(forKey: "rightSecond") as! StatsIntervalModel
self.init(labelMiddleFirst: midFirst, labelMiddleSecond: midSecond, labelMiddleThird: midThird, labelRightFirst: rightFirst, labelRightSecond: rightSecond)
}
func encode(with aCoder: NSCoder) {
aCoder.encode(labelMiddleFirst, forKey: "midFirst")
aCoder.encode(labelMiddleSecond, forKey: "midSecond")
aCoder.encode(labelMiddleThird, forKey: "midThird")
aCoder.encode(labelRightFirst, forKey: "rightFirst")
aCoder.encode(labelRightSecond, forKey: "rightSecond")
}
Now simply archive data and save it in defaults.

Related

How to unarchive object using NSKeyedUnarchiver?

**I'm using this class: **
class Person: NSObject, NSCoding {
var name: String
var image: String
init(name: String, image: String) {
self.name = name
self.image = image
}
required init(coder aDecoder: NSCoder){
name = aDecoder.decodeObject(forKey: "name") as? String ?? ""
image = aDecoder.decodeObject(forKey: "image") as? String ?? ""
}
func encode(with coder: NSCoder) {
coder.encode(name, forKey: "name")
coder.encode(image, forKey: "image")
}
}
For archiving, i used this method:
if let savedData = try? NSKeyedArchiver.archivedData(withRootObject: people, requireSecureCoding: false)
whrere people is Person class array [Person]
As for unarchiving, this method:
NSKeyedUnarchiver.unarchivedTopLevelObjectWithData()
is deprecated... which method should i use now ?
You now must tell the system what type you expect rather than simply unarchiving whatever is found:
try NSKeyedUnarchiver.unarchivedArrayOfObjects(ofClass: Person.self, from: savedData)

Save and get details of Model class using userdefault

I have one model object User and inside that another model object is Picture.
"user": {
"id": 1,
"email": "abc.k#gmail.com",
"user_profile_photo": {
"id": 997,
"user_id": 1,
"photo_url": "https://newproduction.s3.amazonaws.com/profile_image/RkUJAczv5nWpUyFTgyTgMLChR.jpeg",
}
}
I have two model class for this one is User and another is Picture inside user.
I am saving model user in userdefault as below
//Get Response
loginResponseObj = Mapper<LoginResponse>().map(JSONObject:(response.result.value))
//Save user Details
let userData = loginResponseObj.user!
let data = NSKeyedArchiver.archivedData(withRootObject: userData)
UserDefaults.standard.set(data, forKey:"user")
and when i am trying to get data from userdefaults, am getting User Model but inside details are nil.
Get Userdetails from userdefault Code is below
guard let data = UserDefaults.standard.object(forKey: "user") as? Data
else
{
return UserModel()
}
return (NSKeyedUnarchiver.unarchiveObject(with: data) as? UserModel)!
This return **<ABC.User: 0x7f84f740c440>**
But when i am trying to get Picture from User it return nil
In User Model
class User:NSObject,Mappable,NSCoding{
var email: String?
var picture: Picture?
required init?(coder aDecoder: NSCoder) {
self.email = aDecoder.decodeObject(forKey: "email") as? String
}
func initWithCoder(aDecoder:NSCoder) -> UserModel
{
self.email = aDecoder.decodeObject(forKey: "email") as? String
return self
}
func encode(with aCoder: NSCoder) {
aCoder.encode(email, forKey: "email")
}
}
In Picture Model
class Picture:Mappable,NSCoding{
var id: String?
var photoURL: String?
required init?(coder aDecoder: NSCoder) {
self.id = aDecoder.decodeObject(forKey: "id") as? String
self.photoURL = aDecoder.decodeObject(forKey: "photoURL") as? String
}
func initWithCoder(aDecoder:NSCoder) -> Picture
{
self.id = aDecoder.decodeObject(forKey: "id") as? String
self.photoURL = aDecoder.decodeObject(forKey: "photoURL") as? String
return self
}
func encode(with aCoder: NSCoder) {
aCoder.encode(id, forKey: "id")
}
}
Note: I am using MVVM Pattern and Object Mapper
So, How can i get the whole details of user including photo_url (User.Picture.photo_url) ?
In User Model
class User:NSObject,Mappable,NSCoding{
var email: String?
var picture: Picture?
required init?(coder aDecoder: NSCoder) {
self.email = aDecoder.decodeObject(forKey: "email") as? String
self.picture = aDecoder.decodeObject(forKey: "picture") as? Picture
}
func initWithCoder(aDecoder:NSCoder) -> UserModel
{
self.email = aDecoder.decodeObject(forKey: "email") as? String
self.picture = aDecoder.decodeObject(forKey: "picture") as? Picture
return self
}
func encode(with aCoder: NSCoder) {
aCoder.encode(email, forKey: "email")
aCoder.encode(picture, forKey: "picture")
}
}
In Picture Model
class Picture: NSObject,Mappable,NSCoding {
var id: String?
var photoURL: String?
required init?(coder aDecoder: NSCoder) {
self.id = aDecoder.decodeObject(forKey: "id") as? String
self.photoURL = aDecoder.decodeObject(forKey: "photoURL") as? String
}
func initWithCoder(aDecoder:NSCoder) -> Picture
{
self.id = aDecoder.decodeObject(forKey: "id") as? String
self.photoURL = aDecoder.decodeObject(forKey: "photoURL") as? String
return self
}
func encode(with aCoder: NSCoder) {
aCoder.encode(id, forKey: "id")
aCoder.encode(photoURL, forKey: "photoURL")
}
Get Details From User Defaults
guard let data = UserDefaults.standard.object(forKey: "user") as? Data
else
{
return User()
}
return (NSKeyedUnarchiver.unarchiveObject(with: data) as? User)!
above code will return User Model
Using this model access its variables
print(user.email)
print(user.picture.photoURL)

Custom Class Unarchive is nil in Cocoa Swift

I am trying to save and retrieve a custom class to UserDefaults in my macOS app. I am getting nil for newData
class countClass: NSObject, NSCoding {
var leftClickCount : Int = 0
init(leftClickCount: Int) {
self.leftClickCount = leftClickCount
super.init()
}
func encode(with coder: NSCoder) {
coder.encode(self.leftClickCount, forKey: "leftClickCount")
}
required convenience init?(coder decoder: NSCoder) {
guard let leftClickCount = decoder.decodeObject(forKey: "leftClickCount") as? Int
else {
return nil
}
self.init(
leftClickCount: leftClickCount
)
}
}
class ViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
let leftC = countClass(leftClickCount: 25)
let ud = UserDefaults.standard
let archivedData = NSKeyedArchiver.archivedData(withRootObject: leftC)
ud.set(archivedData, forKey: "data")
ud.synchronize()
let tempData = ud.object(forKey: "data") as! Data
let newData = NSKeyedUnarchiver.unarchiveObject(with: tempData) as! countClass // Getting nil here
}
}
I was able to fix this problem by changing from:
decoder.decodeObject(forKey: "leftClickCount") as? Int
with:
decoder.decodeInteger(forKey: "leftClickCount")

Passing Objects To WatchKit with NSKeyedArchiver

class Parent: NSObject, NSCoding {
var name : String?
var childs : [Child]?
override init() {}
func encode(with aCoder: NSCoder) {
aCoder.encode(name, forKey: "name")
aCoder.encode(childs, forKey: "childs")
}
required init(coder aDecoder: NSCoder) {
name = aDecoder.decodeObject(forKey: "name") as? String
childs = aDecoder.decodeObject(forKey: "childs") as? [Child]
super.init()
}
}
class Child: NSObject, NSCoding {
var name: String?
override init() {}
func encode(with aCoder: NSCoder) {
aCoder.encode(name, forKey: "name")
}
required init(coder aDecoder: NSCoder) {
name = aDecoder.decodeObject(forKey: "name") as? String
super.init()
}
}
// iOS side:
public func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: #escaping ([String : Any]) -> Swift.Void) {
var parent = Parent()
parent.name = "John"
var child1 = Child()
var child2 = Child()
parent.childs = [child1, child2]
NSKeyedArchiver.setClassName("Parent", for: Parent.self)
let data = NSKeyedArchiver.archivedData(withRootObject: parent)
replyHandler(["parent": data as AnyObject])
}
// watchOS side:
if WCSession.isSupported() {
session = WCSession.default()
session!.sendMessage(["getParent": "getParent"], replyHandler: { (response) -> Void in
if let rawParent = response as? [String:Data] {
NSKeyedUnarchiver.setClass(Parent.self, forClassName: "Parent")
if let parent = NSKeyedUnarchiver.unarchiveObject(with: data) as? Parent {
// do something
}
}
}, errorHandler: { (error) -> Void in
print(error)
})
}
in this case as you can see I used the NSKeyedUnarchiver.setClass and NSKeyedArchiver.setClassName methods which are working but I still get a runtime crash:
[NSKeyedUnarchiver decodeObjectForKey:]: cannot decode object of class (Child) for key (NS.objects)
If I remove the NSKeyedUnarchiver.setClass(Parent.self, forClassName: "Parent") and the NSKeyedArchiver.setClassName("Parent", for: Parent.self) rows I get the same error but for Parent class. So I am sure that I have to use the same method call for Child but I dont know where.
Anybody has any idea?
Okey I found the solution, if you have the same structure you just simply set the class names after each other
NSKeyedArchiver.setClassName("Parent", for: Parent.self)
NSKeyedArchiver.setClassName("Child", for: Child.self)
and the same when you unarchive it:
NSKeyedUnarchiver.setClass(Parent.self, forClassName: "Parent")
NSKeyedUnarchiver.setClass(Child.self, forClassName: "Child")

Why is retrieval from NSUserDefaults failing for my custom object?

I have a class PredicPair which inherits from NSCoding and NSObject as such:
class PredicPair: NSObject, NSCoding {
var weight : Float
var prediction : String
init(weight: Float, prediction: String) {
self.weight = weight
self.prediction = prediction
super.init()
}
func encode(with aCoder: NSCoder) {
aCoder.encode(weight, forKey: "weight")
aCoder.encode(prediction, forKey: "prediction")
}
required convenience init(coder aDecoder: NSCoder) {
let unarchivedWeight = aDecoder.decodeObject(forKey: "weight") as! Float
let unarchivedPrediction = aDecoder.decodeObject(forKey: "prediction") as! String
self.init(weight: unarchivedWeight, prediction: unarchivedPrediction)
}
class func saveToUserDefaults(pairs: [PredicPair]) {
let dataBlob = NSKeyedArchiver.archivedData(withRootObject: pairs)
UserDefaults.standard.set(dataBlob, forKey: "test")
UserDefaults.standard.synchronize()
}
class func loadFromUserDefaults() -> [PredicPair]? {
guard let decodedNSDataBlob = UserDefaults.standard.object(forKey: "test") as? NSData,
let loadedFromUserDefault = NSKeyedUnarchiver.unarchiveObject(with: decodedNSDataBlob as Data) as? [PredicPair]
else {
return nil
}
return loadedFromUserDefault
}
}
I am trying to store an array of the class in UserDefaults and retrieving it, but the latter always returns nil. Any reason why?
let predicPair1 = PredicPair(weight: 0, prediction: "there")
let predicPair2 = PredicPair(weight: 1, prediction: "hello")
let array = [predicPair1, predicPair2]
PredicPair.saveToUserDefaults(pairs: array)
if let retreivedArray = PredicPair.loadFromUserDefaults() {
print("loaded \(retreivedArray.count) players from NSUserDefaults")
} else {
print("failed")
}
I've also tried saving to a file using NSKeyedArchiver but retrieval fails as well.