I want to save a struct which can not be modified to UserDefaults in my code. I went through a number of solutions but everyone is advising to use Codable/NSCoding in the struct. I can not edit the struct. Does anyone have an idea how we can achieve this.
I tried to wrap the object to an NSDictionary but while trying to save, it crashes saying 'Attempt to insert non-property list object'. Is it because I have nil values in my model?
Any help is appreciated.
If you can't change your struct, then create an extension for it and conform it to Codable.
Once conformed to Codable, you can use JSONEncoder and JSONDecoder to convert to and from data and then save/retrieve from UserDefaults.
struct Sample {
var name = "Sample Struct"
}
extension Sample: Codable {
}
let obj = Sample()
let data = try? JSONEncoder().encode(obj)
//Saving in UserDefaults
UserDefaults.standard.set(data, forKey: "SampleStruct")
//Fetching from UserDefaults
if let data = UserDefaults.standard.data(forKey: "SampleStruct") {
let val = try? JSONDecoder().decode(Sample.self, from: data)
print(val) //Sample(name: "Sample Struct")
}
I tried to wrap the object to an NSDictionary
You can't save a custom model without conforming to Codable/NSCoding , if you don't need to conform then save it as a usual Array/Dictionary without wrapping any custom objects inside
I have saved an array of custom objects that conforms to NSCoding protocol in UserDefaults using NSKeyedArchiver. But when I try to retrieve it using NSKeyedUnarchiver, i get runtime errors from Xcode. I have tried initialising NSArray using unarchived data but it also failed. My guess is that while unarchiving swift doesn't understand custom elements of this array. How shall I do it?
This is how I archived array of custom objects
static func saveCategoryList(_ categoryList : [Category]!) -> Void{
let userDefaults = UserDefaults.standard
let categoryListData = NSKeyedArchiver.archivedData(withRootObject: categoryList)
userDefaults.set(categoryListData, forKey: Constants.CategoryList)
userDefaults.synchronize()
}
I get error like the attached screenshot in runtime. I am quite sure I am not doing it right. How can get my desired result?
I have data sent back to my VC using the delegate method. How can I possibly store it so then I can use it to send to another VC?
func DataToPass(ArrayName: [String]) { //function from delegate
Datacollect = ArrayName
print(ArrayName)
}
Here's the function used in the delegate method that holds my data. ArrayName is an array containing my data.
Datacollect is an attempt to collect it, however nothing gets stored in Datacollect.
I have already assigned Datacollect as a String array.
var Datacollect = [String]()
How can I store the data to my VC from ArrayName?
There are lot of ways of storing data and one of the simplest ways to get started is to use the built-in UserDefaults.
This is how you might use the following code inside a method to store your DataCollect array.
Let defaults = UserDefaults.standard
defaults.set(DataCollect, forKey: "DataCollect")
To retrieve the data you could use the following code inside a method:
let defaults = UserDefaults.standard
let DataCollect = defaults.array(forkey: "DataCollect")
I am assigning UserInfo Dictionary from NSUserDefault as NSMutableDictionary.
Now my dicInfo is mutable dictionary, but object it contains are immutable.
So, when i am trying to replace those value it cause crash.
I am attaching image which describe crash report.
If any solution, to how to convert inner object of mutable dictionary to mutable.
Thanks
The NSDictionary class conforms to the NSMutableCopying protocol. As such, we can call the mutableCopy method on an NSDictionary to get an NSMutableDictionary copy of the object.
let dicInfo = userSharedDefaults?.objectForKey(UserDefaultKey.kUserBasicInfo) as? NSDictionary
let mutableDictionary = dicInfo?.mutableCopy
In Swift, we may need to cast this as the correct type:
let mutableDictionary = dicInfo?.mutableCopy as? NSMutableDictionary
var dicInfo = (userSharedDefault.object(forKey: "kUserbasicInfo") as! NSDictionary).mutableCopy() as! NSMutableDictionary
You can also create Mutable Dictionary as follows:
It will fix the crash.
let dicInfo = NSMutableDictionary.init(dictionary: userSharedDefaults?.objectForKey(UserDefaultKey.kUserBasicInfo) as! NSDictionary)
Neither use NSMutableDictionary nor mutableCopy() in Swift to get a mutable dictionary from UserDefaults.
Never do that.
Normally far be it from me to criticize other answers but NSMutableDictionary and mutableCopy() are indeed inappropriate API in Swift.
To get a dictionary from UserDefaults use dedicated method dictionary(forKey:. The default dictionary type is [String:Any]
To make an object mutable simply use the var keyword
var userbasicInfo : [String:Any]
if let dictionary = UserDefaults.standard.dictionary(forKey: UserDefaultKey.kUserBasicInfo) {
userbasicInfo = dictionary
} else {
userbasicInfo = [String:Any]()
}
userbasicInfo[kPin] = 5678
print(userbasicInfo)
UserDefaults.standard.set(userbasicInfo, forKey:UserDefaultKey.kUserBasicInfo)
I've got a really weird error while running my app on Xcode 7 (Swift 2) that shows a "Thread 1: signal SIGABRT" running error message in the App Delegate class of my app. However I've actually already got this "Thread 1: signal SIGABRT" running error message in the App Delegate class lots of times, mainly when deleting an outlet reference in my code and forgetting to also delete it from storyboard. But that's certainly the first time I've got this same error when trying to make the command:
let wasteGain = WastesGainsClass(value: enteredMoney, originOrCat: segControlArray[segControl.selectedSegmentIndex], specification: plusEspecTField.text!, date: dateArray, mode: "gain")
gains.append(wasteGain)
NSUserDefaults.standardUserDefaults().setObject(gains, forKey: "gains")
What happens is that if I just comment the line NSUserDefaults.standardUserDefaults().setObject(gains, forKey: "gains") the app doesn't crash! So the error might just be in that line.
If anyone could help me, I`d thank you so much.
PS: WastesGainsClass format is like this:
class WastesGainsClass {
var value:Int = 0
var origin:String
var specification:String
var date:[String]
var mode:String
var rowMode:Int = 0
init(value:Int, originOrCat:String, specification:String, date:[String], mode:String) {
self.value = value
self.origin = originOrCat
self.specification = specification
self.date = date
self.mode = mode
}
}
From documentation:
The NSUserDefaults class provides convenience methods for accessing
common types such as floats, doubles, integers, Booleans, and URLs. A
default object must be a property list, that is, an instance of (or
for collections a combination of instances of): NSData, NSString,
NSNumber, NSDate, NSArray, or NSDictionary. If you want to store any
other type of object, you should typically archive it to create an
instance of NSData.
In Swift you can also use:
Int, UInt, Double, Float and Bool types because they are automatically bridged to NSNumber;
String bridged to NSString
[AnyObject] because it is bridged to NSArray;
[NSObject: AnyObject] because it is bridged to NSDictionary.
Of course type of array elements and dictionary values must be one of above types. Dictionary key type must be NSString (or bridged String).
To store instances of any other class you have two options:
Your custom class must be subclass of NSObject and conform to
NSCoding protocol and then you can archive object of this class to NSData with NSKeyedArchiver.archivedDataWithRootObject() and save it to NSUserDefaults and later retrieve it from NSUserDefaults and unarchive with NSKeyedUnarchiver.unarchiveObjectWithData():
import Foundation
class WastesGainsClass: NSObject, NSCoding {
var value: Int
init(value: Int) {
self.value = value
}
required init(coder aDecoder: NSCoder) {
value = aDecoder.decodeObjectForKey("value") as! Int
}
func encodeWithCoder(aCoder: NSCoder) {
aCoder.encodeObject(value, forKey: "value")
}
}
var gains = [WastesGainsClass(value: 1), WastesGainsClass(value: 2)]
NSUserDefaults.standardUserDefaults().setObject(gains.map { NSKeyedArchiver.archivedDataWithRootObject($0) }, forKey: "gains")
if let gainsData = NSUserDefaults.standardUserDefaults().objectForKey("gains") as? [NSData] {
gains = gainsData.map { NSKeyedUnarchiver.unarchiveObjectWithData($0) as! WastesGainsClass }
}
You can save your custom object properties to dictionary and store that
dictionary in NSUserDefaults:
import Foundation
class WastesGainsClass {
var value: Int
init(value: Int) {
self.value = value
}
}
extension WastesGainsClass {
convenience init(dict: [NSObject: AnyObject]) {
self.init(value: dict["value"] as? Int ?? 0)
}
func toDict() -> [NSObject: AnyObject] {
var d = [NSObject: AnyObject]()
d["value"] = value
return d
}
}
var gains = [WastesGainsClass(value: 1), WastesGainsClass(value: 2)]
NSUserDefaults.standardUserDefaults().setObject(gains.map { $0.toDict() }, forKey: "gains")
if let dicts = NSUserDefaults.standardUserDefaults().objectForKey("gains") as? [[NSObject: AnyObject]] {
gains = dicts.map { WastesGainsClass(dict: $0) }
}
NSUserDefaults unfortunately can't accept arbitrary objects, only objects that can be encoded in a Property List. See Apple's reference guide for Property Lists to learn which objects can be stored.
If you need to save several WastesGainsClass objects, you may wish to write a method that returns a Dictionary encoding their Property List-representable properties, and an initializer that accepts such a Dictionary to restore the object.
However, if you truly need to save multiple custom objects like this, you probably don't want to use NSUserDefaults at all. Consider a document-based app, and look into NSCoding.
The code you posted tries to save an array of custom objects to NSUserDefaults. You can't do that. Implementing the NSCoding methods doesn't help. You can only store things like NSArray, NSDictionary, NSString, NSData, NSNumber, and NSDate in NSUserDefaults.
You need to convert the object to NSData (like you have in some of the code) and store that NSData in NSUserDefaults. You can even store an NSArray of NSData if you need to.
see this post : Attempt to set a non-property-list object as an NSUserDefaults