Saving array with UserDefaults - ios

Is this the right way to save array of dictionaries in tableView using UserDefaults in Swift 3?
var arrNotes = [String:String]
func saveNotesArray () {
UserDefaults.standard.set(arrNotes, forKey: "notes")
UserDefaults.standard.synchronize()
}

Use this function to get your notes:
func saveNotesArray () {
var notes = UserDefaults.standard.object(forKey: "notes") as? [String:String]
}
Are you getting object in same way?

"[String: String]" Means a dictionary where the keys and the values are Strings.
"[[String: String]]" Means an array that contains dictionaries where the keys and values are strings.
create a dictionary of string and an array to hold them
let dictionary: [String: String] = ["keyOne" : "valueOne", "keyTwo": "valueTwo"]
let arrayWithDictionaryOfStrings: [[String: String]] = [dictionary]
add the array to userDefaults
let userDefaults = UserDefaults.standard
userDefaults.set(arrayWithDictionaryOfStrings, forKey: "arrayWithDictionaryOfStrings")
userDefaults.synchronize();
get the array of dictionaries from the userdefaults
if let fetchedArrayWithDictionaryOfStrings: [[String: String]] = (userDefaults.object(forKey: "arrayWithDictionaryOfStrings") as? [[String: String]]) {
// handle fetchedArrayWithDictionaryOfStrings
}

your should do it like This
For Saving
let placesData = NSKeyedArchiver.archivedData(withRootObject: placesArray)
UserDefaults.standard.set(arrNotes, forKey: "notes")
To Retrieved array from NSUserDefault.
let placesData = UserDefaults.standard.object(forKey: "notes") as? NSData
if let placesData = placesData {
let arrNotes = NSKeyedUnarchiver.unarchiveObject(with: placesData as Data) as? NSMutableArray
print(arrNotes)
}

In Swift-3, the following way to Saving array with UserDefaults :
let userDefaults = UserDefaults.standard
userDefaults.set(arrayOfname, forKey:"Keyname")
userDefaults.synchronize()

Related

Unable to get NSMutableArray with multiple Object from UserDefaults in Swift

I am having custom class which stores the login information of the user.
App can have multiple users logged in at a time.
This is how I am storing the logged in users data to UserDefaults.
let userDefaults = UserDefaults.standard
let decoded = userDefaults.data(forKey: "CPContactInfo")
if userDefaults.object(forKey: "CPContactInfo") != nil {
let decodedTeams = NSKeyedUnarchiver.unarchiveObject(with: decoded!) as! NSMutableArray
arrayOfArray.add(qidInfo)
}
} else {
print("userdefault not exist")
arrayOfArray.add(qidInfo)
}
addQID(qid_info: arrayOfArray)
Where qidInfo is custom object of class QBM_CPContactInfo which I store it in array,
at first I checked if UserDefault is present or not for given key at very first time, then added my qidInfo object into the array arrayOfArray and then that array is saved into UserDefault by calling function addQID(qid_info: arrayOfArray)
This is how my addQID function is,
func addQID(qid_info: NSMutableArray) {
let userDefaults = UserDefaults()
let encodedData: Data = NSKeyedArchiver.archivedData(withRootObject: qid_info)
userDefaults.set(encodedData, forKey: "CPContactInfo")
userDefaults.synchronize()
}
After this when I add second logged in details it executes the
if userDefaults.object(forKey: "CPContactInfo") != nil
perfectly.
When I try to retrieve data from UserDefaults as below,
let userDefaults = UserDefaults.standard
let decoded = userDefaults.data(forKey: "CPContactInfo")
if userDefaults.object(forKey: "CPContactInfo") != nil {
let decodedTeams = NSKeyedUnarchiver.unarchiveObject(with: decoded!) as! NSMutableArray
print(decodedTeams)
qid_info = decodedTeams.object(at: 0) as! QBM_CPContactInfo
print(qid_info?.qidEmail)
}
it gives me only one object in return for decodedTeams as NSMutableArray, but actually I have added two items in NSMutableArray.
Can any one tell me whats I am doing wrong.
further I need to compare the email of user selected which is in
let title = action.title along with qid_info?.qidEmail.
Any help will be appreciated.
Thanks in advance.
You can have the following approach.
let userDefaults = UserDefaults.standard
let decoded = userDefaults.data(forKey: "CPContactInfo")
if userDefaults.object(forKey: "CPContactInfo") != nil {
let decodedTeams = NSKeyedUnarchiver.unarchiveObject(with: decoded!) as! Array<QBM_CPContactInfo>
arrayOfArray = decodedTeams
arrayOfArray.append(qidInfo)
} else {
arrayOfArray.append(qidInfo)
}
addQID(qid_info: arrayOfArray)
Here, first, get the saved Array from defaults in a temporary array and copy that temporary array in your existing array.
Then append new elements into it and save the updated array for the same key.

