Will property list data persist after killing the application? - ios

I have created a custom property list file. The file is stored in the application document.
While user login is successful the login information is stored in the plist, and it is working fine.
The plist content are cleared while log out, this also works fine.
When i am still login i killed the application. When the app opens the plist data i cleared.
code used to save to file:
NSArray *paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsPath = [paths objectAtIndex:0];
NSString *plistPath = [documentsPath stringByAppendingPathComponent:#"xxxxPlist.plist"];
if (![[NSFileManager defaultManager] fileExistsAtPath:plistPath])
{
plistPath = [[NSBundle mainBundle] pathForResource:#"xxxxPlist" ofType:#"plist"];
}
dict=[[self cleanDictionary:[dict mutableCopy]] mutableCopy];
NSDictionary *plistDict=[[NSDictionary alloc] initWithObjectsAndKeys:dict,#"login_data", nil];
NSError *error = nil;
NSData *plistData = [NSPropertyListSerialization dataWithPropertyList:plistDict format:NSPropertyListXMLFormat_v1_0 options:NSPropertyListImmutable error:&error];
if(plistData)
{
[plistData writeToFile:plistPath atomically:YES];
}
else
{
//error here
NSLog(#"%# ",error);
}
code used to fetch data
NSArray *paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsPath = [paths objectAtIndex:0];
NSString *plistPath = [documentsPath stringByAppendingPathComponent:#"xxxx.plist"];
if (![[NSFileManager defaultManager] fileExistsAtPath:plistPath])
{
plistPath = [[NSBundle mainBundle] pathForResource:#"xxxxPlist" ofType:#"plist"];
}
NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile:plistPath];
return [dict objectForKey:#"login_data"];
I there any way out to persist the data?

There are several things that may be causing problems
When saving to file
1) My understanding is that you specifically want to save to /Documents folder specifically to ensure your file persists
2) So you correctly build following path
"/Documents/xxxxPlist.plist"
3) But then why do you check if a file already exists at that location?
if (![[NSFileManager defaultManager] fileExistsAtPath:plistPath])
{
plistPath = [[NSBundle mainBundle] pathForResource:#"xxxxPlist" ofType:#"plist"];
}
You just have to write to the path when you are ready.
If there is an old file at this location it will be overwritten.
And my understanding is this is the wanted behaviour, because you've already read that file and the data is in that dictionary "dict".
4) Also, by asking NSBundle to give you path for your file name
"xxxxPlist.plist"
there is a risk that it will just give you back some other path with file named the same (not in /Documents) if such file happens to exist. For example if you happened to write to say, /Cache folder earlier (with different code), your app will keep getting the /Cache path and keep reading/writong there (not in /Documents). And with the existing code you would have gotten nil here for path on the very first run, so not sure how he file got created in the first place.
5) Then I am not sure what exactly does this line
dict=[[self cleanDictionary:[dict mutableCopy]] mutableCopy];
Why first make a mutable copy, then presumably get immutable copy back and get a mutable one of it. Can't -cleanDictionary: just return the same mutable copy it was passed?
When reading from file
1) Not sure why you're searching for a different file first?
"/Documents/xxxx.plist" not "/Documents/xxxxPlist.plist"
Also what happens if "xxxx.plist" exists, then you'll never get to "xxxxPlist.plist" that you are writing in the other section.
2) Then, yes, you have to check if a file exists at certain path before you try to read it. But, in your case, if it does not exist, you don't ask NSBundle for another location, because you need your specific file in /Documents, and you don't know what you'll get from NSBundle, if your file is not where it should be.
So if there is no "xxxxPlist.plist" file, it's just your first run of the app and you will be creating your initial dict.

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
}

Loading files from disk to NSMutableArray removes NSMutableArray from memory

