Saving an NSMutableArray to Core Data - iphone

I want to add an NSMutableArray of NSStrings to one of my Entities in my core data model. The problem is that this isn't a supported type in Core Data.
I tried making a tranformable attribute, but the problem is that I see no way of turning a NSMutableArray to NSData, and then going from NSData, back to an NSMutableArray. Does anyone have an idea as to how this issue can be solved?
(I know I can archive the array, but I don't want to do that, I want it to be present in my model).

You could have a binary data attribute in your modeled object, archive the array to data, and hand it off to the object.
But I think the better way would be to have a to-many relationship, instead of using an array directly.
****Edit: Here's how to archive the array into NSData so that it can be used in your managed object***
NSData *arrayData = [NSKeyedArchiver archivedDataWithRootObject:[NSArray arrayWithObjects:#"1",#"2", nil]];
Basically, any class you have which conforms to the NSCoding protocol can be archived in this way. NSArray/NSMutableArray already conform to it. They tell all of their objects to archive themselves, so they must conform too. And all of those objects' members must conform, etc. It's like a tree.
Since your array conforms, and it's an array of NSString (which also conforms), then you're golden.

Related

How to use my Class with PList in objective-c?

I have a Class for handling my data in my project, and now I need to store the data.
I'd like to use a Plist but I'm a bit unsure of how to start.
My class is pretty simple - 6 pieces of data, flat (no hierarchy).
I want my app to start with no data, so can I assume that I should create the PList programmatically once the User creates their first piece of data? (That is, don't create a .plist file in 'Supporting Files' prior to distribution?)
Then, when the app starts the next time, read the data and create an NSMUtableArray array of Class instances?
To create a property list, all you need to do is use appropriate types (i.e. those that support the property list format: NSData, NSString, NSDictionary, NSNumber, NSDate, NSArray), store them in a single container, and tell the containing object to write itself to a file. To read the data, you can initialize that same type using a path. For example:
// writing some data to a property list
NSString *somePath = ... // replace ... with the path where you want to store the plist file
NSMutableDictionary myDict = [NSMutableDictionary dictionary];
[myDict setObject:#"Caleb" forKey:#"name"];
[myDict setObject:[NSNumber numberWithInt:240] forKey:#"cholesterolOrIQ"];
[myDict writeToFile:somePath atomically:YES];
// reading the file again
NSDictionary *readDict = [NSDictionary dictionaryWithContentsOfFile:somePath];
The simplest way is to simple save an NSArray or NSDictionary to disk. Caleb's answer goes into detail there so I won't repeat it, other than to say you might have to convert a non-compatible object like NSColor to an property list object like NSData. It's up to you to do this each time you save or load your data.
NSKeyedArchiver and NSKeyedUnarchiver give you a little more control over the process, but work pretty much the same way. You provide (or get back) a plist compatible root object (usually an NSDictionary) that contains your data. I recommend creating a dictionary that includes your data structure as well as an arbitrary number (your app's build number is a good choice) to use as a version indicator. This way if you ever update your data model you can easily determine if you need to do anything to convert it to the new version.
If you're putting your own objects into the data file, look into NSCoding. The protocol gives you two methods using NSKeyedArchiver and NSKeyedUnarchiver to save and restore your data. This is by far the most straightforward approach if your data model consists of anything more than a few simple strings and numbers, since you're dealing with your own native objects. In your case, you would have your data class implement NSCoding and use the NSKeyedArchiver and NSKeyedUnarchiver methods to encode your six instance variables. When it's time to save or load, pack the instance of your class into an NSDictionary (along with a versioning number as I mentioned above) and call NSKeyedArchiver's archiveRootObject:toFile:. Your save an load methods deal only with your own data object, which makes things easy for you. The common pitfall to watch out for here is if your custom data object contains other custom object. This is fine, but you have to make sure every object that's going to be saved has its own NSCoding implementation.
Two things you can do:
Use NSUserDefaults:
http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSUserDefaults_Class/Reference/Reference.html
The objectForKey method is the one you want to use to store your class. But, as pointed out in the comments, this shouldn't really be used for storing lots of user data; it's best for saving preferences.
For storing more data, you might want to look at Core Data. It's more complex, but should be better suited to your needs. Here's a tutorial on it:
http://mobile.tutsplus.com/tutorials/iphone/iphone-core-data/
Neither of these seems best for your simple application, but I leave this answer up since it gives alternatives for saving data to the iPhone.

Adding Instance Variable to NSData

So, I understand that Categories in Objective-C can be used to add methods to classes without the need for subclassing. I also understand that these Categories cannot be used to add instance variables to classes.
I did a little bit of reading about Class Extensions, which can be used to add instance variables, but I don't understand how I can use Class Extensions to modify an existing class such as NSData.
My problem is the following:
I have a Core Data Model that contains a NSURL and NSData. The NSData displays the data for the NSURL. When a view needs to display the data, I do the following check:
--- If [NSData bytes] > 0, display the NSData.
--- Otherwise, fetch the data at NSURL and display the data when it returns
Simple enough. However, I run into problems when the NSURL is updated. So, if I modify the NSURL path with a new image, because [NSData bytes] is already greater than 0, I don't make the additional call to fetch the new image.
What I would like to do is add an instance variable to NSData called URLKey that would hold information about where the data comes from. I can't subclass NSData because I'm using CoreData.
Does anyone know some simple solutions for this? Perhaps there's a gap in my understanding of Class Extensions, or maybe there's just no simple way.
Class Extensions should be used on classes you implement yourself as a way of keeping ivars and some properties hidden from the header File, that should contain only stuff that should be visible outside the class (and ivars are't that kind of stuff).
Categories are used on classes already implemented, as a way of adding additional functionality. They are usually needed when you want to add a general kind of behavior to a known Class. E.g. adding a method to NSString +(NSString*)reversedString; that returns a reversed instance so you can then use it like this:
NSString *someString = #"string";
NSString *reverse = [someString reversedString];
NSLog(#"%#", someString); //this would output "gnirts"
.
Regarding your particular problem, I can assure you that your CoreDataModel does not contain NSURL or NSData. The supported types are primitives, strings, binary Data and transformables. So, if you want to, you can subclass NSData or NSURL and then use it with CoreData by setting the type to "transformable". And after you have done this, you can then subclass NSData as you wish and use class extensions in the process, or just use a category to add the methods you require to the class.
Quote from Apple about transformable attributes:
The idea behind transformable attributes is that you access an
attribute as a non-standard type, but behind the scenes Core Data uses
an instance of NSValueTransformer to convert the attribute to and from
an instance of NSData. Core Data then stores the data instance to the
persistent store.

Saving custom data types

Im making an app similar to apples weather app. My main view has a NSMutableArray of objects to be displayed. This array is managed by the user. I want to be able to save this array so that when the app is relaunched it has the stuff the user selected. The objects stored are "Event" objects which is a custom object type that stores more custom objects that all hold some of the following: NSString, NSNumber, NSUInteger, NSURL. My research has led me to three options, none of which i understand, nor know how to implement: saving to a plist using writeToFile, archiving the data, or saving it to userPrefs.
Does anybody know of a solution to my problem?
Sample data object:
`
#interface Rider : NSObject {
NSString *name;
}
-(Rider *) initWithName:(NSString *) nam;
- (NSString *)description ;
#property (nonatomic, retain) NSString *name;
`
At least two of those three options are reasonable. NSUserDefaults is really meant more for storing application preferences than as an option for storing the bulk of your data, so if you have a lot of data you should look at other possibilities first.
Property lists are easy to read and write, but you're limited to using a handful of standard types: NSString, NSData, NSArray, NSDate, and NSNumber. You can do quite a lot with those types, but since your own Event class is involved using property lists won't be so simple.
Archiving seems like the best plan. All you need to do is to adopt a simple protocol, NSCoding, in your custom classes (and make sure that the other classes that you use, like NSArray, also implement NSCoding). Then, create an instance of NSKeyedArchiver and ask it to archive your object graph. Recreating the object graph later is just as simple: create an NSUnarchiver instance with your file's data and unarchive your objects.
You can read about both archiving and property lists in the Archives and Serializations Programming Guide. Read that document before you go any farther.
There are other options as well -- you can always use the standard C file operations to write directly to a file if you want. It's unlikely that that will be as convenient as archiving, however.
Your question itself has the answer.
Create each of your object as dictionary and finally you will have array of dictionaries.
NSArray and NSDictionary has the methods to write its data to a file (plist is preferred). If you use plist files, it will be easy to read again.

problem in nsuserdefaults

hii every one i have stil problem in nsuser defaults i'll tell the scenerio in detail
First i have diclared nsmutable array in appDelegate and set it in NSUserDefaults With For Key#"abc"
In FirstView Controller i first fetch the array from NSUserDefaults and save its values in NSMutable Array
When a Click say abcButton i have create a dictionary and adding values in it like
[abcDictionar setObject:[[abcMutableArray objectAtIndex:indexPath.row] objectForKey:#"abc"] forKey:#"abc"];
When i added all values in NSDictionary Then i add NSDictionary in NSMutable Array
like This
[abcMutableArray addObject:abcDictionary];
Then i save it NSUserDefaults
It Give me Exception in Point 4
When i add nsdictionary in Point One It All work fine but data in array is ambigous and it raise exception when am going to display it in tableview
if any one has some idea then let me know thanks in advance...:)
NSUserDefaults does not store mutable objects, only immutable ones. When you retrieve objects from it you must cast/copy them into mutable objects if you wish to mutate them.
Also be aware that:
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. If you want to store any other type of object, you should typically archive it to create an instance of NSData.
Which is from the NSUserDefaults documentation.

OSX: How to store objects in plist file in an OO way

I feel a little dumbfounded. I know how to store any kind of "plist supporting" data in a preferences file, such as NSArray, NSDictionary, NSString and their CF counterparts. No problem there.
Now I like to store the values of my custom object this way. I had thought that if I implement some kind of serialization/archiving protocol, I could get NSUserDefaults to understand my class just like it understand NSDictionary.
E.g, implementing the NSCoding protocol should give the NSUserDefaults code all that it needs: I give it the key names along with the values as plist compatible types (NSString, mostly in my case). But my encoder doesn't even get invoked. :(
Then I thought that there must be at least some function that generates a NSDictionary from the NSCoding protocol, so that I can then send this dict to store in the prefs. Ideally, there'd by something like the NSKeyedArchiver that I pass any NSCoding compatible object and it gives me a NSDictionary, and vice versa. But that doesn't appeat to exist in Apple's framework.
Do I have to write that really myself? I'd expect this would be a quite common need.
Note: I realize that NSKeyedArchiver generates a binary plist, which I could write as a plist file. But that's not what I want. I want to add the contents of this object to my app's prefs plist file, i.e. I want to store both my object and other prefs data in the plist file. That's what doesn't seem to be possible with the given functions.
I'm currently just adding objects to an NSDictionary then calling NSDictionary's "writeToFile:atomically:" method. I'm mostly adding strings, and an image as NSData, but I believe anything I add to the NSDictionary that implements the NSCoding protocol should get written to the file.
Then later when I call NSDictionary's initWithContentsOfFile everything gets put back in the dictionary as it had been. Would this work for you - letting the dictionary take care of the serialization stuff?