How would you be able to save a custom object to NSUserDefaults??
NSUserDefaults.standardUserDefaults().setObject(obj, forKey: "object")
NSUserDefaults.standardUserDefaults().synchronize()
The object is of the SPTAuthViewController class in the Spotify iOS SDK, so I can't edit it to add encoder and decoder methods. How can I encode the object into an NSData object to store it in NSUserDefaults?
You can't store custom objects into user defaults. You can only store "property list objects" (NSString, NSData, NSDate, NSNumber, NSArray, or NSDictionary objects).
In order to save a custom object to user defaults you have to convert it to one of those types.
You can make your custom object conform to the NSCoding protocol. To do that you have to implement the init(coder:) and encode(coder:) methods. (I believe that's the swift form of the method names - I've been working in Objective-C lately so my Swift is getting rusty.) How you implement those methods depends on your class. Typically you make multiple calls to encodeObject:forKey: to encode your object's properties.
Once your custom object conforms to NSCoding you can convert it to NSData using the NSKeyedArchiver method archivedDataWithRootObject(). You then convert it from NSData back to the original object using the NSKeyedUnarchiver method unarchiveObjectWithData().
EDIT:
I just realized that you were trying to save a view controller object (SPTAuthViewController to user defaults. You should not serialize and save view controller objects. Controller objects should not be used to save application data.
Related
I declared two attributes (currentTime and fullTime) as Transformable in data model as shown below.
How to save data in this attribute? Do I need to convert to NSData first? or any other way?
Transformable need to be convertible to NSData. When you're using a type that conforms to the NSCoding protocol, that happens automatically. When you're not (as with CMTime), you can't use a transformable unless you create your own custom transformer by subclassing NSValueTransformer.
You may find it easier to simply save the CMTime properties in Core Data and reconstruct the CMTime from those. The properties are all numeric types that Core Data knows how to handle.
I have a custom class with which i made an (swift-y) struct NSCodable. Now I want to convert that into a CKRecord in order to use CloudKit. Even though I set 'key value'-pairs when encoding my struct, it is in my understanding that the struct is converted into NSData and that you can't convert it to a Dictionary (or another key-value object). So I get the feeling that this is not the way to go.
Is there a way to make this conversion directly? Or with a step in-between (for instance converting the Data into a [String: String]- dictionary)?
NSCoding is only for going from/to the same class. Instead, write a toServerDictionary method on your custom class, including only the properties you want to send to CloudKit, and then use the result to call setValuesForKeys on a CKRecord.
You'll likely find there are properties that need to be specific types, in which case its better to make your method toServerRecord and create a CKRecord and return it. You can also have a updateWithServerRecord to set anything you receive back.
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 would like to add a NSDictionary into a NSManagedObject Category class (or the NSManagedObject class itself)
When I do this, and I try to access the property, an exception is thrown.
Is this actually possible? I can't add this property as transient in the model because there is no NSDictionary Data Type, of course.
Thanks!
You don't say how you have currently created the property or what the exception is, but from the description you give it sounds like you should be setting the attribute in the Core Data model to be transformable. Setting it to be transformable will cause the NSDictionary to be archived (and unarchived) as you use it using the standard NSCoding protocol. Be sure that everything you put into the dictionary supports the NSCoding protocol so that it is properly archived and restored.
Using transformable is the way. Below are few more insights on the transformable property.
The Transformable data type is a special data type that allows us to
create attributes based on an Objective-C class (custom objects). This
data type is heavily used for storing instances of UIImage, UIColor,
and so on. As the information stored in the persistent store has to be
in the form of NSData instance, while using Transformable data type,
we need to create Value Transformers to convert the custom object
(information in attribute of Transformable data type) into an instance
of NSData (before storing in the persistent store) and to convert the
instance of NSData back to custom object while retrieving from the
persistent store.
I have a question about NSKeyedUnarchiver, NSMutableArray and self-created classes. The data is stored by NSKeyedArchiver to user defaults. When the data load into the user defaults will be printed to the console of the data stream. I want to unpack the data into an array, the array will be empty.
Maybe someone of you knows how I save the array or the array load.
Thank you.
Greetings,
Schumi
You should go through this documentation first. But basically, To enable archiving on custom class objects you will have to adopt the NSCoding protocol and implement initWithCoder: and encodeWithCoder: methods.