I'm loading a file to a NSMutableArray. I'm doing it like this:
if(!self.dataArray){
self.dataArray = [[NSMutableArray alloc] init];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES);
NSString *arrayPath = [[paths objectAtIndex:0]
stringByAppendingPathComponent:#"array.out"];
self.dataArray = [NSMutableArray arrayWithContentsOfFile:arrayPath];
}
The array that is loaded into the file consists of multiple NSDictionaries.
However, this somehow deallocates the array in the memory because when I log dataArray after doing this, it logs nil. How come?
Update
I've figured out that [NSMutableArray arrayWithContentsOfFile:arrayPath] is logging nil because the code in which I'm uploading the content to the file, doesn't create the file:
// write data to disk
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *arrayPath = [[paths objectAtIndex:0]stringByAppendingPathComponent:#"array.out"];
[self.dataArray writeToFile:arrayPath atomically:YES];
NSLog(#"uploaded file: %#", arrayPath); // logs an arrayPath, but one that doesn't exists.
Check the following,
Check the dataArray is a weak property ? If so, change to strong.
Check the file exists at path, using
[[NSFileManager defaultManager] fileExistsAtPath:arrayPath];
Verify the file have expected content, by logging it.
Confirm the File content is organized as a property list (plist). Verify it in plist editor/Xcode.
If you dynamically creating it, check the path you are writing to.
Confirm the method of writing NSArray to plist. Use
[array writeToFile:path atomically:YES];
Note:
If you are dynamically creating the file and you are testing on Simulator; you can
find the file by logging file path and following it on Finder.
Property List Reference
Apple documentation
Per Apple documentation, the array returns nil if the file can’t be opened or if the contents of the file can’t be parsed into an array.
Did you use the [writeToFile:atomically:] method to write the array to a file?
Also, make sure that the filePath string matches exactly on both write and read ends. I've wasted a lot of time trying to hunt down a bug when it turned out I had misspelled the name of the file or used the wrong file extension.
Another possibility: have you confirmed that this code is being executed? Sometimes I've had to change (!self.someProperty) to (self.someProperty != nil) in my if condition to get code like this to run.
Peter Segerblom and wildBillMunson are right: the array returns nil if the file can't be opened or if its content can't be parsed into an array.
You said "array.out" is an array of NSDictionaries. Whenever I have that set of data, I use the plist type of file and read it this way:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
NSString *directory = [paths objectAtIndex:0];
NSString *fullPath = [directory stringByAppendingPathComponent:#"data.plist"];
NSString *errorDesc = nil;
NSPropertyListFormat format;
NSData *plistXML = [[NSFileManager defaultManager] contentsAtPath:fullPath];
NSArray *data = (NSArray *)[NSPropertyListSerialization propertyListFromData:plistXML mutabilityOption:NSPropertyListMutableContainersAndLeaves format:&format errorDescription:&errorDesc];
Be sure to check if fullPath is not returning nil.
Hope this helps!

Why can't I retrieve my plist file?

I have a plist file I just created of strings; it looks like this:
This is the code I'm using to create the path to the file:
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); // Create a list of paths
NSString *documentsDirectory = [paths objectAtIndex:0]; // Get a path to your documents directory from the list
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"NailServices.plist"]; // Create a full file path
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath: path]) { // Check if file exists
// Get a path to the plist created before in bundle directory (by Xcode)
NSString *bundle = [[NSBundle mainBundle] pathForResource: #"NailServices" ofType: #"plist"];
[fileManager copyItemAtPath:bundle toPath: path error:&error]; // Copy this plist to your documents directory
}
This is the code I'm using to examine the data (to make sure this is working)... I'm getting a (null) back from the NSLog statement)
//Load Dictionary with wood name cross refference values for image name
NSString *plistDataPath = [[NSBundle mainBundle] pathForResource:#"NailServices" ofType:#"plist"];
NSDictionary *NailServicesDictionary = [[NSDictionary alloc] initWithContentsOfFile:plistDataPath];
NSLog(#"\nnailServicesDict: %#", NailServicesDictionary);
This is my first attempt at creating/using a "strings" plist file; I have read everything I could find on Google and SO without finding an example of a plain ol' strings file. What else do I have to do to be able to get to this plist data?
Your problem is that you are creating an NSDictionary while your plist is an NSArray. Thus, it will return nil when you try to create it as a dictionary because no dictionary exists.
You need to change:
NSDictionary *NailServicesDictionary = [[NSDictionary alloc] initWithContentsOfFile:plistDataPath];
to
NSArray *NailServicesArray = [[NSArray alloc] initWithContentsOfFile:plistDataPath];
As a commenter posted, plist files can either have an NSArray or an NSDictionary as their root. Your example plist has an NSArray as its root, so you'll want to alloc and init an NSArray, not an NSDictionary. If your plist is stored in the app bundle when you build the app in Xcode and you don't need to modify it at runtime, then it's unnecessary to copy it to the NSDocumentsDirectory. Also, I'd recommend using [paths lastObject]; rather than [paths objectAtIndex:0];, which can throw an exception if the paths array is empty.

writing string to txt file in objective c

Pulling my hair out trying to work this out. i want to read and write a list of numbers to a txt file within my project. however [string writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:&error] doesnt appear to write anything to the file. I can see there is the path string returns a file path so it seems to have found it, but just doesnt appear to write anything to the file.
+(void)WriteProductIdToWishList:(NSNumber*)productId {
for (NSString* s in [self GetProductsFromWishList]) {
if([s isEqualToString:[productId stringValue]]) {
//exists already
return;
}
}
NSString *string = [NSString stringWithFormat:#"%#:",productId]; // your string
NSString *path = [[NSBundle mainBundle] pathForResource:#"WishList" ofType:#"txt"];
NSError *error = nil;
[string writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:&error];
NSLog(#"%#", error.localizedFailureReason);
// path to your .txt file
// Open output file in append mode:
}
EDIT: path shows as /var/mobile/Applications/CFC1ECEC-2A3D-457D-8BDF-639B79B13429/newAR.app/WishList.txt so does exist. But reading it back with:
NSString *path = [[NSBundle mainBundle] pathForResource:#"WishList" ofType:#"txt"];
returns nothing but an empty string.
You're trying to write to a location that is inside your application bundle, which cannot be modified as the bundle is read-only. You need to find a location (in your application's sandbox) that is writeable, and then you'll get the behavior you expect when you call string:WriteToFile:.
Often an application will read a resource from the bundle the first time it's run, copy said file to a suitable location (try the documents folder or temporary folder), and then proceed to modify the file.
So, for example, something along these lines:
// Path for original file in bundle..
NSString *originalPath = [[NSBundle mainBundle] pathForResource:#"WishList" ofType:#"txt"];
NSURL *originalURL = [NSURL URLWithString:originalPath];
// Destination for file that is writeable
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSURL *documentsURL = [NSURL URLWithString:documentsDirectory];
NSString *fileNameComponent = [[originalPath pathComponents] lastObject];
NSURL *destinationURL = [documentsURL URLByAppendingPathComponent:fileNameComponent];
// Copy file to new location
NSError *anError;
[[NSFileManager defaultManager] copyItemAtURL:originalURL
toURL:destinationURL
error:&anError];
// Now you can write to the file....
NSString *string = [NSString stringWithFormat:#"%#:", yourString];
NSError *writeError = nil;
[string writeToFile:destinationURL atomically:YES encoding:NSUTF8StringEncoding error:&error];
NSLog(#"%#", writeError.localizedFailureReason);
Moving forward (assuming you want to continue to modify the file over time), you'll need to evaluate if the file already exists in the user's document folder, making sure to only copy the file from the bundle when required (otherwise you'll overwrite your modified file with the original bundle copy every time).
To escape from all the hassle with writing to a file in a specific directory, use the NSUserDefaults class to store/retrieve a key-value pair. That way you'd still have hair when you're 64.

.plist path on iOS device

-(void)login{
NSBundle *bundle = [NSBundle mainBundle];
NSString *path = [bundle pathForResource:#"login" ofType:#"plist"];
NSMutableDictionary* plistDict = [[NSMutableDictionary alloc] initWithContentsOfFile:path];
[plistDict setObject:#"si" forKey:#"stato"];
[plistDict writeToFile:path atomically: YES];
}
In iOS Simulator the plist has been correctly written, but when I try to write the .plist on my iPhone, it doesn't work. I guess it is because of the wrong .plist path.
Do the iOS devices use different path?
First you have to check if the file exits in your documents directory. If it doesn't exits there then you can copy it to the document directory. You can do it this way
-(void)login{
BOOL doesExist;
NSError *error;
NSString *filePath= [[NSBundle mainBundle] pathForResource:#"login" ofType:#"plist"];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString * path =[[NSString alloc] initWithString:[documentsDirectory stringByAppendingPathComponent:#"login.plist"]];
doesExist= [fileManager fileExistsAtPath:path];
if (doesExist) {
NSMutableDictionary* plistDict=[[NSMutableDictionary alloc] initWithContentsOfFile:path];
}
else
{
doesExist= [fileManager copyItemAtPath:filePath toPath:path error:&error];
NSMutableDictionary* plistDict=[[NSMutableDictionary alloc] initWithContentsOfFile:filePath];
}
[plistDict setObject:#"si" forKey:#"stato"];
[plistDict writeToFile:path atomically: YES];
}
You can't write to the [NSBundle mainBundle] location. In order to write files like a plist, you should save in the documents folder, this way:
NSArray *arrayPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES);
NSString *filePathToSave = [arrayPaths objectAtIndex:0];
If the plist is part of your app, I would recommend you, in the first launch, to already copy it to the documents folder using the same filePathToSave, so you will always look at it there, both to read or to save.
This is a big mistake, as the main bundle only is readable and only composed at compile time in the App Bundle. The App Bundle lives in a separate place, whereas the data you should write to disk should be placed into the Documents, Temporary or Library folder of your sandbox.
To gain more understanding please read the official File System Programming Guide.
Everything you need to know is written there.
You can also write to subfolders and you should choose between the 3 above mentioned main directories in terms of backing up, when syncing with iTunes or iCloud. For instance contents in the tmp Folder won't be backed up.
You can not write to the mainBundle on an iOS device. You will have to save the file to a directory and modify it there.
Just to bring the answers into the modern world - you should really be using the URL based methods for getting directories:
NSFileManager *fileManager = [[NSFileManager alloc] init];
NSURL *URLForDocumentsDirectory = [[fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject]

Resources