Saving NSArray data to disk - ios

I have an NSMutableArray that contains NSMutableDictionary's. Each dictionary has an AVAsset, an NSURL, an NSString, and two UIImages. I want to save my array to disk so that each time I close and open my app, I can load the array and convert the URLs's to NSData objects in order to play audio and use the AVAssets for some other actions. I know I can save and load my array using initWithContentsOfFile and writeToFile:atomically and this answer is pretty informative: Saving a NSArray. However, that answer was from 2009. Is there a better way of saving and loading an array these days?.

As for the answer you linked, the answer is still valid. And according to it, you cannot store it the way it mentions. This is because array must be plist format compatible in order to be saved like that. When you parse your array down to lowest element hierarchy, you have UIImage which is just an object pointer and doesn't make sense.
One practical way would be store UIImages as separate files, and store their paths as part of your NSMutableDictionary objects. Same holds true for AVAssets. Off course you need to engineer the solution to fully accomplish this goal.
One more way to store non-plist compatible objects is to use archiving and unarchiving feature. Refer to the documentation. Here, make sure that each object in the tree follows protocol NSCoding (Probably, AVAsset in your question does not conform to it, so you need a way to work around it). For an example, see this answer and search the likes of it.

Related

How do we know what gets done with data objects passed to UIActivityViewController?

For example, the documentation for UIActivityTypeAirDrop states
When using this service, you can provide NSString, NSAttributedString,
UIImage, ALAsset, and NSURL objects as data for the activity items.
You may also specify NSURL objects whose contents use the
assets-library scheme. You may also provide NSArray or NSDictionary
objects that contain the listed data types.
But how do we know what exactly is going to be done with each of the data objects? Do we just need to experiment to find out? Same for the other UIActivity types, none of them say what is specifically done with the objects.
Unfortunately the documentation for this controller is not that comprehensive and there are not too many details on this. Experimentation is pretty much what I've ended up doing. Some of them are common sense though, such as "Save to Camera Roll" requiring an image object. Most of the others will try to use whatever you give them (URL, text, image, etc).
I would implement your own UIActivityItemSource to pass into the controller, and return exactly the media you want for each individual activity item type so that there is no ambiguity in what will actually get passed to the user.

Can I do partial saves with NSKeyedArchiver?

I have a database object and some photos objects.
The database object contains an integer property and a mutable dictionary property.
The integer keeps track of the next free number to use as a mutable dictionary key when I create a new photo object and the mutable dictionary contains pointers to photo objects.
Photo objects contain an image, an image description and the date the image was taken.
I've been using NSKeyedUnarchiver and NSKeyedArchiver to read and write these objects in and out when my applicationDidBecomeActive and applicationWillResignActive methods trigger.
And, it's all been working well. When applicationWillResignActive triggers, it calls NSKeyedArchiver and points it to the database object as the root. The coder then writes out the integer and then when it encounters the mutable dictionary, it descends into it and each photo object is called to save its properties which are the image, the description and the date.
As I said, it's all been working well. But, it has seemed slower and slower as the number of photos has increased so I did some timings.
I found that reading the archive in is roughly 25 times faster than writing it out.
So, I conceived the idea of only writing out the photos which are new or changed as a method of speeding up the write side. After all, most of the photos are from past sessions and I might have 30 or 50 of them from before and I might only shoot two or three new ones this time.
I created some flags that indicate if a photo is new or old. When applicationWillResignActive triggers and I find myself down in the photos object handling each encodeWithCoder call, I save the image, description and date if the photo is new and I skip saving if it is old.
Well, I did not get the result I'd hoped for :-)
When applicationWillResignActive triggers, all the photos I skip writing out end up getting written out as empty photo objects which overwrites the previous photo objects out there with the same key. Then, when I load them back in, I've got bupkis, nada, zip.
First question, I guess, is can I write out only part of my object tree and have the parts I don't write out still remain out there intact from an earlier full write? I'm beginning to wonder if that might be a naive idea.
Gallymon
Using archives of some array of objects is going to be increasingly inefficient as the archive grows. This is exacerbated by the fact that your objects include images. Two thoughts:
You should consider using Core Data for storing this data. (You could use SQLite, directly, too, but Core Data is the preferred object persistence technology in iOS.) This way, when you want to save a new object, you just add that new object, and don't archive the whole collection of objects every time.
Furthermore, if speed is of primary interest and if your images are large, SQLite (the database that Core Data employs by default) is inefficient when handling large BLOBs (i.e. things like images). So, as inelegant as it sounds, you probably want to save images to your Documents folder, and only store a image file URL or path in CoreData.
why don't you save the image only on the init method ? if you create the object chances are you want to save it. so place the flag hasChanges == off in the init method and you check that flag for saving...

Read plaintext document into NSArray in iOS?

