Swift NSUserDefaults NSArray using objectForKey - ios

I'm pretty new to Swift, and I've managed to get pretty stuck.
I'm trying to retrieve data from NSUserDefaults and store it in an array (tasks):
#lazy var tasks: NSArray = {
let def = NSUserDefaults.standardUserDefaults()
let obj: AnyObject? = def.objectForKey("tasks")
return obj as NSArray
}()
All I'm getting is a warning: EXE_BAD_INSTRUCTION on line 3.
Also to note that I haven't actually set any data yet, but what I'm aiming for is that if there is no data, I want the array to be empty. I'll be using the data to populate a table view.
Now using a var instead of a constant:
#lazy var tasks: NSArray = {
let def = NSUserDefaults.standardUserDefaults()
var obj: AnyObject? = {
return def.objectForKey("tasks")
}()
return obj as NSArray
}()
The error has now moved to the return line.

I think the problem here is that you are attempting to cast nil to a non-optional type and return it. Swift does not allow that. The best way to solve this would be the following:
#lazy tasks: NSArray = {
let defaults = NSUserDefaults.standardUserDefaults()
if let array = defaults.arrayForKey("tasks") as? NSArray {
return array
}
return NSArray()
}
Using Swift's if let syntax combined with the as? operator lets you assign and safe cast in one line. Since your method does not return an optional, you must return a valid value if that cast fails.

Related

User defaults gives nil value in Swift 3.0

I am new to swift and trying to store NSMutableArray in Userdefaults. Here is what I am doinig :
//to save aaray in user defaults
var set:NSMutableArray = NSMutableArray(objects: self.listId)
self.saveListIdArray(set)
func saveListIdArray(_ params: NSMutableArray = []) {
let defaults = UserDefaults.standard
defaults.set(params, forKey: "ArrayKey")
defaults.synchronize()
}
//to get array from user default
func getUserDefaultKeyForAllObject(key userDefaultsKey: String) -> NSMutableArray {
let array = UserDefaults.standard.object(forKey: NSUserDefaultsKey.LIST_ID_ARRAY) as! NSMutableArray
print(array)
return array
}
App crashes with "fatal error: unexpectedly found nil while unwrapping an Optional value" exception.
Ignore the way I ask the question, help me out here.
Thank you.
Try converting to NSData then storing to nsuserdefaults like below
func saveListIdArray(_ params: NSMutableArray = []) {
let data = NSKeyedArchiver.archivedData(withRootObject: params)
UserDefaults.standard.set(data, forKey: "test")
UserDefaults.standard.synchronize()
}
For retrieving the data use
if let data = UserDefaults.standard.object(forKey: "test") as? Data {
if let storedData = NSKeyedUnarchiver.unarchiveObject(with: data) as? NSMutableArray
{
// In here you can access your array
}
}
You are force unwrapping the NSMutableArray for a key. Don't force unwrap when you try to get the value from a dictionary or UserDefault for a key because there may be a chance that the value does not exist for that key and force unwrapping will crash your app.
Do this as:
//to get array from user default
if let array = UserDefaults.standard.object(forKey:"ArrayKey") as? NSMutableArray
print(array)
}
I have 2 possible reasons for this:
You need to be 100% sure that you are retrieving array with the same key as you save it with. In your code you are saving the array with "ArrayKey" but retrieving it with NSUserDefaultsKey.LIST_ID_ARRAY, are you sure this is the same string?
What datatype is self.listId? If it's a custom class then you need to make that class conform to the nscoding protocol, then encode it to Data and save that to the userDefaults (Save custom objects into NSUserDefaults)
A 3rd reason is that you are trying to get an object from the defaults without ever writing anything to it. Try changing
let array = UserDefaults.standard.object(forKey: NSUserDefaultsKey.LIST_ID_ARRAY) as! NSMutableArray
print(array)
return array
to
if let array = UserDefaults.standard.object(forKey: "ArrayKey") as? NSMutableArray {
print(array)
return array
}
else {
return NSMutableArray()
}

NsMutable is not subtype of [anyobject] in swift