How to Save Array of object in UserDefaults in iOS 12.0. using Any Array [[String: Any]]

I want to save an array of object in user default without using Codable.
I tried it to convert an array in data and save in User default:
let childsArray = result["childs"] as? [[String: Any]]
let childsData = NSKeyedArchiver.archivedData(withRootObject: childsArray!)
UserDefaults.standard.set(childsData, forKey: "childsArray")
UserDefaults.standard.synchronize()
It gives me this warning:
'archivedData(withRootObject:)' was deprecated in
iOS 12.0: Use +archivedDataWithRootObject:requiringSecureCoding:error: instead
How do I resolve this warning and achieve my desired goal of saving an array of objects in UserDefaults?
You can archived array using code below:
do {
let childsData = try NSKeyedArchiver.archivedData(withRootObject: childsArray!, requiringSecureCoding: true)
UserDefaults.standard.set(childsData, forKey: "childsArray")
UserDefaults.standard.synchronize()
} catch {
print(error)
}
And unarchive objects using code below:
do {
let childData = UserDefaults.standard.value(forKey: "childsArray")
if let childArray = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(childData as! Data) {
print(childArray)
}
} catch {
print(error)
}
let object1 = ["key1":"value1"]
let object2 = ["key2":"value2"]
let array = [object1, object2]
UserDefaults.standard.set(array, forKey: "childArray")
UserDefaults.standard.synchronize()
let storedArray = UserDefaults.standard.value(forKey: "childArray") as! [[String:String]]
print(storedArray)

Save dictionary to UserDefaults

I'm trying to store a dictionary in UserDefaults and always get app crash when the code runs. Here is the sample code which crashes the app when it is executed. I tried to cast it as NSDictionary or make it NSDictionary initially - got the same result.
class CourseVC: UIViewController {
let test = [1:"me"]
override func viewDidLoad() {
super.viewDidLoad()
defaults.set(test, forKey: "dict1")
}
}
Dictionaries are Codable objects by default, you can use the following extensions to save them to UserDefaults
extension UserDefaults {
func object<T: Codable>(_ type: T.Type, with key: String, usingDecoder decoder: JSONDecoder = JSONDecoder()) -> T? {
guard let data = self.value(forKey: key) as? Data else { return nil }
return try? decoder.decode(type.self, from: data)
}
func set<T: Codable>(object: T, forKey key: String, usingEncoder encoder: JSONEncoder = JSONEncoder()) {
let data = try? encoder.encode(object)
self.set(data, forKey: key)
}
}
They can be used like this:
let test = [1:"me"]
UserDefaults.standard.set(object: test, forKey: "test")
let testFromDefaults = UserDefaults.standard.object([Int: String].self, with: "test")
This extension and many others are part of SwifterSwift, you might want to use it for your next iOS project :)
To store a NSDictionary (with non-string key) in NSUserDefaults you need to convert them to NSData first.
Try this
let test = [1:"me"]
override func viewDidLoad() {
super.viewDidLoad()
let data = NSKeyedArchiver.archivedData(withRootObject: test)
let defaults = UserDefaults.standard
defaults.set(data, forKey: "dict1")
if let data2 = defaults.object(forKey: "dict1") as? NSData {
let dict = NSKeyedUnarchiver.unarchiveObject(with: data2 as Data)
print(dict)
}
}

