I have an enum, and a variable that points to a certain object in the enum.
enum Collection {
case First, Second, Third, Fourth
}
var myCollection = Collection.Second
I want to pass myCollection to the NSUserDefaults. Here's what I did: (I hope viewDidLoad is the right place to put it.)
override func viewDidLoad() {
super.viewDidLoad()
// Save the sort by NSUserDefualt
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setObject(self.myCollection, forKey: "myKey")
}
At the last line, I get the following error:
Cannot invoke 'setObject' with an argument list of type '(myViewController.Collection, forKey: String)'
What am I doing wrong, and how can I fix it?
Update
Does this make sense?
let defaults = NSUserDefaults.standardUserDefaults()
let defaultRawValue = defaults.integerForKey("myKey")
if defaultRawValue != nil {
defaults.setInteger(myCollection.rawValue, forKey: "myKey")
} else {
defaults.setInteger(1, forKey: "myKey")
}
myCollection = Collection(rawValue: defaultRawValue)!
NSUserDefaults can only take PropertyList: https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/PropertyLists/AboutPropertyLists/AboutPropertyLists.html
Try
enum Collection:Int{
case First=1, Second, Third, Fourth
}
var myCollection = Collection.Second
override func viewDidLoad() {
super.viewDidLoad()
let defaults = NSUserDefaults.standardUserDefaults()
let defaultRawValue = defaults.integerForKey("myKey")
if defaultRawValue > 0{
myCollection = Collection(rawValue: defaultRawValue)!
}
else
{
defaults.setInteger(1, forKey: "myKey")
myCollection = Collection(rawValue: 1)
}
}
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.
So, if you want to store your enum in NSUserDefaults, you use rawValue for your enum. Here I use String, like this:
enum Collection: String {
case First = "First"
case Second = "Second"
case Third = "Third"
case Fourth = "Fourth"
}
//create myCollection
var myCollection = Collection(rawValue: "Second")
And store it:
override func viewDidLoad() {
super.viewDidLoad()
// Save the sort by NSUserDefualt
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setObject(self.myCollection.rawValue, forKey: "myKey")
}
Related
Whenever I open my app, it doesn't load my array values because the != nil function isn't called. Is there anything I can do about this?
Code:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
var toDoData = NSUserDefaults.standardUserDefaults()
if (toDoData.valueForKey("TDDATA") != nil){
todos = toDoData.valueForKey("TDDATA") as! NSArray as! [TodoModel]
}
if todos.count != 0{
toDoData.setValue(todos, forKeyPath: "TDDATA")
toDoData.synchronize()
}
}
Don't worry about the table. It populates perfectly. I just need the loading data issue fixed.
Code included in your answer helps a lot!
Thanks.
UPDATE:
Here is the TodoModel:
import Foundation
import UIKit
class TodoModel : NSObject, NSCoding {
var id: String
var image: String
var title: String
var desc: String
var scores: String
init (id: String, image: String, title: String, desc: String, scores: String) {
self.id = id
self.image = image
self.title = title
self.desc = desc
self.scores = scores
}
}
valueForKey and setValue:forKeyPath are KVC (Key Value Coding) methods (read here and here). It will not help you read/write to the user defaults database.
Looking in the NSUserDefaults documentation, there are a number of methods available for getting and setting values in the defaults database. Since you are using arrays, we will use:
arrayForKey to get.
setObject:forKey to set. (There is no array-specific setter)
EDIT: Try this in your viewDidAppear. Here we check if we have data, and if we do, we store it. If we don't have data, then check if the defaults database has some saved. If it does, use it instead. It would be advantageous to only load data from the defaults database in viewDidLoad, and then save in viewDidAppear or even better, a function which is called when a todo is added.
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
let defaults = NSUserDefaults.standardUserDefaults()
if todos.count > 0 {
// Save what we have
let data = NSKeyedArchiver.archivedDataWithRootObject(todos)
defaults.setObject(data, forKey: "TDDATA")
defaults.synchronize()
print("saved \(todos.count)")
} else if let storedTodoData = defaults.dataForKey("TDDATA"),
storedTodos = NSKeyedUnarchiver.unarchiveObjectWithData(storedTodoData) as? [TodoModel] {
// There was stored data! Use it!
todos = storedTodos
print("Used \(todos.count) stored todos")
}
}
In addition, we must implement the NSCoding protocol in your model. This should be something like this:
class TodoModel: NSObject, NSCoding {
var myInt: Int = 0
var myString: String?
var myArray: [String]?
required init?(coder aDecoder: NSCoder) {
myInt = aDecoder.decodeIntegerForKey("myInt")
myString = aDecoder.decodeObjectForKey("myString") as? String
myArray = aDecoder.decodeObjectForKey("myArray") as? [String]
}
func encodeWithCoder(aCoder: NSCoder) {
aCoder.encodeInteger(myInt, forKey: "myInt")
aCoder.encodeObject(myString, forKey: "myString")
aCoder.encodeObject(myArray, forKey: "myArray")
}
}
(Of course, replace myInt, myString, myArray, etc, with whatever properties your model might have.)
I am trying to share [[String:String]] object between my app and Today Extension.
In my app, I am saving an object like this:
override func viewWillDisappear(animated: Bool) {
// Save all data and update Today Extension
// 1) Save selectedStations as Dictionary to NSUserDefaults
if var sharedDefaults = NSUserDefaults(suiteName: "group.xxx.xxx") {
// Save to userDefaults (1) stationName, (2) stationLine, (3) id, (4) statnId, (5) subwayId
var arrayOfDic:[[String:String]] = []
for station in selectedStations {
// Organize data to pass to widget
let name: String = station.name ?? ""
let line: String = station.line ?? ""
let id: String = station.id ?? ""
let statnId: String = station.statnId ?? ""
let subwayId: String = station.subwayId ?? ""
// Create dictionary
let dic = [
"name" : name,
"line" : line,
"id" : id,
"statnId" : statnId,
"subwayId" : subwayId
]
// Append to array
arrayOfDic.append(dic)
}
// Save to NSUserDefaults
sharedDefaults.setObject(arrayOfDic, forKey: "selectedStations")
println(" saved:\(arrayOfDic)")
sharedDefaults.synchronize()
}
// 2) Refresh Today Extension
}
And within the same .swift file, I am retrieving the data in 'ViewDidLoad' like this:
override func viewDidLoad() {
super.viewDidLoad()
if var sharedDefaults = NSUserDefaults(suiteName: "group.xxx.xxx") {
sharedDefaults.synchronize()
if let arrayOfDic = NSUserDefaults().objectForKey("selectedStations") as? [[String:String]] {
println(" fetched:\(arrayOfDic)")
}
}
}
But I realize that
println(" saved:\(arrayOfDic)")
logs what I am trying to save, but
println(" fetched:\(arrayOfDic)")
is not logging anything. This part seems to be nil:
if let arrayOfDic = NSUserDefaults().objectForKey("selectedStations") as? [[String:String]]
Is my code wrong??
The problem is in this line of code:
if var sharedDefaults = NSUserDefaults(suiteName: "group.xxx.xxx")
Think again what are you actually doing in here? You are creating a new instance of NSUserDefaults. While what you need is to get reference of the one created in viewWillDisappear method.
My suggestion would be is to implement
sharedDefaults = NSUserDefaults(suiteName: "group.xxx.xxx") outside of both functions (make it lets say instance variable)
and later on in viewDidLoad implement the following:
if let sharedDefaults = sharedDefaults
instead of sharedDefaults = NSUserDefaults(suiteName: "group.xxx.xxx")
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
}
I have a textField for the user to input their name.
#IBAction func nameTextField(sender: AnyObject) {
let defaults = NSUserDefaults.standardUserDefaults()
defaults.setObject("\(nameTextField)", forKey: "userNameKey")
}
Then I recall the inputted name in ViewDidLoad with:
NSUserDefaults.standardUserDefaults().stringForKey("userNameKey")
nameLabel.text = "userNameKey"
What am I doing wrong? Result is simply "userNameKey" every time. I'm new to this, thanks!
You just have to assign the result returned by nsuserdefaults method to your nameLabel.text. Besides that stringForKey returns an optional so I recommend using the nil coalescing operator to return an empty string instead of nil to prevent a crash if you try to load it before assigning any value to the key.
func string(forKey defaultName: String) -> String?
Return Value For string values, the string associated with the
specified key. For number values, the string value of the number.
Returns nil if the default does not exist or is not a string or number
value.
Special Considerations
The returned string is immutable, even if the value you originally set was a mutable string.
You have to as follow:
UserDefaults.standard.set("textToSave", forKey: "userNameKey")
nameLabel.text = UserDefaults.standard.string(forKey: "userNameKey") ?? ""
What you need to do is:
#IBAction func nameTextField(sender: AnyObject) {
let defaults = NSUserDefaults.standardUserDefaults()
defaults.set(yourTextField.text, forKey: "userNameKey")
}
And later in the viewDidLoad:
let defaults = NSUserDefaults.standardUserDefaults()
let yourValue = defaults.string(forKey: "userNameKey")
nameLabel.text = yourValue
EDIT:
I tried to set up NSUserDefaults last night but an error keeps occurring:
ViewController3:
save data
#IBAction func addButtonTapped(sender: UIButton) {
var userDefaults:NSUserDefaults = NSUserDefaults.standardUserDefaults()
var itemList:NSMutableArray? = userDefaults.objectForKey("exercisesList") as? NSMutableArray
var dataSet:NSMutableDictionary = NSMutableDictionary()
dataSet.setObject(textField.text, forKey: "exercises")
if ((itemList) != nil){
var newMutableList:NSMutableArray = NSMutableArray();
for dict:AnyObject in itemList!{
newMutableList.addObject(dict as NSDictionary)
}
userDefaults.removeObjectForKey("exercisesList")
newMutableList.addObject(dataSet)
userDefaults.setObject(newMutableList, forKey: "exercisesList")
}else{
userDefaults.removeObjectForKey("exercisesList")
itemList = NSMutableArray()
itemList!.addObject(dataSet)
userDefaults.setObject(itemList, forKey: "exercisesList")
}
userDefaults.synchronize()
self.view.endEditing(true)
textField.text = ""
}
ViewController1:
load data
var exercises:NSMutableArray = NSMutableArray();
override func viewDidAppear(animated: Bool) {
var userDefaults:NSUserDefaults = NSUserDefaults.standardUserDefaults()
var itemListFromUserDefaults:NSMutableArray? = userDefaults.objectForKey("itemList") as? NSMutableArray
if ((itemListFromUserDefaults) != nil){
exercises = itemListFromUserDefaults!
}
}
Now I wanted to use the loaded data for UIPickerView
func pickerView(pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String! {
switch pickerView {
case pickerView1:
return exercises[row] as String
case pickerView2:
return reps[component][row]
case pickerView3:
return weight[component][row]
default:
assertionFailure("Unknown pickerView")
}
}
But at the point where the UIPickerView should return the exercises it is empty. Need some help here.
I think I've got this.
In your code, you read exercises from UserDefaults in viewDidAppear, which will be called after func pickerView(...) -> String! is called. That's why in func pickerView(...) -> String! the exercises is always empty.
To do this correctly, you need to initialize exercises in init() or viewDidLoad()
Don't use NSUserDefaults to pass variables.
Add a public property (in the .h file) to destinationViewController and set that property with the value (the array) in prepareForSegue.
To save the array between app launches do as #Martin suggests, writing the array to the Documents directory.There are many answers here with that information.
How to store your arrays using NSUserDefaults is pretty simple and I encourage you to try it.
var exercises = ["A","B","C"]
NSUserDefaults.standardUserDefaults().setObject(exercises, forKey: "exercises")
How to load your string arrays using NSUserDefaults
if let myLoadedStringArray = NSUserDefaults.standardUserDefaults().stringArrayForKey("exercises"){
println(myLoadedStringArray) // "[A, B, C]"
}
// Note: myLoadedStringArray has no value outside curly brackets
How to delete/reset a value stored using NSUserDefaults
NSUserDefaults.standardUserDefaults().removeObjectForKey("exercises")
println(NSUserDefaults.standardUserDefaults().stringArrayForKey("exercises")) // "nil"
If you prefer to store it as a file at the documents folder you can do as follow
let fileUrl = (NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first! as NSURL).URLByAppendingPathComponent("exercises.db")
(exercises as NSArray).writeToURL(fileUrl, atomically: true) // true
// forced unwrap if you are sure it won't return nil
let loadedArray = NSArray(contentsOfURL: fileUrl)! as [String]
// or use if let to safely unwrap your array
if let loadedArray = NSArray(contentsOfURL: fileUrl) as? [String] {
println(loadedArray) // "[A, B, C]"
}