Saving a NSArray that contains a NSDictionary that contains an NSArray of UIImages (and others) - iphone

I've seen various questions here pertaining to saving NSArrays/NSDictionaries, but I'm a bit confused about what to do when some of the subelements are UIImages.
To give a little context, the app is essentially a blog-type app. When the user is composing a new entry, their post can contain the following:
Up to 3 images from their photo album
Text
Location
In essence, I'm trying to implement a "Save Draft" functionality to the app if the user decides to temporarily cancel their blog post. When the user cancels the blog post, they will be asked in a UIActionSheet if they would like to save their draft. When the user wants to post again, they can begin from where they left off with their saved draft.
At this point, I would need to save these following objects:
1) NSArray of selected photos
---> contains NSDictionaries (up to 3)
--------> UIImage (large sized version)
--------> UIImage (thumbnail sized version)
2) NSDictionary of NSValues (just some view x,y position data)
3) Text -- NSString data of the blog text they have written
4) Location text -- NString data of their current location
Given that I need to save the above 1~4 data in order to make the "Save Draft" functinality, what is the best way to do this? Should I make a special class to hold all of this data? Also, do I first need to make the UIImages into NSData before I can save them to disk?
Thank you!!

Yes a class/model like structure make more sense and easier to handle as well.
Something like-
Interface Blogdata
NSArray *selectedPhoto;
NSDictionary *positionValues;
NSString *blogText;
NSString *locationText;
and then you can make one more model for photo data;
Interface Photodata
NSDictionary *photo;
UIImage *largeImage;
UIImage *thumbImage;

All of the properties that you mentioned seem like they would belong in a Blog class. Are they already grouped together? A Blog object could capture the state of the draft with variables (properties) of the object being the four things you mentioned. You can then save the Blog object as NSData and read it when the user wants the draft again.
The advantage of this is that you only have to worry about saving one object, instead of having to think about saving four each time (and retrieving them).

