Save contents of NSMutableArray to file [duplicate] - ios

This question already has an answer here:
Writing a NSArray to file
(1 answer)
Closed 9 years ago.
I have an app which has several ViewControllers. Each ViewController has a question with 2-5 buttons as possible answers. The title of a button is saved to a NSMutableArray called submission. The user can go back to previous questions, change their answer, and the NSMutableArray will be updated accordingly. I need to save this array to file so new results can be saved into a UITableView each time the questionnaire is completed. I have researched and it sounds like a .plist is a good option, as all of my objects in the array are NSStrings.
A sample of my array:
2013-12-17 14:06:34.210 Questionnaire[1724:70b] (
1234,
"Dec 17, 2013",
Yes,
Games,
"Not Applicable",
Yes
)
"1234" is the User ID, the date is the Date of Birth, and the other submissions are the answers to each question.
My ViewControllers look like this:
MainViewController
InfoViewController <-- Array allocated + initialised, inserting ID and DoB
Q1ViewController <-- question
..
Q4ViewController <--question
ENDViewController <-- offers user options for Home or Results
ResultsViewController <-- UITableView ordered by User ID
SavedResultsViewController <-- UITableView showing complete submission
The NSMutableArray gets passed through each ViewController.
My questions: What method of saving to file would best suit my needs? (.plist, filetype, etc). Viewing the results on Excel would be nice (but not essential). Where should the save take place? I was thinking when the last object is inserted into the array on Q4 ViewController, so it would be saved to file when the ENDViewController is popped, is this logical? Do I need to create a new Objective-C file to store the data? I have saw a few tutorials explaining this, declaring each object in a separate NSObject file, although I'm not sure if that is needed as my objects are already stored in the array.
As you've probably guessed I am quite new to Objective-C and iOS programming, so any help offered is greatly appreciated. I am not sure how else to describe my problem, so apologies if the question is still unclear.
Edit: I have learnt a bit more about Objective-C since creating this post, and have decided to save my data to a .csv file. This file is strictly for the purpose of emailing, it doesn't get displayed on the UITableView (I am currently implementing Core Data for that). This solution might help someone in the future, so here it is:
// Set column titles for .csv
NSArray *columnTitleArray = #[#"User ID", #"DoB", #"Question 1", #"Question 2", #"Question 3", #"Question 4"];
NSString *columnTitle = [columnTitleArray componentsJoinedByString:#","];
NSString *columnTitleToWrite = [columnTitle stringByAppendingString:#"\n"];
// Separate submissions from array into cells
NSString *questionResults = [self.submission componentsJoinedByString:#","];
NSString *questionResultsToWrite = [questionResults stringByAppendingString:#"\n"];
// Find documents directory
NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
// Set file name and type
NSString *survey = [docPath stringByAppendingPathComponent:#"result.csv"];
// Create new file if none exists
if (![[NSFileManager defaultManager] fileExistsAtPath:survey]){
[[NSFileManager defaultManager] createFileAtPath:survey contents:nil attributes:nil];
// Set column titles for new file
NSFileHandle *fileHandle = [NSFileHandle fileHandleForUpdatingAtPath:survey];
[fileHandle writeData:[columnTitleToWrite dataUsingEncoding:NSUTF8StringEncoding]];
}
NSFileHandle *fileHandle = [NSFileHandle fileHandleForUpdatingAtPath:survey];
[fileHandle seekToEndOfFile];
[fileHandle writeData:[questionResultsToWrite dataUsingEncoding:NSUTF8StringEncoding]];
[fileHandle closeFile];

You could save your array as a JSON file. This would allow you to view it in a text editor, but not a spreadsheet. The following code will write it out as JSON to a file:
NSURL *url = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
url = [url URLByAppendingPathComponent:#"yourdata.json"];
NSError *e = nil;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:yourdata options:NSJSONWritingPrettyPrinted error:&e];
if (jsonData) {
[jsonData writeToFile:url.path atomically:YES];
}
You could also save your data as a plist file. The following will write the data to a plist:
NSURL *url = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
url = [url URLByAppendingPathComponent:#"yourdata.plist"];
NSError *e = nil;
NSData *plistData = [NSPropertyListSerialization dataWithPropertyList:yourdata format:NSPropertyListXMLFormat_v1_0 options:0 error:&e];
if (plistData) {
[plistData writeToFile:url.path atomically:YES];
}
Plists can be saved in binary or XML format, and the above will save it as XML format, which means the XML will also be readable with a text editor. Again, this won't be viewable as a spreadsheet. If you want to export your data in CSV format, you'll probably have to write your own code to output it.

You can use standard NSCoding protocol for saving data but in this case you can't read it in excel.
Or you can save it in xml.
Or you can manually create a csv (comma separated values) file and open it anywhere.
Implementation is up to you anyway :)

Related

iOS: Create a dynamic registration form

I am working on a registration form app. Right now I'm trying to come up with a way to to dynamically create forms. Right now I have a hardcoded form, with hardcoded questions, that will save the form data into a .csv file. Here is some of the code:
// saves text field data in comma separated CSV file format
NSString *formData = [NSString stringWithFormat:#"%#,%#,%#,%#,%#,%#,%#,%#,%#,%#,%#\n",
self.nameTextfield.text, self.emailTextfield.text,
self.phoneTextfield.text, self.termTextfield.text,
self.schoolTextfield.text, self.graduationTextfield.text,
self.gpaTextfield.text, degreeString,
self.interestTextfield.text, groupString,
appliedString];
// get document directory path
NSString *documentDirectoryPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask, YES)objectAtIndex:0];
NSString *csv = #".csv";
NSString *filename = [NSString stringWithFormat:#"%#%#", _eventName.text, csv];
// append results.csv onto doc path
NSString *event = [documentDirectoryPath stringByAppendingPathComponent:filename];
// creates folder if it does not exist
if (![[NSFileManager defaultManager] fileExistsAtPath:event]) {
[[NSFileManager defaultManager] createFileAtPath:event contents:nil attributes:nil];
}
NSFileHandle *fileHandle = [NSFileHandle fileHandleForUpdatingAtPath:event];
[fileHandle seekToEndOfFile];
[fileHandle writeData:[formData dataUsingEncoding:NSUTF8StringEncoding]];
[fileHandle closeFile];
Basically I want to be able to create a form on the app that can be any number of questions, and either uses segmented controls or text inputs. One issue I see already is formatting the file in the comma separated format. For instance, if the app creates 5 questions, how do you format the file such that formData = [NSString stringWithFormat:#"%#,%#,%#,%#,%#\n"] without hardcoding that in?
Thank you so much for your help!
One (but not the only) way to correctly create dynamic form is to build it of typical blocks (such as switch-block, textfield-block, etc). You can add these blocks to view manually or use UITableView for example. Also you need data structure describing concrete form (number and types of blocks).
Formatting file is no problem since you always can use NSMutableString and append values one by one.

Searching data from txt file in iOS

I have a .txt file in which some data is present, when I stored it in an array I got 50000 words. I want to search the data from text file according to user input and show it on UITableview cell, how is it possible ?
Can any body help me?
Here is my code to read data from .txt file in viewDidLoad:
NSString *filepath = [[NSBundle mainBundle] pathForResource:#"myList" ofType:#"txt"];
NSError *error;
NSString *fileContents = [NSString stringWithContentsOfFile:filepath encoding:NSUTF8StringEncoding error:&error];
if (error)
NSLog(#"Error reading file: %#", error.localizedDescription);
// maybe for debugging...
NSLog(#"contents: %#", fileContents);
NSArray *listArray = [fileContents componentsSeparatedByString:#"\n"];
NSLog(#"items = %d", [listArray count]);
You should use fast enumeration using block. Those are fastest of all for loop iteration. But changing the value in that array at time of enumeration could cause a crash.
Here is a link of how to use block enumeration
Here is a link of how it performance wise.
Hope this helps you!!

CSV to NSString results in empty null object

I'm trying to retrieve content of a csv file to NSString. Thats what I do:
NSString *strBundle = [[NSBundle mainBundle] pathForResource:#"socs" ofType:#"csv"];
NSLog(#"bundle path: %#",strBundle);
NSString *file = [NSString stringWithContentsOfFile:strBundle
encoding:NSUTF8StringEncoding
error:nil];
if ([[NSFileManager defaultManager]fileExistsAtPath:strBundle]) {
NSLog(#"file is there!!!");
}else {
NSLog(#"no file");
}
NSLog(#"file: %#",file);
NSArray *allLines = [file componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
NSLog(#"lines: %lu",(unsigned long)[allLines count]);
file manager shows that the file is there. When i try to log the NSString or number of files it says null. I even created NSData object with the content of exactly the same file and when I logged the NSData object, I clearly saw that there is some data. Then when I tried to create NSString with the content of NSData, I had the same result as before - null. Maybe the problem is somewhere in the formatting of the file?
Any help will be appreciated :)
I see 3 issues:
You are passing a nil argument to the error: parameter in your stringWithContentsOfFile: line. If there's a possibility something might go wrong (and apparently there is), you should pass a real argument there so you can figure out what went wrong.
You can use componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet], but that has a tendency to produce blank "components" between every line. Plain old #"\n" works better in virtually all cases I've run into.
You should be checking fileExistsAtPath before you try to load it into the NSString
If you were truly able to create an NSData object from the path it doesn't necessarily mean it's correct data. But let's say it is, if you were not able to convert it to a NSString then you need to check your encoding parameter.

How to update the existing .plist?

How to update the existing plist with info? I have simple dictionary with strings. Here is my save method:
#define DOCUMENTS [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]
- (IBAction)saveData:(id)sender {
NSMutableDictionary *data = [NSMutableDictionarydictionaryWithObjects:#[self.textFieldOne.text, self.textFieldTwo.text, self.textFieldThree.text] forKeys:#[#"Key1", #"Key2", #"Key3"]];
NSError *error = nil;
NSData *dataToPlist = [NSPropertyListSerialization dataWithPropertyList:data format:NSPropertyListXMLFormat_v1_0 options:0 error:&error];
NSString *path = [DOCUMENTS stringByAppendingPathComponent:#"userData.plist"];
[dataToPlist writeToFile:path atomically:YES];
}
So, it's OK when I call this method from Class 1, but when I want to update this plist with other info from Class 2 – it overwrites old info with new info. What I should do to update the existing plist with new info from other classes?
You need to load the existing plist into an NSDictionary or NSArray (as appropriate), then update the data with the additional data, and finally write out the updated data to the plist file.
Basically the existing one you have to read into dictionary using dictionarywithcontentsfile method and then on the basis of your requirement use setobjecwt method into dictionary and then write to plist. So that old value will be retain there and you can updates new as well.

How to insert data to text file with using NSFileHandle

My code replaces text instead of inserting it starting from 5 symbol:
NSFileHandle *file = [NSFileHandle fileHandleForUpdatingAtPath: filePath];
[file seekToFileOffset: 5];
[file writeData: [value dataUsingEncoding:NSUTF8StringEncoding]];
Is there any way to insert data to text file?
That's because your code set the index position to 5 and start writing from there, thus replacing everything from 5 onwards.
I would copy the content of the file to a variable and modify it from there as a string.
as by the looks of it what you attempt to do is not possible
Update:
Given that what you need is to write from X offset this should do the trick
NSFileHandle *file;
NSMutableData *data;
const char *bytestring = "black dog";
data = [NSMutableData dataWithBytes:bytestring length:strlen(bytestring)];
file = [NSFileHandle fileHandleForUpdatingAtPath: #"/tmp/quickfox.txt"];
if (file == nil)
NSLog(#"Failed to open file");
[file seekToFileOffset: 10];
[file writeData: data];
[file closeFile];
Well this might not be an efficient way, but you could read the entire text file into an NSMutableString and then use insertString:atIndex: and then write it back out. As far as I know there is no way to insert text into an existing file. Similar question
A quick example:
NSString *path = //Your file
NSMutableString *contents = [NSMutableString stringWithContentsOfFile:txtFilePath encoding:NSUTF8StringEncoding error:NULL];
[contents insertString:#"Some string to insert" atIndex:5];
[contents writeToFile:path atomically:YES encoding:NSUTF8StringEncoding error:NULL];
Inserting data somewhere in a sequential file would require that the entire file be rewritten. It is possible, however, to add data to the end of a file without rewriting the file.

Resources