Save and Load Custom Dictionary - NSUserDefaults

I have an [Int:Bool] dictionary and I am trying to save it into my NSDictionary.. However, it crashes with error Attempt to set a non-property-list object
let dictionary = [Int:Bool]()
self.dictionary[2] = true
self.dictionary[3] = false
NSUserDefaults.standardUserDefaults().setObject(dictionary, forKey: "dictionary")
Also, for loading, first I tried this but error logged it strictly requires AnyObject?.
NSUserDefaults.standardUserDefaults().objectForKey("dictionary")
Then I tried this and it logged:
NSUserDefaults.standardUserDefaults().objectForKey("dictionary") as? [Int:Bool]
I also tried dictionaryForKey. I got..
NSUserDefaults.standardUserDefaults().dictionaryForKey("dictionary")
Cannot assign value to type [String: AnyObject] to type [Int:Bool]
So which one of these 2 is a better approach to take? (The values would be optional in my case I think)
1.
NSUserDefaults.standardUserDefaults().objectForKey("dictionary") as? [Int:Bool] ?? [Int:Bool]()
2.
NSUserDefaults.standardUserDefaults().objectForKey("dictionary") as? [Int:Bool])!
Swift 4
Among basic types, UserDefaults can save any object that conforms to Codable protocol. Dictionary is one of the types that implements this protocol. You don't even need to write any custom code:
let dictionary = ["name": "Adam"]
// Save to User Defaults
UserDefaults.standard.set(dictionary, forKey: "names")
// Read from User Defaults
let saved = UserDefaults.standard.value(forKey: "names") as? [String: String]
See more info about Codable
Swift 3
You can use UserDefaults to save a Dictionary as long as key and value types are types that can be represented in a plist format (NSNumber, Data, etc.). If that's not the case, we can always serialise other types to Data when writing and deserialise from Data when reading. It can be accomplished with pretty simple extension of UserDefaults using NSKeyArchiver:
extension UserDefaults {
/// Save dictionary on key
open func set<Key, Value>(dictionary: [Key: Value]?, forKey key: String) {
let data = NSKeyedArchiver.archivedData(withRootObject: dictionary as Any)
set(data, forKey: key)
}
// Retrieve dictionary for key
open func dictionary<Key, Value>(forKey key: String) -> [Key: Value]? {
guard let data = object(forKey: key) as? Data else { return nil }
return NSKeyedUnarchiver.unarchiveObject(with: data) as? [Key: Value]
}
}
Now you can call these methods:
let ages = ["Adam": 25]
// Save
UserDefaults.standard.set(dictionary: ages, forKey: "ages")
// Read
let saved: [String: Int]? = UserDefaults.standard.dictionary(forKey: "ages")
print(saved) // Optional(["Adam": 25])
Swift 2
Save custom data
func setCustomDictionary(dict: [Int: Bool]) {
let keyedArch = NSKeyedArchiver.archivedDataWithRootObject(dict)
NSUserDefaults.standardUserDefaults().setObject(keyedArch, forKey: "dictionary")
}
Retrieve data
func getDictionary() -> [Int: Bool]? {
let data = NSUserDefaults.standardUserDefaults().objectForKey("dict")
let object = NSKeyedUnarchiver.unarchiveObjectWithData(data as! NSData)
return object as? [Int: Bool]
}
Usage
var customDictionary = [Int: Bool]()
customDictionary[2] = true
customDictionary[3] = false
// Store data in NSUserDefaults
setCustomDictionary(customDictionary)
// Get data from NSUserDefaults
let userDefaultsDictionary = getDictionary()
I had a similar issue, but with different types of data.
My suggestion is to convert to NSData and retrieve the data like so:
/// Save
NSUserDefaults.standardUserDefaults().setObject(NSKeyedArchiver.archivedDataWithRootObject(object), forKey: key)
/// Read
var data = NSUserDefaults.standardUserDefaults().objectForKey(key) as NSData
var object = NSKeyedUnarchiver.unarchiveObjectWithData(data) as [String: String]
(Although it is mentioned [String : String] I actually used for [[String: AnyObject]] and worked, so maybe it can work for you too!)
This is for Swift 3
func setCustomDictionary(dict: [Int: Bool]) {
let keyedArch = NSKeyedArchiver.archivedData(withRootObject: dict)
UserDefaults.standard.set(keyedArch, forKey: "dictionary")
}
func getDictionary() -> [Int: Bool]? {
let data = UserDefaults.standard.object(forKey: "dict")
let object = NSKeyedUnarchiver.unarchiveObject(with: (data as! NSData) as Data)
return object as? [Int: Bool]
}
if you neeed more types you can use generics like this:
func saveUserDefaults<T>(withKey key: String, dict: AnyObject, myType: T.Type) {
guard let dict = dict as? T else {
print("Type mismatch")
return
}
let archiver = NSKeyedArchiver.archivedData(withRootObject: dict)
UserDefaults.standard.set(archiver, forKey: key)
}
func getUserDefaults<T>(withKey key: String, myType: T.Type) -> T? {
let unarchivedObject = getUserDefaultData(withKey: key)
return unarchivedObject as? T
}
func getUserDefaultData(withKey key: String) -> Any? {
guard let data = UserDefaults.standard.object(forKey: key) else {
return nil
}
guard let retrievedData = data as? Data else {
return nil
}
return NSKeyedUnarchiver.unarchiveObject(with: retrievedData)
}
For example [Int:Int] type usage:
var customDictionary = [Int: Int]()
customDictionary[234] = 1
customDictionary[24] = 2
customDictionary[345] = 3
saveUserDefaults(withKey: "hello", dict: customDictionary as AnyObject, myType: [Int: Int].self)
let savedDictionary = getUserDefaults(withKey: "hello", myType: [Int: Int].self)
print(savedDictionary)

