Save / Load IOS data - ios

I've been attempting to get my app to save and load data from a set of arrays in the app, the odd issue is that when the app terminates (completely shut down) the data does not seem to load upon being restarted, i've had a look over a lot of posts, tutorials etc but i can't seem to get it to work, i have two test buttons on the app to trigger the save and load methods and also a button to clear the records, when i use these to test, it works perfectly and the data saves and loads correctly.
My current setup is as follows:
I have a plist file called data.plist in the supporting files directory, inside this file i have the various arrays with the same data in index 0 as the data that is initialised when the global data class creates an instance of itself.
My save code:
- (void)saveData{
// get paths from root direcory
NSArray *paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
// get documents path
NSString *documentsPath = [paths objectAtIndex:0];
// get the path to our Data/plist file
NSString *plistPath = [documentsPath stringByAppendingPathComponent:#"data.plist"];
// create dictionary with arrays and their corresponding keys
NSDictionary *plistDict = [NSDictionary dictionaryWithObjects: [NSArray arrayWithObjects: personalitySliderValue, looksSliderValue, humourSliderValue, chemistrySliderValue, emptySlider, notesValues,nameValues, noValues, ratingValues, nil] forKeys:[NSArray arrayWithObjects: #"personality", #"looks", #"humour", #"chemistry",#"empty",#"notes",#"name",#"no",#"rating", nil]];
NSString *error = nil;
// create NSData from dictionary
NSData *plistData = [NSPropertyListSerialization dataFromPropertyList:plistDict format:NSPropertyListXMLFormat_v1_0 errorDescription:&error];
// check if plistData exists
if(plistData)
{
// write plistData to our Data.plist file
[plistData writeToFile:plistPath atomically:YES];
}
else
{
NSLog(#"Error in saveData: %#", error);
}
}
My load code:
- (void)loadData{
// get paths from root direcory
NSArray *paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
// get documents path
NSString *documentsPath = [paths objectAtIndex:0];
// get the path to our Data/plist file
NSString *plistPath = [documentsPath stringByAppendingPathComponent:#"data.plist"];
// check to see if data.plist exists in documents
if (![[NSFileManager defaultManager] fileExistsAtPath:plistPath])
{
// if not in documents, get property list from main bundle CHECK D capitalisation
plistPath = [[NSBundle mainBundle] pathForResource:#"data" ofType:#"plist"];
}
// read property list into memory as an NSData object
NSData *plistXML = [[NSFileManager defaultManager] contentsAtPath:plistPath];
NSString *errorDesc = nil;
NSPropertyListFormat format;
// convert static property list into dictionary object
NSDictionary *dictionaryTemp = (NSDictionary *)[NSPropertyListSerialization propertyListFromData:plistXML mutabilityOption:NSPropertyListMutableContainersAndLeaves format:&format errorDescription:&errorDesc];
if (!dictionaryTemp)
{
NSLog(#"Error reading plist: %#, format: %d", errorDesc, format);
}
// assign values
personalitySliderValue = [NSMutableArray arrayWithArray:[dictionaryTemp objectForKey:#"personality"]];
looksSliderValue = [NSMutableArray arrayWithArray:[dictionaryTemp objectForKey:#"looks"]];
humourSliderValue = [NSMutableArray arrayWithArray:[dictionaryTemp objectForKey:#"humour"]];
chemistrySliderValue = [NSMutableArray arrayWithArray:[dictionaryTemp objectForKey:#"chemistry"]];
emptySlider = [NSMutableArray arrayWithArray:[dictionaryTemp objectForKey:#"empty"]];
notesValues = [NSMutableArray arrayWithArray:[dictionaryTemp objectForKey:#"notes"]];
nameValues = [NSMutableArray arrayWithArray:[dictionaryTemp objectForKey:#"name"]];
noValues = [NSMutableArray arrayWithArray:[dictionaryTemp objectForKey:#"no"]];
ratingValues = [NSMutableArray arrayWithArray:[dictionaryTemp objectForKey:#"rating"]];
}
And finally the app delegate methods:
- (void)applicationDidEnterBackground:(UIApplication *)application
{
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
// save the app data
[[GlobalData sharedGlobalData]saveData];
NSLog(#"save method run");
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
// load the app data
[[GlobalData sharedGlobalData]loadData];
NSLog(#"load method run");
}
This has literally been making me pull my hair out, so any help would be great!

You can load data at launch time in this app delegate method:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
....
[[GlobalData sharedGlobalData] loadData];
}

Related

Cleared the files but memory is not reduced

I am creating an application. I am storing the files in Document directory. And after my work completed, delete the files from document directory as like below:
NSMutableDictionary * Dictionary = [NSMutableDictionary alloc]init];
// Next every file storing into this dictionary like below
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *baseDir = [paths objectAtIndex:0];
NSString *pathComp = [baseDir stringByAppendingPathComponent:[NSString stringWithFormat:#"IMG%d.PNG",presentCount];
fileURL = [NSURL fileURLWithPath:pathComp];
[Dictionary setObject:fileURL forKey:fileURL];
while ([[Dictionary allKeys]count]!=0) {
NSURL *deleteFileURL = [[Dictionary allKeys] lastObject];
NSLog(#"Path %#",deleteFileURL.path);
[[NSFileManager defaultManager] removeItemAtPath:deleteFileURL.path error:nil];
[Dictionary removeObjectForKey:deleteFileURL];
}
Here my problem is, after delete the files from document directory, memory is not reduced, still it's occupying as like files exist. Due to this issue, my is crashing. So please help me how to clear the memory.
Actually i am getting the files(Photos ) from the server and first placing in documents directory,and trying to save using photo library.Once i give input from dictionary to photo library, after completion handler, i am trying to delete the file.Its removed and photo saved, but memory is not reduced.
1、Remove the file after checking for the existence of a file on the path, and check the return value of removeItemAtPath: to determine if the deletion succeeded.
NSString *path = #"a/b";
if ([[NSFileManager defaultManager] fileExistsAtPath:path]) {
BOOL success = [[NSFileManager defaultManager] removeItemAtPath:path error:nil];
// Check the success's value
}

NSKeyedUnarchiver returns nil after subsequent app launch

EDIT: Have checked and rechecked file paths, code for archiving and unarchiving and no bugs found. Is there an issue with App File System between launches that I am not aware of?
I am using NSCoder to store images in my app. I create a unique file path, store that file path to Core Data, and archive an image using that same file path in the documents.
On the app's first launch, everything works as expected - the file paths saved in Core Data are used to unarchive the images stored in Documents. On subsequent launches, however, the unarchiver returns nil, and I get a crash.
ImageForArchiving.m - the object that is to be saved with init and encode methods
+ (ImageForArchiving *)createImageWithImage:(NSData*)data andDate:(NSDate*)date {
ImageForArchiving *archiveImage = [[ImageForArchiving alloc] init];
[archiveImage setDate:date];
[archiveImage setImageData:data];
return archiveImage;
}
- (id) initWithCoder: (NSCoder *)coder
{
if (self = [super init])
{
[self setDate: [coder decodeObjectForKey:#"date"]];
[self setImageData: [coder decodeObjectForKey:#"image"]];
}
return self;
}
- (void) encodeWithCoder: (NSCoder *)coder
{
[coder encodeObject:self.date forKey:#"date"];
[coder encodeObject:self.imageData forKey:#"image"];
}
Saving to archive, Core Data, creation of file path and unarchiving code
- (void)saveItemsAtFilePath:(NSString*)filePath andImageToArchive:(ImageForArchiving*)imageRecord {
[NSKeyedArchiver archiveRootObject:imageRecord toFile:filePath];
}
- (void)loadItemsWithFilePath:(NSString*)filePath {
if ([[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
ImageForArchiving *image = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
NSLog(#"image %#", image.date);
} else {
NSLog(#"nothing saved");
}
}
-(void)saveToCoreDataWithFilePath:(NSString*)filePath{
AppDelegate *delegate = [UIApplication sharedApplication].delegate;
NSManagedObjectContext *context = delegate.managedObjectContext;
NSManagedObject *photoAndDate = [NSEntityDescription insertNewObjectForEntityForName:#"Image" inManagedObjectContext:context];
[photoAndDate setValue:[NSDate date] forKey:#"date"];
[photoAndDate setValue:filePath forKey:#"image"];
NSError *error = nil;
if (![context save:&error]) {
NSLog(#"Can't Save! %# %#", error, [error localizedDescription]);
}
}
- (NSString *)createPathForDataFile
{
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsPath = [paths objectAtIndex:0];
documentsPath = [documentsPath stringByExpandingTildeInPath];
NSError *error = nil;
if ([fileManager fileExistsAtPath: documentsPath] == NO)
{
[fileManager createDirectoryAtPath:documentsPath withIntermediateDirectories:YES attributes:nil error:&error];
}
NSString *stringForSaving = [[NSUUID UUID]UUIDString];
NSString *filePath = [documentsPath stringByAppendingPathComponent:stringForSaving];
return filePath;
}
Looping through Core Data array to load up data source. The addition of the unarchived object is w
I was encountered the same problem before. You write a image to a path and save it to the core data. Later read the image path, it is exactly same but no image is loaded.
So, Did you tested the app in the simulator? I don't know if it is intended or a kind of a bug but it would be working as you wanted on the real devices.
So try to run your app in the real devices and see if it works.
I just found out that in iOS 8 the file paths are reset after each app launch. The code above save the file name to Core Data and re-create the file path to Documents when calling for the file.
For instance, the below would create the file name.
- (NSString *)createPathForDataFile
{
NSString *stringForSaving = [[NSUUID UUID]UUIDString];
NSLog(#"filePath in CreatePathForDataFile %#", stringForSaving);
return stringForSaving;
}
Saving would occur by calling the path to the Documents folder and passing in the file name as the parameter filePath.
- (void)saveItemsAtFilePath:(NSString*)filePath andImageToArchive:(ImageForArchiving*)imageRecord {
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsPath = [paths objectAtIndex:0];
NSString *finalFilePath = [documentsPath stringByAppendingPathComponent:filePath];
[NSKeyedArchiver archiveRootObject:imageRecord toFile:finalFilePath];
}

My plist is not updated when i close my application and open it again

Please,
I created a plist with some Keys and values,
And then I tried to add new item to my Plist and save it.
After that i close my App and open it again, but i don't see my new items on my Table View.
So I don't understand where is my wrong !!!
Please Help!!!
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsPath = [paths objectAtIndex:0];
NSString *plistPath = [documentsPath stringByAppendingString:#"MyProfile.plist"];
NSString *error=nil;
NSData *plistData = [NSPropertyListSerialization dataFromPropertyList:MyProfile format:NSPropertyListXMLFormat_v1_0 errorDescription:&error];
if (plistData){
[plistData writeToFile:plistPath atomically:YES];
}
else{
NSLog(#"Error in save Data:%#",error);
}
self.profileInfo = [[NSMutableDictionary alloc]initWithContentsOfFile:filePathPlist];
The values in self.profileInfo is not mutable, if the value for a specific key is a array, it will be NSArray not NSMutableArray.
Let's talk about the code below:
self.names = [self.profileInfo objectForKey:#"Names"];
Although you declare the property names as a NSMutableArray, the instance you get here is still a NSArray. So you should send mutableCopy method to id to get a mutable array:
self.names = [[self.profileInfo objectForKey:#"Names"] mutableCopy];
BTW:
You can also save self.profileInfo to file directly using [self.profileInfo writeToFile:path atomically:YES].

how to store an image path in a plist?

I know this is probably a silly question but I'm storing most of my game data in a plist - with that I'd like to include references to images used within my game - same hierarchal level as 'supporting files'. I have different types of images stored in 3 separate folders. One folder for example is called imageclue. How could I store the path in my plist, I'm stuck because I can't just store the path in my plist as string - filename.jpg. I've tried getting the path of the file but when I log it out it .
Sorry if I'm not explaining well and thank you in advance for any help :)
EDIT**
I have a plist file added to my program I don't want to programatically add to it as the images are constants - the screenshots below show a tutorial instead of the filename.jpg (because that won't work seen as my images are stored in a file) I wondered what path name do I use as a string.
The image is from a tutorial off of appcoda.com - where it says thumbnails are the image path files. If you look at where the images are stored on the left - they are stored with the program files. My images are in a folder in there so I'm confused as to what to enter in my plist for the image file.
Hope this clears up what I meant, sorry :)
Store three variables in .h file
#interface YourViewController : UIViewController
{
NSString *folder1;
NSString *folder2;
NSString *folder3;
}
in viewdidload:
-(void) viewdidLoad
{
//get the documents directory:
NSArray *paths = NSSearchPathForDirectoriesInDomains
(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
//getting the folder name:
folder1 = [NSString stringWithFormat:#"%#/imageclue",
documentsDirectory];
folder2 = [NSString stringWithFormat:#"%#/folder2",
documentsDirectory];
folder3 = [NSString stringWithFormat:#"%#/folder3",
documentsDirectory];
}
-(NSArray*) getPlistFromFolder:(NSString*)folder imageName:(NSString*)image
{
NSString *imageTitle = [NSString stringWithFormat:#"%#/image",
folder];
NSArray *data = [[NSArray alloc] initWithContentsOfFile:plistName];
return data;
}
So in the plist file, just store the image name.
Hope this helps...
Do it like this,
NSDictionary *imagePaths = #{#"image 1": [NSHomeDirectory() stringByAppendingPathComponent:#"image 1"]};
[self writeToPlist:imagePaths];
- (void)writeToPlist:imagePaths:(id)plist{
NSError *error;
NSData *data = [NSPropertyListSerialization dataWithPropertyList:plist format:kCFPropertyListXMLFormat_v1_0 options:0 error:&error];
if(error){
NSLog(#"Could not write to file");
return;
}
[data writeToFile:[self plistPath] atomically:YES];
}
Like wise loading is simple as this;
[self loadImagePathForImageNamed:#"image 1"];
- (NSString*)loadImagePathForImageNamed:(NSString*)imageName{
}
- (NSString*)loadImagePathForImageNamed:(NSString*)imageName{
NSData *data = [NSData dataWithContentsOfFile:[self plistPath]];
NSString *error;
NSPropertyListFormat format;
NSDictionary *dictionary = [NSPropertyListSerialization propertyListFromData:data mutabilityOption:NSPropertyListImmutable format:&format errorDescription:&error];
if(error){
NSLog(#"Could not open plist %#", error);
return nil;
}
return dictionary[imageName];
}
You may have to handle the error when the file is not there by creating a new one, otherwise this should work.
You are storing path right way, just need to store filename of image with extension in plist when your images are in your Application Bundle, for more reference you can define key name Instead "item1", "item2" in your plist.
Now coming to actual Question, how to access image from plist
Step 1 : Read your recipes.plist from Application Bundle
NSString *bundlePath = [[NSBundle mainBundle] pathForResource:#"recipes" ofType:#"plist"];
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:bundlePath];
Step 2 : Now Get Image/Thumbnails name out of it, which you want to load
Step 3 : Define following Function in your Controller, which returns image from name
- (UIImage *)getImageWithName:(NSString *)imageFileName
{
NSString *ext = [imageFileName pathExtension];
NSString *imagePath = [[NSBundle mainBundle] pathForResource:[imageFileName stringByDeletingPathExtension] ofType:ext];
return [UIImage imageWithContentsOfFile:imagePath];
}
HOW TO USE
Suppose you want to load Image with key "Item2" then write following code
NSString *imageFileName = [[dict objectForKey:#"Thumbnail"] valueForKey:#"Item2"];
UIImage *item2Image = [self getImageWithName:imageFileName];
For "Item6"
NSString *imageFileName1 = [[dict objectForKey:#"Thumbnail"] valueForKey:#"Item6"];
UIImage *item6Image = [self getImageWithName:imageFileName1];

iOS release not working as expected

I am using this code to get book names from a config.plist file. However my memory management is problematic. The '[dict release]' breaks the app completely and it exits.
The code works when the '[dict release]' is removed but it causes memory leaks as far as I can tell.
bnames is a global NSMutableArray
What am I doing wrong?
- (NSString *)loadBookname: (NSInteger) bookToLoad {
bookToLoad = [self bookOrder:bookToLoad];
//---get the path to the property list file---
plistFileNameConf = [[self documentsPath] stringByAppendingPathComponent:#"Config.plist"];
//---if the property list file can be found---
if ([[NSFileManager defaultManager] fileExistsAtPath:plistFileNameConf]) {
//---load the content of the property list file into a NSDictionary object---
dict = [[NSDictionary alloc] initWithContentsOfFile:plistFileNameConf];
bnames = [dict valueForKey:#"BookNames"];
[dict release];
}
else {
//---load the property list from the Resources folder---
NSString *pListPath = [[NSBundle mainBundle] pathForResource:#"Config" ofType:#"plist"];
dict = [[NSDictionary alloc] initWithContentsOfFile:pListPath];
bnames = [dict valueForKey:#"BookNames"];
[dict release];
}
plistFileNameConf = nil;
NSString *bookNameTemp;
bookNameTemp = [bnames objectAtIndex:bookToLoad - 1];
NSLog(#"bookName: %#", bookNameTemp);
return bookNameTemp;
}
You need to allocate your array properly:
bnames = [[NSArray alloc] initWithArray:[dict valueForKey:#"BookNames"]];
Double check that your dict returns the right data type.
There does not appear to be anything wrong with the way you allocate NSDictionary (although you could also use the [NSDictionary dictionaryWithContentsOfFile:] and save yourself having to worry about the release.
Either way I would suggest the issue is not with the [release] but probably the line BEFORE release:
bnames = [dict valueForKey:#"BookNames"];
a) Where is that allocated. I don't see an allocation or declaration of it anywhere?
b) What type of value do you expect back?
Put a break point on it and make sure your getting what you expect or anything.
If dict is not already a strong property, make it one. Then, use self.dict when assigning to it (and keep the release).
I've found what appears to be a better solution to the issue. This lets iOS manage the memory.
//---finds the path to the application's Documents directory---
- (NSString *) documentsPath {
NSLog(#"Start documentsPath");
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDir = [paths objectAtIndex:0];
// NSLog(#"Found documentsPath 40");
NSLog(#"End documentsPath");
return documentsDir;
}
- (NSString *) configPath {
NSLog(#"Start configPath");
NSString *plistFileNameConf = [[self documentsPath] stringByAppendingPathComponent:#"Config.plist"];
if (![[NSFileManager defaultManager] fileExistsAtPath:plistFileNameConf]) {
plistFileNameConf = [[NSBundle mainBundle] pathForResource:#"Config" ofType:#"plist"];
}
NSLog(#"plistFile: %#",plistFileNameConf);
NSLog(#"End configPath");
return plistFileNameConf;
}
The following calls the above code as necessary:
NSString *Choice;
NSArray *properties;
NSString *errorDesc = nil;
NSPropertyListFormat format;
NSData *plistXML = [[NSFileManager defaultManager] contentsAtPath:[self configPath]];
NSDictionary *temp = (NSDictionary *)[NSPropertyListSerialization propertyListFromData:plistXML mutabilityOption:NSPropertyListMutableContainersAndLeaves format:&format errorDescription:&errorDesc];
if (!temp) {
NSLog(#"Error reading plist: %#, format: %d", errorDesc, format);
}
Choice = [temp objectForKey:#"Choice"];
properties = [temp objectForKey:Choice];

Resources