How do i save multiple strings and not overwrite while writing to a plist file in iOS? - ios

I'm using a "facts.plist" to display a fact(string) after pressing a button, i have a button there that writes that string to a "favourites.txt" file and there i can use it for future use.
Here is the code for that:
NSArray *paths = NSSearchPathForDirectoriesInDomains
(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *fileName = [documentsDirectory stringByAppendingPathComponent:#"MyFavorites.txt"];
//documentsDirectory];
[self.displayJoke.text writeToFile:fileName
atomically:NO
encoding:NSStringEncodingConversionAllowLossy
error:nil];
NSError *error;
NSString *str = [NSString stringWithContentsOfFile:fileName encoding:NSUTF8StringEncoding error:&error];
NSLog(#"%#", str);
But every time I tap that button the previous string gets overwritten with the new string. How do i create a dictionary or Array to prevent this?

every time i tap that button the previous string gets overwritten with the new string
It doesn't "get overwritten". You are overwriting it:
[self.displayJoke.text writeToFile:fileName
atomically:NO
encoding:NSStringEncodingConversionAllowLossy
error:nil];
That line replaces the file fileName with a new file. If that's not what you want to do, then don't do that. If you want to include the existing contents of the file, it's up to you to read the file first and include that in what you write. (Alternatively, you could look into NSFileHandle, which allows you to append to a file.)

Related

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!

How to load app with local data and subsequently update it when online.

Right now I have an app that successfully parses JSON from my website. So whenever there is no internet connection, my app crashes. Now I am trying to make it so that when the app is loaded with no internet connection, it will show the data that was shown previously. What would be the best way to do this?
I read this article but I don't know how to embed a JSON file into my app bundle. Could someone explain how to do this?
Thanks in advance!
The best way is:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"YourParsedJSON.plist"];
NSFileManager *fileManager = [NSFileManager defaultManager];
if (noInternet){
if ([fileManager fileExistsAtPath: path]){
// if this is true, you have a saved version of your JSON
YourArray = [[NSMutableArray alloc] initWithContentsOfFile: path];
// or
YourDict = [[NSMutableArray alloc] initWithContentsOfFile: path];
}
else{
// first time the app is running, and no internet, no json, inform user about this
}
}
else{
// make an array or dictionary ( what is your JSON )
// response can be a NSDictionary or NSArray
// YourArray = parsedJSON or YourDict = parsedJSON
[YourArray writeToFile:path atomically:YES];
//or
[YourDictionary writeToFile:path atomically:YES];
}
I hope it helps !
Use Apple Reachability sample code to check if your app is able to establish connection to your server.
On first successful request-response, parse the JSON and cache it to disk as a .plist file. This will save you parsing the stored response again. A parsed JSON response can be a NSDictionary or NSArray. Use the writeToFile:atomically: API to write it to disk.
On subsequent request, if reachability fails, i.e. no network connectivity, read the cached response from disk. You need to decide the cache duration and update the plist when a fresh response is fetched.
Hope that helps!
EDIT:
I think I did not understand the question completely. Thanks Xman, for pointing it out. What I would have done in this case is - save the last loaded JSON file to my bundle and use it for displaying information while querying the server and loading updates in the background.
The flow should be like this:
Parse and display data using local JSON file. (Assuming there is local copy of JSON file)
Query the server for latest data.
Upon receiving response, update the bundle with the latest JSON file.
Then, do step 1. In case there is no JSON file, just start from step 2. If there is a Network error display the appropriate information.
This SO question answers how to handle Network connections in iOS: How to check for an active Internet connection on iOS or OSX?
Saving file locally:
Assuming you have the unparsed JSON data in a NSString (responseString) do the following:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [NSString stringWithFormat:#"%#/%#", documentsDirectory, #"latest_json.json"];
NSError *error;
[jsonString_ writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:&error];
NSLog(#"%#", error)
Reading file
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *filePath = [NSString stringWithFormat:#"%#/%#", documentsDirectory, #"latest_json.json"];
NSString *jsonString_ = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:&error];
Previous Answer
Embedding JSON file is similar to embedding any resource into your project. The following method shows you how I added an XML file and accessed it in my app.
Drag and drop your JSON/XML file to your resources group/folder in your XCode window. If you don't have the Resouces folder, it is better you create it. Then in your code do this:
NSString* filePath_ = [[NSBundle mainBundle] pathForResource:#"fileName" ofType:#"json"];
NSString *jsonString = [[NSString alloc] initWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error: NULL];
the variable jsonstring contails the JSON information. It is upto you how you would like to parse it.

Retrieve by specific file name from document directory in iOS

Now i am retrieving file from document directory by specific name in iOS with following code.
NSMutableArray *arrayToSearch = [[NSMutableArray alloc] init];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSError * error;
arrayToSearch = (NSMutableArray *)[[NSFileManager defaultManager] contentsOfDirectoryAtPath:[NSString stringWithFormat:#"%#/Manual.txt",documentsDirectory] error:&error];
I am sure i have the Manual.txt file in document directory.
However it doesn't show anything in tableView.
I also reload tableView.
Is there anything wrong?
The method is contentsOfDirectoryAtPath:. Read the name of the method. Read the description in the docs. The path you pass must reference a directory, not a file.
What you are trying to do doesn't make sense logically. If you know a specific file, then why search for it? Why create an array?
If you want to see if the file exists, use the fileExistsAtPath: method of NSFileManager.
If you just want the filename in the array then do:
NSString *filename = [documentsDirectory stringByAppendingPathComponent:#"Manual.txt"];
[arrayToSearch addObject:filename]; // since the array was pre-allocated
Please don't use stringWithFormat to create the filename. Use the proper NSString path methods like I did above.

How to write a String in different lines to a .txt file on iOS

I am trying to write a string to a t.t file in the documents folder of my app. I can write the string to it, but when I write another string to the file it overwrites the other string, is it possible to write more strings to a text files, with a blank line between strings, many in this formate
String
String
Strine
…
I am using this code to write the string to an text file, it works for one string, but not for multible strings.
NSArray *paths = NSSearchPathForDirectoriesInDomains
(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
//make a file name to write the data to using the documents directory:
NSString *fileName = [NSString stringWithFormat:#"%#/Barcodedata.txt",
documentsDirectory];
//create content - four lines of text
NSString *content = [NSString stringWithFormat:#"%#",sym.data];
//save content to the documents directory
[content writeToFile:fileName
atomically:NO
encoding:NSStringEncodingConversionAllowLossy
error:nil];
There's a few ways to do this, depending on how you implemented your code.
One way would be to load up the original .txt file into a NSMutableString object and then append that new line to the end of the string and re-write out the file (this isn't super efficient, especially as you start appending after 1000 strings, 100 strings, 50 strings, etc.)
Or you could use the low level C function "fwrite" with the append bit set.
Edited:
Since you want to see code, here's how to do it with my first suggestion:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
//make a file name to write the data to using the documents directory:
NSString *fileName = [NSString stringWithFormat:#"%#/Barcodedata.txt", documentsDirectory];
//create content - four lines of text
NSError * error = NULL;
NSStringEncoding encoding;
NSMutableString * content = [[NSMutableString alloc] initWithContentsOfFile: fileName usedEncoding: &encoding error: &error];
if(content == NULL)
{
// if the file doesn't exist yet, we create a mutable string
content = [[NSMutableString alloc] init];
}
if(content)
{
[content appendFormat: #"%#", sym.data];
//save content to the documents directory
BOOL success = [content writeToFile:fileName
atomically:NO
encoding:NSStringEncodingConversionAllowLossy
error:&error];
if(success == NO)
{
NSLog( #"couldn't write out file to %#, error is %#", fileName, [error localizedDescription]);
}
}

Content is not retrieved from file

I've got this wired problem, I cannot get the content from the file and initiate my NSMutableArray with it.
Here's my code:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSLog(#"Does file exist?: %i", [[NSFileManager defaultManager] fileExistsAtPath:[NSString stringWithFormat: #"%#/length.txt", documentsDirectory]]);
NSMutableArray *tempArr;
tempArr = [[NSMutableArray alloc] initWithContentsOfFile:[NSString stringWithFormat: #"%#/length.txt", documentsDirectory]];
When trying this, initWithContentsOfFile returns (null). The row checking if the file exist prints '1' to the console.
This is the code I'm using to save the data:
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
[length.text writeToFile:[NSString stringWithFormat: #"%#/length.txt", documentsDirectory] atomically:NO];
I'm using more or less exactly the same code in a different program without problems.
Really need some help here, perhaps I'm just blind for the moment...
When you try to create an array from the contents of a file, the file must be in plist format, and the outer-most plist element must be <array>. If it doesn't have that format, initialization will fail and your array will be nil.
You're creating the file by writing an NSString to a file, which means you should probably be reading it in to an NSString, not an NSArray.
The docs for NSArray's initWithContentsOfFile: method say:
Return Value An array initialized to contain the contents of the file
specified by aPath or nil if the file
can’t be opened or the contents of the
file can’t be parsed into an array.
The returned object might be different
than the original receiver.
You don't include the declaration of length in your code snippet, but I'm guessing that length.text returns an NSString object, not an NSArray. So you'd need to read that back from a file using initWithContentsOfFile: from NSString, not NSArray.

Resources