I want to get the array from NSUserdefault and store it into NsMutableArray but unfortunately it fails.
error
NsMutable is not subtype of [anyobject] in swift
code
arrayImage = NSUserDefaults.standardUserDefaults().arrayForKey("ImageArray") as NSMutableArray
I am also trying to do downcast NSArray->NSString like in this post.
The NSUserDefaults arrayForKey method returns an optional array of AnyObject. It you want it to be mutable just declare it using "var".// example:
var mutableArray = NSUserDefaults.standardUserDefaults().arrayForKey("ImageArray")
If you want it to be immutable you have to declare it using "let".
let imutableArray = NSUserDefaults.standardUserDefaults().arrayForKey("ImageArray")
If you want to make sure your method does not return nil before setting any value to userdefaults you can use the nil coalescing operator "??" to return an empty array as follow:
var mutableArray = NSUserDefaults.standardUserDefaults().arrayForKey("ImageArray") ?? []
If you want to store images using user defaults you have to first convert your image to NSData. So you should create a method class to help you with it:
class Save {
class func image(key:String, _ value:UIImage) {
NSUserDefaults.standardUserDefaults().setObject(UIImagePNGRepresentation(value), forKey: key)
}
class func imageArray(key:String, _ value:[UIImage]) {
NSUserDefaults.standardUserDefaults().setObject(NSKeyedArchiver.archivedDataWithRootObject(value), forKey: key)
}
}
class Load {
class func image(key:String) -> UIImage! {
return UIImage(data: ( NSUserDefaults.standardUserDefaults().objectForKey(key) as NSData))!
}
class func imageArray(key:String) -> [UIImage]! {
if let myLoadedData = NSUserDefaults.standardUserDefaults().dataForKey(key) {
return (NSKeyedUnarchiver.unarchiveObjectWithData(myLoadedData) as? [UIImage]) ?? []
}
return []
}
}
Testing
let myProfilePicture1 = UIImage(data: NSData(contentsOfURL: NSURL(string: "http://i.stack.imgur.com/Xs4RX.jpg")!)!)!
let myProfilePicture2 = UIImage(data: NSData(contentsOfURL: NSURL(string: "https://lh6.googleusercontent.com/-Axxzrunya9Y/AAAAAAAAAAI/AAAAAAAAAHo/gsDxk5FlliE/photo.jpg")!)!)!
Save.image("myPic", myProfilePicture1)
let myLoadedPic = Load.image("myPic")
let myImageArray = [myProfilePicture1,myProfilePicture2]
Save.imageArray("myPicArray", myImageArray)
let myLoadedPicArray = Load.imageArray("myPicArray")
Apple doc says:
You can also create an NSArray object directly from a Swift array literal, following the same bridging rules outlined above. When you explicitly type a constant or variable as an NSArray object and assign it an array literal, Swift creates an NSArray object instead of a Swift array.
A brief example here:
let imageArray: [AnyObject]? = NSUserDefaults.standardUserDefaults().arrayForKey("ImageArray")
if let array = imageArray{
var arrayImage: NSArray = array
}

Appending an array in swift, having problems

I am getting the error 'AnyObject' does not have a member named 'append' for results that I am trying to post to an array. I'm not sure how to fix the error, as I thought my code was correct. Can someone help me make the proper adjustments? My code is as follows:
import CloudKit
import UIKit
import Foundation
class Items {
var theSelfie: [String]
init(record: CKRecord) {
println("init")
var record: CKRecord
var iama: String
var youarea: String
var image: CKAsset? // tried also initializing the CKAsset separately
var postPic: UIImage?
let database = CKContainer.defaultContainer().publicCloudDatabase
let container = CKContainer.defaultContainer()
let publicDB = container.publicCloudDatabase
let data = CKRecord(recordType: "theUsers")
var predicate = NSPredicate(value: true)
let myQuery = CKQuery(recordType: "theUsers", predicate: predicate)
var mySelfie = theSelfie
publicDB.performQuery(myQuery, inZoneWithID: nil) { results, error in
if error != nil {
println(error)
} else {
for record in results{
let aselfie = data.objectForKey("selfie")
aselfie.append[mySelfie]()
return ()
}
}
}
}
}
}
The return type of objectForKey is AnyObject!
The type AnyObject doesn't have a method named append. You'll need to cast the return value to the correct type.
In addition, I don't think I see you ever put an entry in data with key "selfie". If you want it to be an array then somewhere you need to call data.setObject
You're getting aselfie from a dictionary. The dictionary returns objects of type AnyObject. Since you seem confident, that's it's an Array you can cast it to Array or NSArray.
let aselfie = data.objectForKey("selfie") as Array
I don't think that aselfie.append[mySelfie]() is valid Swift syntax. Does it even compile? Maybe you meant aselfie.append(mySelfie)?
Also, how do you know that aselfie is an Array? You should cast it:
let aselfie = data.objectForKey("selfie") as? Array<Whatever>
aselfie.append[mySelfie]()
return ()
}
You should cast aselfie to array then call its append method not its subscript
if let aselfie = data.objectForKey("selfie") as? [AnyObject] // if aselfie is supposed to hold object of single type case it as [ObjcetsToHold]
aselfie.append(mySelfie)
return ()
}

