So, I was wondering if it is possible to save a function to user defaults using Swift. For a string, for example, I would do this:
var inputData = "Hi"
NSUserDefaults.standardUserDefaults.setValueForKey(inputData, forKey: "data")
Then to save it do a variable, I would say:
var gotData = NSUserDefaults.standardUserDefaults.valueForKey("data")!
Now, my question is, if it is possible to save a function to user defaults. For example:
var inputData = func helloWorld(){println("Hello World!")}
Is it possible to then call that function that was saved to user defaults by doing something similar to .valueForKey ?
Any help is valued! Thanks a lot in advance!
You cannot save a function to NSUserDefaults. From the NSUserDefaults Class Reference:
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.
A function is not any of the supported types, and as far as I know you cannot archive a function either.
Related
I've having difficulty saving an NSManagedObject in userDefaults and I'd like to know a) should I be trying to do this or is this not an appropriate approach or b) if it is an appropriate approach, how can I get it to work?
I'm writing my app in Swift 2.3 and it has a few user default options, one of which is for a default "lift" (as in weightlifting, e.g. 'bench press', 'clean and jerk', 'incline bench press'). I'm actually converting them from an enum to a Core Data entity because every lift event that the user will be able to keep track of will be one of the available lifts types (for which I'll establish the appropriate relationship).
Here's the extension with the properties:
extension Lift {
#NSManaged var liftName: String
#NSManaged var type: NSSet
}
and the Lift entity with the things Xcode is complaining about:
class Lift: NSManagedObject, NSCoding {
func encodeWithCoder(aCoder: NSCoder) {
aCoder.encodeObject(liftName, forKey: "liftName")
} // Super.init isn't called on all paths before returning from initializer
required init(coder aDecoder: NSCoder) {
// Initialization of immutable variable was never used, etc...
let liftName = aDecoder.decodeObjectForKey("liftName") as! String
}
}
I've dealt with these types of errors before so my real concern is whether or not I'm headed down the wrong path.
I've read numerous threads tonight which have taught me that I'll need to encode an object (but not specifically an NSManagedObject) to save it then unencoded it when retrieving it and that my class must conform to NSCoding and what that protocol requires. But then I've seen threads that say NSManagedObjects should NOT be stored in userDefaults, but I don't know if that's true.
I've spent a few hours on this so before I go further, can/should this be done?
No, you should not store an NSManagedObject in NSUserDefaults. Core Data is an object persistence technology, so it doesn't make sense to try and persist an NSManagedObject in some other format.
There are few alternatives you can use:
You could simply store the name of the lift in your user defaults and then query for this to get the object back from Core Data. This may not work for you if lift names aren't unique
You can add an identifier attribute to your Lift entity and store something like a UUID string in that attribute; You can then store the same string in UserDefaults. This will ensure one object is selected uniquely.
You can add a new boolean attribute to your Lift entity default and again use a query to retrieve it; You would need to ensure you only set the value to true on one lift at a time.
You can use managedObject.objectId.uriRepresentation to get a URL that you can store as a string and then use to retrieve the object later. This is probably the most complex solution and I would suggest you try one of the other options.
No, You cannot do this. You cannot save Core Data objects in User Defaults. It can only save in its DB.
This two are totally two different things. NSUserDefault stores the light pieces of data where NSManagedObject stores the light or heavy amount of data and is very fast than NSUserDefault for storing and retrieving purposes.
NSManagedObject -> NSManagedObject link to coredata.
You should it to store a large list of elements. As far your last question, there is nothing preventing you from using both Core Data and a backend to store your data. In fact, there are frameworks out there to facilitate exactly this.
NSUserDefaults -> NSUserDefaults is a class that allows simple storage of different data types. It is ideal for small bits of information you need to persist between app launches or device restarts. NSUserDefaults is not sufficient and reliable to store and query the huge amount of data. It's suggestable if you'll have a backend (database on the server) to store events and their invitees to persist consistency of user's information (if user logged in back to your app from other app supportive device then he'll get all information he stored).
NSUserDefaults supports the following data types:
NSString, NSNumber, NSDate, NSArray, NSDictionary and NSData
Hope it will help you.
Storable Types in NSUserDefaults.
The NSUserDefaults class acts very much like something called a Property List (aka plist). It may be just a fancy interface for a plist, or it may be more, I’m not entirely sure. Nonetheless, plists are limited in what kind of objects they can store. The six types plists can store are:
NSData
NSString
NSNumber
NSDate
NSArray
NSDictionary
So,you need to use in NSKeyedArchiver.
let ArchvieArr = NSMutableArray()
ArchvieArr.addObject(NSKeyedArchiver.archivedDataWithRootObject(LiftObj)).
NSUserDefaults.standardUserDefaults().setObject(ArchvieArr, forKey: "savedArray")
To avoid being an XY problem, here's some background:
My app allows users to create and save a lot of settings kinda like the Xcode's fonts and colors chooser:
This is because there are a lot of things that users can set. It would be easier to just tap on a saved setting instead of setting all those things again.
I used Core Data to store the settings the user saved. Each setting the user created is an instance of an NSManagedObject subclass. And now I need to store the selected setting persistently so that the user will have the same setting selected as before when the app reopens.
My first thought was to store the NSManagedObject subclass instance in NSUserDefaults. But according to the docs, I can't store it unless I convert it to NSData:
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.
Then I tried storing the objectID of the NSManagedObject subclass instance. I see that there is a URIRepresentation() method that returns an NSURL. So I thought this would work:
NSData(contentsOfURL: selectedOption!.objectID.URIRepresentation())
But the initializer failed somehow.
Now I realize that this is a stupid idea because even if I can convert it to NSData, I can't convert NSData back to NSManagedObjectID!
According to this question, the OP seems to be able to store the object id:
I store the selected theme objectID in NSUserDefaults so that when the app restarts, the selected theme will still be intact.
How can I do that?
NSUserDefaults has "convenience methods"
public func setURL(url: NSURL?, forKey defaultName: String)
public func URLForKey(defaultName: String) -> NSURL?
which allow to store and retrieve a NSURL like the one obtained
by URIRepresentation(). The conversion to and from NSData
is handled transparently. From the documentation:
When an NSURL is stored using -[NSUserDefaults setURL:forKey:], some adjustments are made:
Any non-file URL is written by calling +[NSKeyedArchiver archivedDataWithRootObject:] using the NSURL instance as the root
object.
...
When an NSURL is read using -[NSUserDefaults URLForKey:], the following logic is used:
If the value for the key is an NSData, the NSData is used as the argument to +[NSKeyedUnarchiver unarchiveObjectWithData:]. If the NSData can be unarchived as an NSURL, the NSURL is returned otherwise nil is returned.
...
So saving the managed object ID is simply done as
NSUserDefaults.standardUserDefaults().setURL(object.objectID.URIRepresentation(),
forKey: "selected")
and retrieving the object ID and the object for example like this:
if let url = NSUserDefaults.standardUserDefaults().URLForKey("selected"),
let oid = context.persistentStoreCoordinator!.managedObjectIDForURIRepresentation(url),
let object = try? context.existingObjectWithID(oid) {
print(object)
// ...
}
For alternative approaches of saving the selected settings, see the above comment.
I just learned how to store an array into a Parse Cloud using the example provided by the Parse Documentation:
gameScore.addUniqueObjectsFromArray(["flying", "kungfu"], forKey:"skills")
gameScore.saveInBackground()
Now, utilizing this logic, I want to append strings into the array. So this is what I wrote:
#IBAction func requestButtonPressed(sender: AnyObject) {
var prayerRequests = PFObject(className: "PrayerRequests")
prayerRequests.addObject(["YOIDJFO"], forKey:"skills")
prayerRequests.saveInBackground()
}
Now, after having executed the function requestButtonPressed three times, in parse this is happening:
However. I don't want that to happen when I execute the function requestButtonPressed three times. I want it to be something like this:
Anybody have a solution to this problem?
Every time you use this statement var prayerRequests = PFObject(className: "PrayerRequests") a new PFObject will be created. In order to update a object you need to query the object first and then update its field. In your case you should first get the array by querying for the object, modify / append data to the array and then update the object.
Instead of doing addObject, do insertObject:{yourObject} atIndexPath:{storingPosition} forKey:{#"youKey"}.
And the the value you are adding is an array ["YOIDJFO"] , object should be like {"YOIDJFO"}
I have a super simple app idea that I'm building out in order to get more practices at using Swift. I just want to ask if using NSUserDefaults is the most appropriate choice. All I will have is list of names with a number associated with each name that I will update (the number) from time to time. I was going to use a NSDictionary for the data.
I think Core Data is over kill for something like this.
I just wanted to get a 2nd opinion on my idea on how to save the data for this simple app.
Thanks
You can use NSUserDefault. But If want to store an array or dictionary you have to do some extra work. The documentation says:
The value parameter can be only property list objects: NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary. For NSArray and NSDictionary objects, their contents must be property list objects.
You can convert your NSDictionary to NSData, then save it to NSUserDefault.
// Convert the dictionary and store
var data = NSKeyedArchiver.archivedDataWithRootObject(yourDict)
NSUserDefaults.standardUserDefaults().setObject(data, forKey: yourKey)
// Retrieving the dictionary
var savedData = NSUserDefaults.standardUserDefaults().dataForKey(yourKey)
var dict = NSKeyedUnarchiver.unarchiveObjectWithData(savedData)
Hope this helps.. :)
If it's just one NSDictionary that should be fine. NSUserDefaults is good for small things like that.
NSUserDefaults.standardUserDefaults().setObject(**YOUR DICTIONARY**, forKey: **YOUR KEY**)
I am using Parse.com as a backend server, and am uploading data. I have a PFObject called person which has a column called 'Email' of Boolean type. I have a Core Data object with a BOOL property called email, which is set by: [NSNumber numberWithBool:[self.email isOn]];. When I try to set the PFObject as follows:
Person[#"email"] = [person.email boolValue];,
it gives me an error saying I am assigning a bool to id (Person is a PFObject). Am I doing something wrong?
What is the type of a PFObject's email property?
The fact that you can send it a boolValue message makes me think it might be an NSNumber. (Either that or NSString)
If it's already an NSNumber, you don't have to do anything. Simply assign the person.email directly to your destination key/value pair:
aPerson[#"email"] = person.email;
If person.email is a string, then you could use:
aPerson[#"email"] = #([person.email boolValue]);
Which would convert person.email to a bool, and then create an NSNumber using that bool.
BTW, you should not name variables, properties, or methods starting with an upper case letter. Objective-C has the strong convention that only classnames should start with an upper case letter.
I also cringe at having 2 different variables that only differ based on case ("Person" and "person"). That's a typing mistake away from a future bug.
According to the iOS Documentation, boolValue returns a primitive BOOL. id is not primitive, and can only be assigned objects such as NSNumber.
When using Parse, I normally send NSNumbers up to the server, as you cannot send BOOLs. Try writing this instead.
Person[#"email"] = [NSNumber numberWithBool:self.email];
This will send the NSNumber value up to the server, and when you need to retrieve it in BOOL form, just use NSNumber's boolValue