Im following an Objective-C tutorial and on this task I have to make an address book style app that stores contact details after pressing a button and then on the next load restores them.
Having problems figuring out why text doesn't appear to be being saved - so will obviously not load next time I load the app in the simulator.
[super viewDidLoad];
NSFileManager *filemgr;
NSString *documentsDirectory;
NSArray *directoryPaths;
filemgr =[NSFileManager defaultManager];
directoryPaths = NSSearchPathForDirectoriesInDomains(NSDocumentationDirectory, NSUserDomainMask, YES);
documentsDirectory =[directoryPaths objectAtIndex:0];
filePath =[[NSString alloc]initWithString:[documentsDirectory stringByAppendingPathComponent:#"userInfo.dat"]];
if ([filemgr fileExistsAtPath:filePath])
{
NSMutableArray *userData;
userData =[NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
txtFirstName.text =[userData objectAtIndex:0];
txtLastName.text =[userData objectAtIndex:1];
txtEmail.text =[userData objectAtIndex:2];
txtPhone.text =[userData objectAtIndex:3];
NSLog(#"File Has Been loaded");
}else
{
NSLog(#"Error.");
}
}
- (IBAction)btnSaveDetails:(id)sender {
NSMutableArray *contactDetails;
contactDetails =[[NSMutableArray alloc]init];
[contactDetails addObject:txtFirstName.text];
[contactDetails addObject:txtLastName.text];
[contactDetails addObject:txtEmail.text];
[contactDetails addObject:txtPhone.text];
[NSKeyedArchiver archiveRootObject:contactDetails toFile:filePath];
NSLog(#"File Saved");
}
Thanks in advance
Have you tried this?
-(id)viewDidLoad
{
NSMutableArray * userData = [[NSMutableArray alloc] init];
NSData * downloadedData = [[NSData alloc] initWithContentsOfFile: [NSHomeDirectory() stringByAppendingPathComponent:#"Documents/userInfo.plist"]];
if (downloadedData.length)
[userData setArray:[NSKeyedUnarchiver unarchiveObjectWithData:downloadedData]];
NSLog(#"userData :%#", userData);
downloadedData = nil;
}
-(IBAction)btnSaveDetails:(id)sender
{
NSMutableArray * contactDetails =[[NSMutableArray alloc]init];
[contactDetails addObject:txtFirstName.text];
[contactDetails addObject:txtLastName.text];
[contactDetails addObject:txtEmail.text];
[contactDetails addObject:txtPhone.text];
NSData * contactData = [NSKeyedArchiver archivedDataWithRootObject:contactDetails];
[contactData writeToFile:[NSHomeDirectory() stringByAppendingPathComponent:#"Documents/userInfo.plist"] atomically:YES];
contactData = nil;
}
Related
I successfully added my dictionary to plist but when i add 2nd time it remove my old dictionary from my plist.
Here is my code :
NSArray *paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsPath = [paths objectAtIndex:0];
NSString *plistPath = [documentsPath stringByAppendingPathComponent:#"favQuote.plist"];
NSDictionary *plistDict = [[NSDictionary alloc] initWithObjectsAndKeys:self.txtQuote.text,#"quote",self.lblAuthor.text,#"author",nil];
NSError *error = nil;
NSData *plistData = [NSPropertyListSerialization dataWithPropertyList:plistDict format:NSPropertyListXMLFormat_v1_0 options:NSPropertyListWriteStreamError error:&error];
if(plistData)
{
[plistData writeToFile:plistPath atomically:YES];
NSLog(#"Data saved sucessfully");
}
else
{
NSLog(#"Data not saved");
}
How do I add my new dictionary without losing old data?
You should store your dictionary of array in your document directory something like,
NSArray *paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsPath = [paths objectAtIndex:0];
NSString *plistPath = [documentsPath stringByAppendingPathComponent:#"favQuote.plist"];
NSFileManager *manager = [NSFileManager defaultManager];
NSMutableArray *arr = [[NSMutableArray alloc]init];
if ( [manager fileExistsAtPath:plistPath]) {
NSData *data = [NSData dataWithContentsOfFile:plistPath];
arr = (NSMutableArray*)[NSKeyedUnarchiver unarchiveObjectWithData:data];
}
NSDictionary *plistDict = [[NSDictionary alloc] initWithObjectsAndKeys:#"test",#"quote",#"test2",#"author",nil];
[arr addObject:plistDict];
NSData *plistData = [NSKeyedArchiver archivedDataWithRootObject:arr];
if(plistData)
{
[plistData writeToFile:plistPath atomically:YES];
NSLog(#"Data saved sucessfully");
}
else
{
NSLog(#"Data not saved");
}
then you can retrieve and use your values like,
NSMutableArray *arr1 = [[NSMutableArray alloc]init];
if ( [manager fileExistsAtPath:plistPath]) {
NSData *data = [NSData dataWithContentsOfFile:plistPath];
arr1 = (NSMutableArray*)[NSKeyedUnarchiver unarchiveObjectWithData:data];
}
if (arr1.count > 0) {
NSDictionary *firstDic = [arr1 objectAtIndex:0];
}
+ (NSString *)getValueforLocale:(NSString*) i18nkey :(NSString*)locale{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
NSLog(#"paths are : %#",paths);
NSString *libraryDirectory = [paths objectAtIndex:0];
NSLog(#"libraryDirectory : %#",libraryDirectory);
NSString *filePath = [libraryDirectory stringByAppendingPathComponent:#"I8nDB"];
filePath = [filePath stringByAppendingPathComponent:locale];
NSLog(#"file path is : %#",filePath);
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:filePath];
if(fileExists)
{
NSDictionary *dict = [[[NSDictionary alloc] initWithContentsOfFile:filePath]autorelease];
NSDictionary *resourceBundle = [[[NSDictionary alloc] init]autorelease];
NSString *keyValue = [[[NSString alloc]init]autorelease];
resourceBundle = [dict valueForKey:#"hash"];
keyValue=[resourceBundle valueForKey:i18nkey];
NSLog(#"value for %# is(container) : %#",i18nkey,keyValue);
if(keyValue != nil || keyValue != NULL)
{
return keyValue;
}
else
{
NSLog(#"key not found in the container file");
NSString *path = [[NSBundle mainBundle] pathForResource:#"Localizable"
ofType:#"strings"
inDirectory:nil
forLocalization:locale];
NSLog(#"path for %# is : %#",locale,path);
fileExists = [[NSFileManager defaultManager] fileExistsAtPath:path];
if(fileExists)
{
NSDictionary *dict = [[NSDictionary dictionaryWithContentsOfFile:path]autorelease];
NSLog(#"value for %# is(resources) : %#",i18nkey,[dict objectForKey:i18nkey]);
return [dict objectForKey:i18nkey];
}
else
{
return NULL;
}
}
}
else
{
NSLog(#"%# locale does not exist in container",locale);
NSString *path = [[NSBundle mainBundle] pathForResource:#"Localizable"
ofType:#"strings"
inDirectory:nil
forLocalization:locale];
NSLog(#"path for %# in resources is : %#",locale,path);
fileExists = [[NSFileManager defaultManager] fileExistsAtPath:path];
if(fileExists)
{
NSDictionary *dict = [[NSDictionary dictionaryWithContentsOfFile:path]autorelease];
NSLog(#"value for %# is : %#",i18nkey,[dict objectForKey:i18nkey]);
return [dict objectForKey:i18nkey];
}
else
{
return NULL;
}
}
}
If we remove Autorelease from the above code, it is working in iOS7 if not the app is crashing
My Main concern is why it doesn't crash in iOS8&9 and only crashes in iOS7
is there in change related to autorelease over these versions
Why don't you use ARC? Then you won't need autorelease...
See http://rypress.com/tutorials/objective-c/memory-management
Your problem might be related to the settings about ARC.
in Your code you only alloc a dictionary in
NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:filePath];
so you only need to care about it, another object is not owned by You! so You don't need release or autorelease them.
Try flowing code
+ (NSString *)getValueforLocale:(NSString*) i18nkey :(NSString*)locale
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
NSLog(#"paths are : %#",paths);
NSString *libraryDirectory = [paths objectAtIndex:0];
NSLog(#"libraryDirectory : %#",libraryDirectory);
NSString *filePath = [libraryDirectory stringByAppendingPathComponent:#"I8nDB"];
filePath = [filePath stringByAppendingPathComponent:locale];
NSLog(#"file path is : %#",filePath);
BOOL fileExists = [[NSFileManager defaultManager] fileExistsAtPath:filePath];
if(fileExists)
{
NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:filePath];
//NSDictionary *resourceBundle = [[[NSDictionary alloc] init]autorelease];
//NSString *keyValue = [[[NSString alloc]init]autorelease];
NSDictionary *resourceBundle = [dict valueForKey:#"hash"];
// relese dict here because not use after
[dict release];
NSString *keyValue=[resourceBundle valueForKey:i18nkey];
NSLog(#"value for %# is(container) : %#",i18nkey,keyValue);
if(keyValue != nil || keyValue != NULL)
{
return keyValue;
}
else
{
NSLog(#"key not found in the container file");
NSString *path = [[NSBundle mainBundle] pathForResource:#"Localizable"
ofType:#"strings"
inDirectory:nil
forLocalization:locale];
NSLog(#"path for %# is : %#",locale,path);
fileExists = [[NSFileManager defaultManager] fileExistsAtPath:path];
if(fileExists)
{
// NSDictionary *dict = [[NSDictionary dictionaryWithContentsOfFile:path]autorelease];
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path];
NSLog(#"value for %# is(resources) : %#",i18nkey,[dict objectForKey:i18nkey]);
return [dict objectForKey:i18nkey];
}
else
{
return NULL;
}
}
}
else
{
NSLog(#"%# locale does not exist in container",locale);
NSString *path = [[NSBundle mainBundle] pathForResource:#"Localizable"
ofType:#"strings"
inDirectory:nil
forLocalization:locale];
NSLog(#"path for %# in resources is : %#",locale,path);
fileExists = [[NSFileManager defaultManager] fileExistsAtPath:path];
if(fileExists)
{
// NSDictionary *dict = [[NSDictionary dictionaryWithContentsOfFile:path]autorelease];
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:path];
NSLog(#"value for %# is : %#",i18nkey,[dict objectForKey:i18nkey]);
return [dict objectForKey:i18nkey];
}
else
{
return NULL;
}
}
}
In manual reference counting, retains and releases need to be balanced.
In
NSDictionary *dict = [[[NSDictionary alloc] initWithContentsOfFile:filePath]autorelease];
NSDictionary *resourceBundle = [[[NSDictionary alloc] init]autorelease];
the retains and releases are balanced, because alloc (along with retain, new, copy, mutableCopy) returns a retained instance, and autorelease counts as a release.
However, in
NSDictionary *dict = [[NSDictionary dictionaryWithContentsOfFile:path]autorelease];
you have an overrelease because you are autorelease something that you have not retained.
iOS version has absolutely nothing to do with it.
I have a simple objective c program in which I save user entered data in a plist. But when I try to save multiple data in my plist it rewrites over the old data and I can just get only one data in my plist, i.e. the last data that the user has entered. Here is my code below to save the data -
- (IBAction)savedata:(id)sender
{
NSError *error;
NSString *arr= [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
arr=[arr stringByAppendingPathComponent:#"datalist.plist"];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSMutableArray *valuearray = [[NSMutableArray alloc]init];
NSMutableDictionary *dict = [[NSMutableDictionary alloc]init];
[dict setValue:_nametextfield.text forKey:#"NAME"];
[valuearray addObject:dict];
if ([fileManager fileExistsAtPath:arr])
{
NSLog(#"FILE EXIST");
self.bundle = [[NSBundle mainBundle]pathForResource:#"datalist" ofType:#"plist" ];//NSString *bundle = [[NSBundle mainBundle] pathForResource:#”data” ofType:#”plist”];
NSLog(#"%#",_bundle);
[fileManager copyItemAtPath:_bundle toPath: arr error:&error];
[valuearray writeToFile:arr atomically:YES];
NSLog(#"%#",arr);
//6
}
else
{
NSLog(#"FILE DOESN'T EXIST");
[valuearray writeToFile:arr atomically:YES];
}
}
And here is my fetch data -
- (IBAction)fetchdata:(id)sender
{
NSString *arr= [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
arr=[arr stringByAppendingPathComponent:#"datalist.plist"];
if ([[NSFileManager defaultManager]fileExistsAtPath:arr])
{
NSArray *dict=[NSArray arrayWithContentsOfFile:arr];
NSLog(#"%#",dict);
}
}
Now I can only retrieve the last entered data and I don't know why is it causing so much problem. Please somebody help me on this. I have also attached a screenshot of my main bundle.
Replace the save part with this -
- (IBAction)savedata:(id)sender
{
NSError *error;
NSString *arr= [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
arr=[arr stringByAppendingPathComponent:#"datalist.plist"];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSMutableArray *valuearray = [[NSMutableArray alloc]init];
NSMutableDictionary *dict = [[NSMutableDictionary alloc]init];
[dict setValue:_titletextfield.text forKey:#"NAME"];
[valuearray addObject:dict];
if ([fileManager fileExistsAtPath:arr])
{
NSLog(#"FILE EXIST");
//self.bundle = [[NSBundle mainBundle]pathForResource:#"datalist" ofType:#"plist" ];//NSString *bundle = [[NSBundle mainBundle] pathForResource:#”data” ofType:#”plist”];
// NSLog(#"%#",_bundle);
//[fileManager copyItemAtPath:_bundle toPath: arr error:&error];
NSMutableArray *valuearray = [[NSMutableArray alloc]initWithContentsOfFile:arr];
if (dict.count)
{
[valuearray addObject:dict];
}
else
{
NSLog(#"data not retrieved");
}
[valuearray writeToFile:arr atomically:YES];
NSLog(#"%#",arr);
//6
}
else
{
NSLog(#"FILE DOESN'T EXIST");
[valuearray writeToFile:arr atomically:YES];
}
UIAlertView *alert=[[UIAlertView alloc]initWithTitle:#"sucess" message:#"successfully saved" delegate:self cancelButtonTitle:#"ok" otherButtonTitles: nil];
[alert show];
[self.navigationController popToRootViewControllerAnimated:YES];
}
Thanks to #rmaddy.
I am trying to fetch Images which i am storing in directory which i have shown in below code . I have tried a lot in StachOverFlow And Chats but not able to achieve the task . Actually i want to generate array of images from the array of filePath which are storing the path of images . Which i will show in UICollectionView . Please check my code and tell me what all can be done to achieve the needed . Thanks in advance
I have array of filepath already generated , i just want to fetch images from them and show it in grid view
-(void)plistRender{
// 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:#"PhotoBucket.plist"];
//pngDATA.....
NSString *totalName = [NSString stringWithFormat:#"EGK_%# ", [NSDate date]];
PhotodocumentsPath = [paths objectAtIndex:0];
PhotofilePath = [PhotodocumentsPath stringByAppendingPathComponent:totalName]; //Add the file name
NSData *pngData = UIImagePNGRepresentation(printingImage);
//Write image to the file directory .............
[pngData writeToFile:[self documentsPathForFileName:PhotofilePath] atomically:YES];
[photos_URL addObject:PhotofilePath];
[photos addObject:totalName];
[grid_copy addObject:[NSNumber numberWithInteger:count]];
[grids addObject:whichProduct];
[Totalamount addObject:[NSNumber numberWithInteger:amt]];
NSDictionary *plistDictionary = [NSDictionary dictionaryWithObjects: [NSArray arrayWithObjects: photos,grids,grid_copy,Totalamount,nil] forKeys:[NSArray arrayWithObjects: #"Photo_URL",#"Product",#"Copy",#"Amount", nil]];
NSString *error = nil;
// create NSData from dictionary
NSData *plistData = [NSPropertyListSerialization dataFromPropertyList:plistDictionary format:NSPropertyListXMLFormat_v1_0 errorDescription:&error];
// check is plistData exists
if(plistData)
{
// write plistData to our Data.plist file
[plistData writeToFile:plistPath atomically:YES];
}
else
{
NSLog(#"Error in saveData: %#", error);
}
NSString *string = [[NSString alloc] initWithData:plistData encoding:NSUTF8StringEncoding];
NSLog(#" plist Data %#", string);
}
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([segue.identifier isEqualToString:#"PhotoBucket"]){
RecipeCollectionViewController *photoBucket = [segue destinationViewController];
NSLog(#"Prepare Segue%#",photoBucket.photoCollection);
NSLog(#"Number of photos %#",photos_URL);
NSMutableArray *imgQueue = [[NSMutableArray alloc] initWithCapacity:photos_URL.count];
for (NSString* path in photos_URL) {
[imgQueue addObject:[UIImage imageWithContentsOfFile:path]];
}
photoBucket.photoCollection = imgQueue;
}
}
try this
for(int i=0;i<[filePathsArray count];i++)
{
NSString *strFilePath = [filePathsArray objectAtIndex:i];
if ([[strFilePath pathExtension] isEqualToString:#"jpg"] || [[strFilePath pathExtension] isEqualToString:#"png"] || [[strFilePath pathExtension] isEqualToString:#"PNG"])
{
NSString *imagePath = [[stringPath stringByAppendingFormat:#"/"] stringByAppendingFormat:strFilePath];
NSData *data = [NSData dataWithContentsOfFile:imagePath];
if(data)
{
UIImage *image = [UIImage imageWithData:data];
}
}
}
Hi you can fetch like this:
NSArray *pathPlist1 =
NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectoryStr1 = [pathPlist1 objectAtIndex:0];
NSString *plistLocation1 =
[documentsDirectoryStr1 stringByAppendingPathComponent:#"ImageStorage"];
NSFileManager *filemgr;
NSArray *filelist;
int i;
filemgr =[NSFileManager defaultManager];
filelist = [filemgr contentsOfDirectoryAtPath:plistLocation1 error:NULL];
NSLog(#"filelist =%lu",[filelist count]);
cacheImagesArray=[[NSMutableArray alloc] init];
cacheImagesDataArray=[[NSMutableArray alloc] init];
for (i = 0; i < [filelist count]; i++){
NSLog(#"%#", [filelist objectAtIndex: i]);
NSString *imageName=[NSString stringWithFormat:#"%#",[filelist objectAtIndex: i]];
NSString *path=[NSString stringWithFormat:#"%#/%#",plistLocation1, [filelist objectAtIndex: i]];
NSLog(#"Path is =%#",path);
NSData* data = [NSData dataWithContentsOfFile:path];
[cacheImagesDataArray addObject:data];
[cacheImagesArray addObject:imageName];
}
thanks
I am recently having a memory crash and I am suspecting that I am failing to empty the array, This is my code for the array
- (void)viewDidLoad
{
allImagesArray = [[NSMutableArray alloc] init];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *location=#"Bottoms";
NSString *fPath = [documentsDirectory stringByAppendingPathComponent:location];
NSArray *directoryContent = [[NSFileManager defaultManager] directoryContentsAtPath: fPath];
collectionBottoms.delegate =self;
collectionBottoms.dataSource=self;
for(NSString *str in directoryContent){
NSLog(#"i");
NSString *finalFilePath = [fPath stringByAppendingPathComponent:str];
NSData *data = [NSData dataWithContentsOfFile:finalFilePath];
if(data)
{
UIImage *image = [UIImage imageWithData:data];
[allImagesArray addObject:image];
NSLog(#"array:%#",[allImagesArray description]);
}}
}
Ho can I free the memory by releasing the object in the array?
You save the image array in Plist. After save you just re initialize your array it will clear the memory in array. Then are you using ARC or not? Because ARC is more comfortable for this. Then re-initialze or removeAllObject from array in -viewWillDisappear
Hi Just release the memory once object works is over. Here is the code for you.
- (void)viewDidLoad
{
allImagesArray = [[NSMutableArray alloc] init];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *location=#"Bottoms";
NSString *fPath = [documentsDirectory stringByAppendingPathComponent:location];
NSArray *directoryContent = [[NSFileManager defaultManager] directoryContentsAtPath: fPath];
collectionBottoms.delegate =self;
collectionBottoms.dataSource=self;
for(NSString *str in directoryContent)
{
NSString *finalFilePath = [fPath stringByAppendingPathComponent:str];
NSData *data = [NSData dataWithContentsOfFile:finalFilePath];
if(data)
{
UIImage *image = [UIImage imageWithData:data];
[allImagesArray addObject:image];
NSLog(#"array:%#",[allImagesArray description]);
image = nil;
}
finalFilePath=nil;
data=nil;
}
paths= nil;
documentsDirectory= nil;
location= nil;
fPath= nil;
directoryContent = nil;
}
If you are using this image array, allImagesArray, when the view is visible then put this code in viewWillAppear or viewDidAppear methods of UIViewController.
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
allImagesArray = [[NSMutableArray alloc] init];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *location=#"Bottoms";
NSString *fPath = [documentsDirectory stringByAppendingPathComponent:location];
NSArray *directoryContent = [[NSFileManager defaultManager] directoryContentsAtPath: fPath];
collectionBottoms.delegate =self;
collectionBottoms.dataSource=self;
for(NSString *str in directoryContent){
NSLog(#"i");
NSString *finalFilePath = [fPath stringByAppendingPathComponent:str];
NSData *data = [NSData dataWithContentsOfFile:finalFilePath];
if(data)
{
UIImage *image = [UIImage imageWithData:data];
[allImagesArray addObject:image];
NSLog(#"array:%#",[allImagesArray description]);
}
}
}
And, then release it in viewWillDisappear/viewDidDisappear methods of UIViewController.
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
[allImagesArray removeAllObjects];
//[allImagesArray release]; // (Uncomment) Call this only if you are not using ARC.
}
Alternatively, a quick, dirty but safe fix is (without knowing your usage of allImagesArray) -
- (void)viewDidLoad
{
[super viewDidLoad];
// Remove previously added objects.
if (allImagesArray != nil && allImagesArray.count > 0) {
[allImagesArray removeAllObjects];
}
// Allocate memory if not done earlier (i.e. first time).
if (allImagesArray == nil) {
allImagesArray = [[NSMutableArray alloc] init];
}
// Rest of your code as stated in your question
}