Posts Tagged ‘NSFileManager’
Some time ago, I wrote an article on NSFileManager and how to use it for saving an image to and loading/deleting it from the documents directory. According to my stats, this is the most popular article on my blog and also comments show, that quite a few readers have been working with my sample code. I really appreciate that and would like to thank you all!
Anyways, since you guys seem to enjoy saving and loading stuff, I figured it’d be a good idea to write another tutorial on that subject. This time we’ll work with all different kinds of information (arrays, boolean values, integers, floats, images, etc…). We’ll save such information when the user exits/quits the app and we’ll load it when the app launches again.
Alright, let’s get busy, shall we? So, what do we need?
- Method for loading info right after the app did finish launching.
- Method for saving info when user exits/quits the app.
First, we navigate to our AppDelegate.h and add two methods:
//your AppDelegate.h will look something like this:
@interface AppDelegate : NSObject <UIApplicationDelegate> {
}
- (void)loadPreviousSession; //for loading
- (void)saveCurrentSession; //for saving
@end
Now, let’s add some NSMutableArray, boolean, float and an integer variable, so we have something to work with.
@interface AppDelegate : NSObject <UIApplicationDelegate> {
NSMutableArray *myMutableArray;
BOOL someBool;
int someInt;
float someFloat;
}
- (void)loadPreviousSession; //for loading
- (void)saveCurrentSession; //for saving
@property (nonatomic, retain) NSMutableArray *myMutableArray;
@end
Now, we’ll implement those methods into our AppDelegate.m
@implementation AppDelegate
@synthesize myMutableArray; //don't forget to synthesize
- (void)dealloc { //a habit of mine, to insert the dealloc method, right below @synthesize. This way I don't forget to release stuff I synthesize.
[super dealloc];
[myMutableArray release]; //do proper house keeping and don't forget to release
}
- (void)loadPreviousSession {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:@"NameOfYourApp.dat"]; //you can choose any name for the .dat file. Usually, the name of your app is a good idea. This file will hold all our information.
NSFileManager *fileManager = [NSFileManager defaultManager];
//checks, if we already have some information stored in our .dat file
if ([fileManager fileExistsAtPath:path]) { //file exists, so there must be some information in it
NSLog(@"Data Found");
NSMutableData *theData;
NSKeyedUnarchiver *decoder;
theData = [NSData dataWithContentsOfFile:path];
decoder = [[NSKeyedUnarchiver alloc] initForReadingWithData:theData]; //our decoder allows us to easily access information inside our "theData" object.
NSMutableArray *tempMutableArray; //in this example we're also working with NSMutableArray. We first create a temp version of this array and then assign its content to our "real" NSMutableArray. Why? I noticed, that directly assigning it, fails sometimes.
//now, let's load our information
tempMutableArray = [decoder decodeObjectForKey:@"myMutableArray"]; //the key is something, you can choose yourself of course. However, take a look at the - (void)saveCurrentSession method below. Your keys have to be consistent between these two methods.
someBool = [decoder decodeBoolForKey:@"someBool"];
someInt = [decoder decodeIntForKey:@"someInt"];
someFloat = [decoder decodeFloatForKey:@"someFloat"];
//now, let's assign our tempMutableArray to our myMutableArray object
[self setMyMutableArray:tempMutableArray];
//tell decoder, we're done and finally release it
[decoder finishDecoding];
[decoder release];
}
else { //file does not exist - if necessary, we can assign default values to variables in here.
NSLog(@"No Data Found");
someInt = 1;
someBool = YES;
someFloat = 1.0;
}
}
Ok, we’re half way through. Now, we need to take care of saving our stuff, in case user quits or exits the app.
Let’s add another method to our AppDelegate.m:
- (void)saveCurrentSession {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:@"NameOfYourApp.dat"];
//almost same story as above, but the other way round this time
NSMutableData *theData;
NSKeyedArchiver *encoder; //instead of a decoder, we create an encoder
theData = [NSMutableData data];
encoder = [[NSKeyedArchiver alloc] initForWritingWithMutableData:theData];
//let's start saving - we make a call to our encoder, followed by the type we want to encode and the key we want to assign. Important: We have to use the same keys inside our -(void)loadPreviousSession method!
[encoder encodeObject:myMutableArray forKey:@"myMutableArray"];
[encoder encodeBool:someBool forKey:@"someBool"];
[encoder encodeInt:someInt forKey:@"someInt"];
[encoder encodeFloat:someFloat forKey:@"someFloat"];
[encoder finishEncoding];
[theData writeToFile:path atomically:YES];
//release our encoder
[encoder release];
NSLog(@"successfully saved!");
}
We’re almost done! However, our methods would be pretty useless, if we didn’t call them somewhere. So, let’s take care of that
Look for the following method and add a couple lines:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//in case you're working with NSMutableArray(s), make sure to properly allocate them
myMutableArray = [[NSMutableArray alloc] init];
//load previous session
[self loadPreviousSession];
}
Finally, we have to make a call to our – (void)saveCurrentSession method.
We do that inside the following methods:
- (void)applicationWillTerminate:(UIApplication *)application { //IMPORTANT: please mind, that this method is hardly called anymore these days. Reason: multitasking (aka. fast app switching) - an app usually stays "active" in the background. Another important fact to notice is this: If your user force quits the app from the fast app switching bar, this method ISN'T CALLED either! However, this method will be called on devices, that do not support multitasking.
[self saveCurrentSession];
}
- (void)applicationDidEnterBackground:(UIApplication *)application { //user just quit the app - let's save our information
[self saveCurrentSession];
}
- (void)applicationWillResignActive:(UIApplication *)application { //app is about to become inactive. You can also make a call to - (void)saveCurrentSession in here. However, if you're also doing it inside the method above, you might trigger the method twice, which is not necessary.
[self saveCurrentSession];
}
When your app becomes active again, it might be necessary to load previously saved information. In this case, add the following method:
- (void)applicationDidBecomeActive:(UIApplication *)application {
[self loadPreviousSession];
}
That’s it!
One more thing…working with UIImages.
If your NSMutableArray contains UIImages, you’ll run into a problem. I suggest, to convert an UIImage into a NSData object first and THEN add it to the array. This way, you won’t have any issues saving/loading it.
//we assume, we have an UIImage, named myUIImage. We want to add this image to our myMutableArray object, so we can save/load it between sessions NSData *convertedImage = UIImagePNGRepresentation(myUIImage); //add the image to the array [myMutableArray addObject:convertedImage]; //in order to assign the convertedImage to an UIImage, do something like this: myUIImage = [UIImage imageWithData:[myMutableArray objectAtIndex:0]];
Hope, you find this helpful. As usual, feel welcome to add comments, if you have questions/feedback.
NSFileManager offers a convenient way to write images to and load them from the documents directory.
If you’re frequently doing that in your project, I suggest to wrap up NSFileManager support in three simple methods:
//saving an image
- (void)saveImage:(UIImage*)image:(NSString*)imageName {
NSData *imageData = UIImagePNGRepresentation(image); //convert image into .png format.
NSFileManager *fileManager = [NSFileManager defaultManager];//create instance of NSFileManager
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); //create an array and store result of our search for the documents directory in it
NSString *documentsDirectory = [paths objectAtIndex:0]; //create NSString object, that holds our exact path to the documents directory
NSString *fullPath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.png", imageName]]; //add our image to the path
[fileManager createFileAtPath:fullPath contents:imageData attributes:nil]; //finally save the path (image)
NSLog(@"image saved");
}
//removing an image
- (void)removeImage:(NSString*)fileName {
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *fullPath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.png", fileName]];
[fileManager removeItemAtPath: fullPath error:NULL];
NSLog(@"image removed");
}
//loading an image
- (UIImage*)loadImage:(NSString*)imageName {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *fullPath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.png", imageName]];
return [UIImage imageWithContentsOfFile:fullPath];
}
Now, you can easily save an image like:
[self saveImage: myUIImage: @"myUIImageName"];
or load it like:
myUIImage = [self loadImage: @"myUIImageName"];
or remove it like:
[self removeImage: @"myUIImageName"];
