NSUserDefaults vs NSCoding - ios

When saving small amounts of data from within my App is it better to use NSUserDefaults or NSCoding?
Right now I use NSCoding (encodeWithCoder/initWithCoder, etc.) but it appears that NSUserDefaults might be simpler.
My total data is about a variety of Ints/Strings/MutableArray, only about a few dozen total.

I assume that by NSCoding you mean "saving objects to files after serializing them with NSCoding APIs". Although both approaches are valid for primitive data types, the NSUserDefaults approach gets more difficult once you start serializing objects with complex structures.
In contrast, saving data of NSCoding classes to files offers high degree of flexibility in terms of object structure. If you know that you are not going to need this flexibility in the future, go with NSUserDefaults; if you are not sure, stay with the files.

It is my preference to use a plist file that is programmatically created
NSString *appFile;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
appFile = [documentsDirectory stringByAppendingPathComponent:#"myFile"];
//this creates the file path
NSDictionary* tempDict = [[NSDictionary alloc]initWithContentsOfFile:appFile];
//this gets the data from storage
[tempDict writeToFile:appFile atomically:YES];
//this updates the data to storage

Related

Do I need to create a copy of the database file multiple times? or just once in the appdelegate?

What do I need to set up the database in my ios project? I know I have to make a copy of the database file but do I need to rewrite the same setup code in each view controller where I plan to actually retrieve and save data?
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentDir = [documentPaths objectAtIndex:0];
self.databasePath = [documentDir stringByAppendingPathComponent:#"gameDefault.sqlite"];
[self createAndCheckDatabase];
I have this in my appdelegate, but do I need to have the same code in my facebook view controller where I plan to save user info?
Use a single instance of FMDB, initialised with the path, across your app and view controllers. FMDB will create the DB for you if it doesn't exist.

Save images to NSMutableDictionary and than to plist

I need to save images which I get from server using
[[UIImage alloc] initWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:URL]]];
To NSMutableDictionary - it's ok for me, I'm doing it like this (it is in loop for every fetched image):
[self.imagesDict setObject:image forKey:[[xmlArray objectAtIndex:x] objectForKey:#"_img"]];
And than, I need to save it to disc. There were two solutions for me, but no one works... :-(. First one was save it do NSUserDefaults and second is save it to .plist file to root of iPhone. I want to go with that .plist, so I did:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *pathLD = [documentsDirectory stringByAppendingPathComponent:#"imagesDict"];
[self.imagesDict writeToFile:pathLD atomically:YES];
But when I try to NSLog it, it's not working. However when I save to dictionary some string instead of image, it works like a charm. So can someone help me please what I'm doing wrong? Thanks a lot!
You can not store the UIImage objects directly. Put their NSData inside the NSDictionary to be able to save them. Either the downloaded NSData or create it new via UIImagePNGRepresentation(image)
From the Dokumentation of NSDictionary's writeToFile:atomically:
This method recursively validates that all the contained objects are property list objects (instances of NSData, NSDate, NSNumber, NSString, NSArray, or NSDictionary) before writing out the file, and returns NO if all the objects are not property list objects, since the resultant file would not be a valid property list.
I can see two problems. First, you should really use .plist as the file extension of your dictionary. But more importantly, you're trying to save a dictionary that contains an object (UIImage) that cannot be serialized to a plist. Plists only support a limited number of object types, such as NSNumber, NSData, NSString. Looks at the writeToFile:atomically: description in the NSDictionary class reference. It has a list of what's allowed. If you want to save an image to a plist, you have to serialize it yourself into an NSData. I don't think it's a good idea, though, because the NSData representation might be a lot larger than the original image. I think you would be better off finding another way to do this.

Save PNG to Disk, but save file path to Core Data

There might be a duplicate question on here, but I searched and couldn't find an answer.
I have an app that accepts a signature from the user and saves that signature as a .png file. Here's the code I use:
-(void)saveSignature
{
UIGraphicsBeginImageContext(self.frame.size);
[self.layer renderInContext:UIGraphicsGetCurrentContext()];
signature = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
NSData *pngData = UIImagePNGRepresentation(signature);
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsPath = [paths objectAtIndex:0]; //Get the docs directory
NSString *filePath = [documentsPath stringByAppendingPathComponent:#"signature.png"];
[pngData writeToFile:filePath atomically:YES]; //Write the file
}
This code works great. It saves a .png of the signature in my application's documents folder. What I need to do is save the file path to my Core Data model and access it via. Core Data.
If anyone can point me in the right direction, that'd be great.
You don't say if you have anything else in Core Data. If you do, simply create an imagePath attribute on your model, and assign the path to the document into it. Then, simply load the image into the UIImage when you need to, based on the model, using UIImage's imageWithContentsOfFile: method.
If you don't already have Core Data in place and models generated, well, you really need to learn Core Data, and StackOverflow might not be the best place for that. This tutorial might be a good place to start.
My first piece of advise when it comes to Core Data is to use the MagicalRecord library, which makes the general use of Core Data much, much easier.

How to use NSUserDefaults to allocate space dynamically?

I ve used NSUserDefaults before , to store some variables for the settings section of my application.
However now i am making a recipes application , where the user can type in his own recipes and saves them. Each recipe has a title a description a date etc.. So i guess i ll need an array to save every recipe. But how will i add every new recipe dynamically?
I mean i ll just start to save the recipe in the next position on the array? and what happens if the user deletes one recipe and theres a free position in the array?
My logic here is correct on saving the data? What would you do?
NSUserDefaults is not what you're looking for. Technically it would work for what you want, but you're probably better off just making a custom class that has a property for all of the recipe characteristics and making the class conform to the <NSCoding> protocol so that you can convert it to data and write it to a file.
I know that might sound complicated if you've never done it before but it's really not too bad.
Here's an example for implementing <NSCoding>. Ignore the end part where it shows you saving the data to NSUserDefaults.
To save your data, instead of using NSUserDefaults, take a look at this question. It might seem like a lot of code for a small task, but the concept is pretty simple.
Edit:
To convert your object to data, assuming you've already implemented <NSCoding> in your custom class:
YourClass* someObject;
// do whatever you do to fill the object with data
NSData* data = [NSKeyedArchiver archivedDataWithRootObject:someObject];
/*
Now we create the path to the documents directory for your app
*/
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
/*
Here we append a unique filename for this object, in this case, 'Some_Recipe'
*/
NSString* filePath = [documentsDirectory stringByAppendingString:#"Some_Recipe"];
/*
Finally, let's write the data to our file
*/
[data writeToFile:filePath atomically:YES];
/*
We're done!
*/

Using NSKeyedArchiver to generate XML from NSManagedObjects held in an NSDictionary

I have an NSDictionary, which contains a bunch of NSManagedObjects.
I can then use NSKeyedArchiver to write this to an NSData object.
These are generated using this method. Which works fine and allows me to save a section of schema to disc and then read it back as a new set of objects in the core data model.
If I use either archivedDataWithRootObject:
or archiveRootObject:toFile:, as per the documentation
I can see that the format of the archive is NSPropertyListBinaryFormat_v1_0, whereas I want to serialise in NSPropertyListXMLFormat_v1_0, so that I can write my objects to a file and then process them elsewhere as plain old XML. (In fact I want to generate documents from them on a Windows based system.)
1) Is there a way I can do this? If so how?
2) Is there a better approach.
I want to maintain the serialised nature, since I also want to send the file back to the iOS device later and recreate the object model.
Create your own instance of NSKeyedArchiver with initForWritingWithMutableData:.
Set the format with setOutputFormat:NSPropertyListXMLFormat_v1_0.
Encode your root object with encodeObject:forKey:.
Call finishEncoding.
To unarchive the data you encoded in this way, you have to similarly instantiate an NSKeyedUnarchiver.
Thanks Ole! I was heading in that direction, but was not sure if it was the right way. Here is what I did in code in case it helps someone.
NSDictionary *dataAsDictionary=[self toDictionaryBlockingRelationships:blockRelationship];
NSString *savePath = [#"~/Documents/Saved.data" stringByExpandingTildeInPath];
NSMutableData *xmlData=[NSMutableData data];
NSKeyedArchiver *archive=[[NSKeyedArchiver alloc ]initForWritingWithMutableData:xmlData];
[archive setOutputFormat:NSPropertyListXMLFormat_v1_0];
[archive encodeRootObject:dataAsDictionary];
[archive finishEncoding];
if(![xmlData writeToFile:savePath atomically:NO]){
NSLog(#"Failed to write to file to filePath=%#", savePath);
}
[archive release];

Resources