The easiest way would be to save the images to the apps documents folder and save the NSArray of filenames and other data that can be represented as text in a drafts.plist.
filenameStr = [NSString stringWithFormat:#"image1.png"];
fileManager = [NSFileManager defaultManager];
paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
documentsDirectory = [paths objectAtIndex:0];
imagePath = [documentsDirectory stringByAppendingString:#"/"];
imagePath = [imagePath stringByAppendingString:filenameStr];
NSData *imageData = UIImagePNGRepresentation(myImage);
[imageData writeToFile:imagePath atomically:YES];

Related

NSCoding, NSData, UIImages and performance

I'm using NSCoding to manage a custom object with a few different fields, one of them images. I'm only targeting iOS 5.0+ and have switched to ARC. I have everything working but am focused on performance--I haven't seen a question like this asked, so here it goes:
I transform the UIImage into NSData and add it to the main NSCoding file (a plist, if it matters) for storage on the disk. If there is more than one image, the image names become sequential (e.g. image1, image2, image3.) I then use the image both in a UITableView (as a resized thumbnail) and in a detail view. The negative side to this is that the plist balloons in size, which means slow initial load times when I use it because it's loading all of the NSData at once.
What is the best way to eliminate this problem and only force the loading of one image at a time?
What I've thought of:
I write the NSData to the disk, add an array to the plist, and add only a reference to the filename of each image to the array. I suppose I'd then reference the image filename at specified position, find it on the disk, and use it?
Any and all thoughts would be most welcome. I'm more stuck on the conceptual implementation than anything else and, funnily enough, this is not an oft-discussed topic.
Thanks,
EDIT:
As requested below, here's an example of taking an image and turning it into NSData:
UIImage *originalImage;
NSData *imageData = UIImagePNGRepresentation(originalImage);
//I save it all to the app's document directory
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
//I am using the images in a tableView and thus found it easiest to append the row number to each image filename.
//'y' below is just an integer that corresponds to the number of items in the master array
NSString *fileName = [NSString stringWithFormat:#"image%d.png",y];
documentsDirectory = [documentsDirectory stringByAppendingPathComponent:fileName];
[imageData writeToFile:documentsDirectory atomically:YES];
// NSLog(#"The filename is %#", fileName);
//newObject is an instance of my NSCoding object
[newObject setImageName: fileName];
Your suggestion is sound. Imagine a relational database. I would never save the images in one field as a blob. A filesystem is a very good place to save binary date in large amounts. It also gives you easy ways to duplicate files and so on.
So saving a reference in your plist will make parsing really fast and lazy loading an easy task to process images only when you need them.
iOS Programming: The Big Nerd Ranch Guide has a perfect example of this solution in chapter 14 of the 3rd Edition. Just download the code that is accessible from their site for a completed solution.
iOS Programming 3rd edition
Download Solutions

How to cache or store parsed xml file?

My app parses an xml file from my server, but I want to store parsed xml file and next start of my app, controller initially should load stored xml file, then controller should parse it again to check that there may be an update I did on xml file, if there is, new elements parsed should also be stored again.
I am referring to those app such as magazines, newspaper apps. When you open those kind of apps, it loads stored data that was downloaded previous session. Yet, after it loads, it starts to update the data, and it stores new update again.
Where do I start? What do you guys suggest?
Thanks in advance...
You can use CoreData or SQLite (use Objective-C wrapper FMDB https://github.com/ccgus/fmdb) to persist your XML. Then update the database everytime you see a unique id. Depends on how your XML data is.
It's actually quite easy to store to the documents directory. For example:
NSData *data; //this is your xml file
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docs = [paths objectAtIndex:0];
NSString *filename = [NSString stringWithFormat:#"test.xml"];
NSString *path = [docs stringByAppendingPathComponent:filename];
[data writeToFile:path atomically:YES];
Then to retrieve it later, you can get the path like above, but retrieve the file instead of writing it:
NSData *data = [NSData dataWithContentsOfFile:path];
either CoreData or SQLite can do the trick

How to save a video taken from camera into SQLite database in iPhone?

I have an application in which there is button named video. When a user clicks on the button a new view opens which consists of 4 other buttons namely take video and browse for video.
When he clicks on take video the camera opens which allows him to take video and the video gets inserted on the view. This all has been done, but there is a problem when the user has taken the video and the video gets inserted on the view. There is a button on the view named add which allows to insert the video into the SQLite database.
I am having a problem on how to bind a video and how to insert the captured video into SQLite database.
I am not sure why you want to save binary data contains captured videos into database. In DB best practice, this is not good idea. I would suggest to save your videos into home folder directory and named the videos into something that you can bind into the database.
For example, user recorded a new video, in database you make a new record and you got generated id, let say ID 10, so you should save the video into home directory like 10.mp4
Hence, you can retreive your video later on based on the same ID value.
:)
Trying to put Roberto's suggestion in code:
1) generate a unique Id (generated by time stamp/ hash / CFUUIDCreate ...) .
CFStringRef uStr = CFUUIDCreateString(kCFAllocatorDefault,CFUUIDCreate(kCFAllocatorDefault));
NSString *uniqueId = [[NSString alloc] initWithString:uStr];
2) save the file in with this unique name. Now save the uniqueId in SQLite DB.
NSString *docsDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *filePath = [docsDir sstringByAppendingPathComponent:uniqueId];
NSFileManager *fManager = [NSFileManager defaultManager];
if (![fManager fileExistsAtPath:fileDirPath])
{
// save the file
}
3) In order to get the video, from the uniqueId, try to construct the file path from it as,
NSString *docsDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *filePath = [docsDir sstringByAppendingPathComponent:uniqueId];
NSFileManager *fManager = [NSFileManager defaultManager];
if ([fManager fileExistsAtPath:fileDirPath])
{
// access the file
}

How to save multiple files in bundle in iphone app?

Hi all i am trying to save image in the bundle which i have currently on my view,but the problem is that i can only save one image,if i want to save the another image it replaces the old.I am not getting how to save the multiple images in the bundle then.
Here is my code.
- (void)writeImageToDocuments:(UIImage*)image
{
NSData *png = UIImagePNGRepresentation(image);
NSArray *paths=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSError *error = nil;
[png writeToFile:[documentsDirectory stringByAppendingPathComponent:#"image.png"] options:NSAtomicWrite error:&error];
}
Please Help me out, how to save multiple images, files e.t.c in bundle
Thanks in advance
You're not saving into a bundle, you're saving into your app's documents directory. There's no bundle aspect to it.
You're using the filename #"image.png" for every file that you save. Hence each new write overwrites the old one. In fact, you write each file twice. To save multiple files, use different file names.
It's also bad form to pass a numeric constant as the 'options:' parameter of NSData writeToFile:options:error: (or indeed, any similar case). The value '3' includes an undefined flag, so you should expect undefined behaviour and Apple can legitimately decline to approve your application. Probably you want to keep the NSAtomicWrite line and kill the one after it.
If you're just looking to find the first unused image.png filename, the simplest solution would be something like:
int imageNumber = 0;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *pathToFile;
do
{
// increment the image we're considering
imageNumber++;
// get the new path to the file
pathToFile = [documentsDirectory stringByAppendingPathComponent:
[NSString stringWithFormat:
#"image%d.png", imageNumber]];
}
while([fileManager fileExistsAtPath:pathToFile]);
/* so, we loop for as long as we keep coming up with names that already exist */
[png writeToFile:pathToFile options:NSAtomicWrite error:&error];
There's one potential downside to that; all the filenames you try are in the autorelease pool. So they'll remain in memory at least until this particular method exits. If you end up trying thousands of them, that could become a problem — but it's not directly relevant to the answer.
Assuming you always add new files but never remove files then this problem is something you could better solve with a binary search.
File names searched will be image1.png, image2.png, etc.

Saving application data state on exit

I have an NSMutableArray with 24 strings.
I need to save this data if a user gets a call or quits the application.
I have been looking into many examples but for some reason can’t seem to determine the best way to save the data.
The 24 strings correspond to 24 buttons and their state. When a button is clicked, it displays the corresponding Array info for that buttons tag (0 – 23). What I would need to retain is if 10 buttons where clicked and their data shown, how/what would be the best way of retaining this data so it can be reloaded when the app starts?
I am thinking I would need to store:
Button Tag,
Buttons corresponding Array value,
Button state (whether it has clicked and value is show or not)
I would store this data on exit of the application and then when app is started again, I would determine if this stored data exists, if so populate the array and examine the button states to determine if it had already been shown and if so, set it accordingly. Then when this file was loaded, I would delete the stored data (.DAT file if stored this way). This way if a user quits gracefully, on next start up, it would start a new game.
I have looked at several examples where they store data into a .DAT file but am having problem implementing this….and wondering if this is even the best way.
Any help or thoughts on this is greatly appreciated.
Geo…
you should be able to store it in NSUserDefaults
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:buttonState forKey:#"buttonState"];
[defaults synchronize];
// Then to get the state back
NSMutableArray* buttonState = [[[NSUserDefaults standardUserDefaults] arrayForKey:#"buttonState"] mutableCopy];
You could save the data to a plist in the Documents directory. If the data is there, load it up, and if not, it would suggest a clean run.
To load the data:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documents = [paths objectAtIndex:0];
NSString *filePath = [documents stringByAppendingPathComponent:#"buttonState.plist"];
NSMutableDictionary *buttonState = [[NSMutableDictionary alloc] initWithContentsOfFile:filePath];
To save the data:
[buttonState writeToFile:filePath atomically: YES];
I save my data by using NSKeyedArchiver/NSKeyedUnarchiver, since your NSMutableArray already supports the NSCoding protocol, you can just use
yourArray = [NSKeyedUnarchiver unarchiveObjectWithFile:file];
[NSKeyedArchiver archiveRootObject:yourArray toFile:file];
Edit:
I suppose NSArray's own methods work also
[NSArray writeToFile:]
[NSArray initWithContentsOfFile:]