Save array in Keychain

I am trying to save an array in keychain but I can't convert an array to NSData. I have my function prepared for saving strings but I don't know how to get a valueData from an array.
func add(key: String, value: AnyObject) {
let service = NSBundle.mainBundle().bundleIdentifier!
let valueData: NSData! = value.dataUsingEncoding(NSUTF8StringEncoding,
allowLossyConversion: false)
let secItem = [
kSecClass as String : kSecClassGenericPassword as String,
kSecAttrService as String : service,
kSecAttrAccount as String : key,
kSecValueData as String : valueData
]
let result: UnsafeMutablePointer<AnyObject?> = nil
let status = Int(SecItemAdd(secItem, result))
if status == Int(errSecDuplicateItem){
self.update(key, newData: value)
} else {
print("An error occurred with code \(status)")
}
}
You can use NSKeyedArchiver and NSKeyedUnarchiver to convert NSArrays and NSDictionaries to data:
let array : NSArray = ["one", "two"]
let data = NSKeyedArchiver.archivedDataWithRootObject(array)
let arrayFromData = NSKeyedUnarchiver.unarchiveObjectWithData(data) as! NSArray
You can use NSKeyedArchiver.archivedDataWithRootObject on your array when saving, and NSKeyedUnarchiver.unarchiveObjectWithData when loading.
Providing the solution with the new methods in swift 5 code.
The provided answers are using deprecated methods.
let array : NSArray = ["one", "two"]
var data = Data()
do {
data = try NSKeyedArchiver.archivedData(withRootObject: array, requiringSecureCoding: true)
} catch {
print("archivedData Error");
}
do {
let arrayFromData = try NSKeyedUnarchiver.unarchivedObject(ofClass: NSArray.self, from: data)
} catch {
print("unarchivedObject Error");
}

Resources