Saving And Loading Integers errors Swift

I am having a problem saving and loading up my difficulty integer. I have a button pushed to decide it on one view:
(This is one difficulty)
Difficulty + 1
let SecondDefaults: NSUserDefaults = NSUserDefaults.standardUserDefaults()
SecondDefaults.setObject(Difficulty, forKey: "Difficulty")
SecondDefaults.synchronize()
On another view under viewDidLoad for the loading:
let SecondDefaults: NSUserDefaults = NSUserDefaults.standardUserDefaults()
var difficulty = SecondDefaults.valueForKey("Difficulty")?.integerValue
SecondDefaults.synchronize()
Difficulty = difficulty!.stringByTrimmingCharactersInSet(NSCharacterSet.decimalDigitCharacterSet().invertedSet).toInt()!
You need to change:
var difficulty = SecondDefaults.valueForKey("Difficulty") as? String
to
var difficulty = SecondDefaults.valueForKey("Difficulty")?.stringValue
I don't know why you need to convert the integer to string. You only need the following code for getting an integer from NSUserDefaults no need to convert it to string.
let SecondDefaults: NSUserDefaults = NSUserDefaults.standardUserDefaults()
var difficulty = SecondDefaults.valueForKey("Difficulty")?.integerValue
Your issue lies on this line:
var difficulty = SecondDefaults.valueForKey("Difficulty") as? String
The problem is that the value may be a String or it may nil. Who knows? The as? keyword indicates that the cast may fail, so difficulty is a String optional, which you implicitly unwrap. Anything you unwrap as nil will give you a runtime error.
Assuming that Difficulty from your second code snippet is of type Int, this should work:
let defaults: NSUserDefaults = NSUserDefaults.standardUserDefaults()
let maybeDifficulty = defaults.valueForKey("Difficulty") as? Int
if let difficulty = maybeDifficulty {
Difficulty = difficulty
} else {
println("Difficulty is not an Int or may be nil")
}

Crash while getting value from the dictionary in Swift

I have created a dictionary like
var tempArray1 = ["sdds","dsads"]
var tempArray2: AnyObject = ["sddsa",34,tempArray1]
var dictionary: [String:Array] = ["key1":["value1"],"key2":["value2",6,tempArray2]]
The application crashed when I tried to print all values from the dictionary like
let allValues = [Array](dictionary.values)
for value in allValues{
println(value)
}
I just started learning dictionary concept in swift language. I want to know my approach is right or wrong.
Please help me to figure it out
As Swift arrays have associated I don't think that you can declare type with array without specifying its associated type. I am not sure why you do not get compile time errors. This should work:
var tempArray1 = ["sdds","dsads"]
var tempArray2: AnyObject = ["sddsa",34,tempArray1]
var dictionary: [String:Array<AnyObject>] = ["key1":["value1"],"key2":["value2",6,tempArray2]]
let allValues = [Array<AnyObject>](dictionary.values)
for value in allValues{
println(value)
}
Or even shorter:
var tempArray1 = ["sdds","dsads"]
var tempArray2: AnyObject = ["sddsa",34,tempArray1]
var dictionary: [String:[AnyObject]] = ["key1":["value1"],"key2":["value2",6,tempArray2]]
let allValues = dictionary.values
for value in allValues{
println(value)
}
You can try this also
var tempArray1 = ["sdds","dsads"]
var tempArray2: AnyObject = ["sddsa",34,tempArray1]
println("Array inside array \(tempArray2)")
var dictionary: [String:Array] = ["key1":["value1"],"key2":["value2",6,tempArray2]]
println(dictionary)
let allValues = Array(dictionary.values)
for value in allValues{
println(value)
}

Resources