How to use NSUserDefaults to allocate space dynamically? - ios

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!
*/

Related

iOS - Best way to save a list of user's favourite objects in memory

I am working on an app that display photos of buildings along with some information about them. The photos are jpegs that all come in the app bundle, and the Building objects associated with them have a few properties such as name, address, and of course, a photos array. I'm loading the Building objects from a plist when the app launches. Everything's pretty straight forward, except that when the user scrolls through the images of buildings they have the option to add the ones they like to a favourite's list for viewing later.
Originally I was going to save each Building object they favorited using Core Data, but now that I think about it it seems a little redundant because all of the Building objects are already loaded from the plist. When the user is scrolling through the images of each building the photos have to display an indication of weather that building has been added to the favourite's list yet, which means I'll have to continuously compare an array of objects taken from CoreData with an array of objects loaded from a plist, which seems like it could be a memory wasting task.
I was wondering if, as an alternative to CoreData, I could add a unique ID property to the Building object and store it in the plist with them. Then when the user adds a Building to their favourites list I can just add it's UID to an array in NSUSerDefaults and make the comparison that way. Does that seem like a sensible way to tackle this?
Storing data like this in NSUserDefaults is a really bad habit to get into. Save user preferences in NSUserDefaults, not sets of data.
Much better idea is to save the user's favorites in a file in the Documents folder. If you've added an "ID property" to your Building object, you can maintain an array of selected IDs. That array can be saved to and read from disk with:
NSArray *documentPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [documentPaths objectAtIndex:0];
NSString *myFile = [documentsDirectory stringByAppendingPathComponent:#"myFavs"];
and then
// save the array contents to file named "myFavs"
[a writeToFile:myFile atomically:YES];
or
// read the saved array when you launch the app
NSArray *b = [NSArray arrayWithContentsOfFile:myFile];
Of course, you'll want to add error handling, but this is a much, much better idea than stuffing an array into NSUserDefaults.
Perhaps using CoreData for favourites is OK, although, consider using Sets which stores distinct values of the same type in a collection with no defined ordering
Assumptions:
- Your buildings data isn't coming from a dynamic/external source
- You don't have a massive amount of data.
why not add a property to the buildings to flag it as a favorite and store that local when the app goes to the background. (NSUserDefaults or document directory as plist)?. Then in future app loads use the local one that has the flags.
That way you don't need an additional data structure to store identifiers and a list of favorites.
CoreData seems like overkill for the problem you're describing.

CoreData iOS - How to create unique ID for objects?

I have a project in IOS for iPhones and iPads and such. For the project I am using CoreData to hold the data the user feeds in the app. For ease of understanding lets say its like a contacts app. So basically you know you have your name, work, phone etc. Well this app also stores an image for each "contact". Optionally of course. To prevent the database from being inefficient I store the image into the DocumentsDirectory using the below code for the path. The image is recalled by using the ManagedObjects id which is also what is used in name of image. However, multiple objects can have this same id, which causes some problems.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *savedImagePath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:#"Person%i.jpg",[[_detailItem id] intValue]]];
Basically I am taking the image using the id of the person. However, multiple people can have the same id(An int-32). Instead of doing this, I was wondering if there is a way that CoreData creates or can create a Unique id for each object stored. That way if I change the id it won't lose its picture or if another object has same id it won't use the same picture.
Some databases like a MySQL I used to use had an option for attributes that would give it a unique id for uses with users/forums/posts/etc... Is there something similar in XCode's CoreData? I would hate to have to parse through every object to get a number not being used yet.
Some databases like a MySQL I used to use...
Stop thinking about MySQL when using Core Data. Doing so will only cause problems.
Managed objects have a unique ID field which you can look up using objectID, of type NSManagedObjectID. It's an opaque class, but you can convert it to an NSURL using its URIRepresentation method. From there you could convert it to a string and use that for an image filename.
These IDs are unique as long as you're only working with a single device. If you ever sync data between devices, the IDs on one device won't match those on the other device. The ID is intended for Core Data's internal use, and is only consistent within the same persistent store file.
If you really want an integer, you'll have to maintain it yourself. Store the value of the most recent integer in user defaults or in the persistent store file's metadata. When you create a new instance you'd read the integer, increment it, use it for the new instance, and save the new value.
A much simpler approach would be to use UUIDs for the unique IDs. Get a new unique ID by doing this:
NSString *newID = [[NSUUID UUID] UUIDString];
Store that as an attribute, and use it as part of the image filenames.
Core Data does not have a way to do this automatically. You could parse the string form of the (permanent) objectID to get Core Data's unique identifier. A better option would be change your id to be a string and use a UUID.
I'd suggest creating your own uniqueId attribute, and populating it with something like CFUUID, described in further detail here

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.

NSUserDefaults vs NSCoding

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

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.

Resources