I'm new to iOS but have plenty of experience with c++ and Python. I'm trying to figure out how to read a plaintext file I have on my computer into an NSArray in xcode. In c++ I would do this:
while(istr>>string) myArray.push_back(string);
However, I need to create a local copy to be stored on the iOS device. Is there a way I can package this data so that a local copy of JUST THE ARRAY will be stored on the device? I was thinking of maybe doing something with a JSON serialization or something.
Should I really just suck it up and do this:
NSArray myArray = [[NSArray alloc] initWithObjects: #"myInfo", nil];
I just want a more elegant way to handle this, I guess.
I think maybe you're thinking a little too C about this. In C and C++, strings are arrays of bytes. In ObjC, there's an object for that. It's called NSString, and it's probably what you should be storing plaintext in.
It even has an easy class method to help you out with this if you already have a byte array:
+(id)stringWithCString:(const char *)cString encoding:(NSStringEncoding)enc
See the NSString documentation for more details.
As to storing it on the device, there are solutions that range from the simple (NSUserDefaults) to the complex (Core Data), but pretty much anything will expect plain text be in an NSString.
EDIT:
The title of this question talks about reading the string from the filesystem. First step is to get the bytes of the file into an NSData object. The easy way:
+(id)dataWithContentsOfFile:(NSString *)path
Then make a string out of the data with this initializer of NSString:
-(id)initWithData:(NSData *)data encoding:(NSStringEncoding)encoding
I don't know if this can help you, anyway if you just need to store an array of data to filesystem and deserialize it back to NSArray, an easy way is to use plists.
It is a convenient way to store a small amount of data, without any kind of relationship (there is Core Data for that). The main advantage is that you can store in it NSArray, NSDictionary, NSNumber, NSString, NSDate and NSData (so any kind of binary information) and they get automatically serialized and deserialized through some simple methods.
You can write an NSArray to a file in this way:
- (BOOL)writeToFile:(NSString *)path atomically:(BOOL)flag
and deserialize it back with this:
+ (id)arrayWithContentsOfFile:(NSString *)aPath
If you just want to provide some initial data to your app, and it is for example an array of strings or something similar, you can manually add a plist to your project by going to File->New->File and choosing Resources->Property list, and fill it by hand.
You can read more at https://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/PropertyLists/Introduction/Introduction.html

Storing a persistent array of UIImages - CoreData or on disk?

In my app I will have an array of up to 50 images that people can maintain. They can choose to create new or delete existing images. Each image will have a few things associated with them, like a rating for example.
My question is how I should go about storing them. Should I create a CoreData entity called "Image" and store them that way? Should I set up a UIView subclass that conforms to NSCoding and just encode and decode the array and store it on the device? Is there another way I should consider? Thanks for any suggestions.
You can create an entity that represents the image with its information, and use core data's external storage property for entity's attribute. This way, you get the convenience of core data without actually storing the images on the persistent store.
I had to make a similar decision recently and I decided to store the image in CoreData. I have a managed object called photo with a Binary Data field called image:
photo.image = UIImagePNGRepresentation(imageFile); // <- imageFile is a UIImage
And I recreate the image using:
[UIImage imageWithData:self.image];
One immediate advantage is that the images are deleted automatically with the object and there's no extra overhead in retrieving the image if you've already queried for the record.
Core Data is probably overkill for what you want to do. Choose some key value pairs for descriptive information about the image. One key will be "path" and the value will be the path to the image file (or just its name). You can serialize array (or set) of dictionaries.
When your app starts up, read in the serialized array of dictionaries. Every time something changes, serialize and save the information. Write a short audit routine to insure that there is a one - to - one correspondence between dictionaries and images on file, and if one or the other is missing delete the other (will handle situations were you crash before getting to update something or the other).
Later on you can add more attributes to the dictionaries if you want, or even remove some.
[PS: I did this in a shipping app for one version, when the information needed to become relational I switched to Core Data.]

iOS - Saving NSMutableArray to iPhone

I'll ask in the form of a hypothetical, which might make it easier for me to explain.
I have a class called Person, and in this has three fields:
NSString *name;
NSDate *dateOfBirth; and
NSMutableArray *friends.
An example object is this:
name = "John Smith"
dateOfBirth = 01/04/1985
friends = "Simon Scott"; "Jennifer Lane"; "Mary Firth"
Once the user has filled the NSMutableArray with the data they want, what would be the best way to save this data to the iPhone? I would anticipate that there could be up to 100 instances of the Person object, and all that will be required is the displaying of this data in a UITableView and giving the user the ability to add and remove entries at their will.
I have seen multiple suggestions on this site, which include NSDictionaries and using the writeToFile method, but before I research one of these, I was hoping someone could point me in the right direction? I would like to ensure that I'll be using the easiest and most appropriate method that's out there.
Many thanks.
Please take a look at the Property List Programming Guide. As long as you stick with a core set of object types for your data, you can write and read your data from a file or URL in one line, like this:
[people writeToURL:someURL atomically:NO];
The types you've mentioned in your question (strings, dates, arrays, dictionaries) can all be written to a property list.
#Achiral,
It really depends on what you want to do with the data and how concerned you are with the flexibility of your code.
However, my recommendation would be to use CoreData to make an SQLlite database and make a 'Person' entity with the properties that you list above. I don't know if you are familiar with CoreData, but it is highly optimized on iOS and is pretty easy to use, since it has a pretty simple 'fill in the blanks' style form for creating the data models. You should also note that CoreData is a well accepted and supported way to store data in an iOS and OS X app.
I hope this helps.

Resources