ios read csv file at project directory - ios

I have implemented the module for reading the csv file
The project structure is as follows:
Project
--test_de.csv
--Folder
--Controller.h
--Controller.m
but the result shows no response. No words are added:
2014-06-19 15:32:16.817 marker[1748:60b] /var/mobile/Applications/E2B95450-429D-4777-97BE-0209522EFDEF/Documents/test_de.csv
2014-06-19 15:32:16.824 marker[1748:60b] (
)
The below is my code
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* fullPath = [paths objectAtIndex:0];
fullPath = [fullPath stringByAppendingPathComponent:#"test_de.csv"];
NSLog(#"%#", fullPath);
NSMutableArray *titleArray=[[NSMutableArray alloc]init];
NSString *fileDataString=[NSString stringWithContentsOfFile:fullPath encoding:NSUTF8StringEncoding error:nil];
NSArray *linesArray=[fileDataString componentsSeparatedByString:#"\n"];
int k=0;
for (id string in linesArray)
if(k<[linesArray count]-1){
NSString *lineString=[linesArray objectAtIndex:k];
NSLog(#"%#",lineString);
NSArray *columnArray=[lineString componentsSeparatedByString:#"\n"];
[titleArray addObject:[columnArray objectAtIndex:0]];
k++;
}
NSLog(#"%#",titleArray);

Tip: use JSON!
You are better off using a JSON format instead CVS for your resource - regardless whether this resource comes from a backend at runtime or has been embedded as an application resource at build time.
You can use the iOS system JSON decoder to parse the file into an object representation. This saves you from the many hassles to implement your own CVS parser.
Also, it should be a no-brainer to convert any input provided in CVS into JSON before you actually provide it as a resource for your application.
How to read an app resource?
Well, first ensure your resource file is included the project and has been assigned a target (see "Target Membership" in the File Inspector pane).You can also check this in the "Copy Bundle Resources" section in the "Build Phases" tab in the target editor when you select the associated target. Your file should be listed there.
Assuming, you put the given file as an application resource, you should use the Bundle class to read that resource. First get its location as a URL:
(Note: I'm only showing this in Swift and leave the Objective-C version as an exercise)
Swift:
var fileUrl = Bundle.main.url(forResource: "test_de", withExtension: "json")
This also assumes, the resource is in the root folder of the app resource folder, which happens to be the default case when you flag a file as a resource. Note, that the "app resource folder" is "opaque" to you, that is, actually you do not need to know where it is - the Bundle handles all that for you.
For Objective-C you may read here: NSBundle
Now, that you have the URL, read the file and get a Data object:
Swift:
let data = try Data(contentsOf: fileUrl)
For Objective-C you may read here: NSData
Obtain an Object Representation for your Resource
Once you have the file as data (NSData or Data), and assuming its content is a JSON Array you can get an object representation as follows:
Objective-C:
NSError *error = nil;
id jsonObject = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:&error];
if ([jsonObject isKindOfClass:[NSArray class]]) {
...
}
else {
NSLog(#"did not find a JSON Array");
...
}
Swift:
Doing it swiftly would require you to define a type (struct) which represents an element in the JSON Array. Usually, this is easy to accomplish:
struct MyElement: Codable {
let field1: String
let field2: String
let field3: String
}
Then, use the Swift JSON Decoder to get an array of MyElements:
let elements = try JSONDecoder.decode([MyElement].self, from: data)

According to SoulWithMobileTechnology this is how to do it
[self readTitleFromCSV:csvPath AtColumn:0];
And our method looks like following:
-(void)readTitleFromCSV:(NSString*)path AtColumn:(int)column
{
titleArray=[[NSMutableArray alloc]init];
NSString *fileDataString=[NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:nil];
NSArray *linesArray=[fileDataString componentsSeparatedByString:#"\n"];
int k=0;
for (id string in linesArray)
if(k<[linesArray count]-1){
NSString *lineString=[linesArray objectAtIndex:k];
NSArray *columnArray=[lineString componentsSeparatedByString:#";"];
[titleArray addObject:[columnArray objectAtIndex:column]];
k++;
}
NSLog(#"%#",titleArray);
}

Related

iOS: read raw plist

I want to read the UserDefaults plist but not as Dictionary or Data. I want it as string like it is when you open it with an editor.
NSString* documentsPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *homeDir = [documentsPath stringByReplacingOccurrencesOfString:#"Documents" withString:#""];
NSString *defaultsPath = [homeDir stringByAppendingString:[NSString stringWithFormat:#"Library/Preferences/%#.plist", [[NSBundle mainBundle] bundleIdentifier]]];
NSData *data = [NSData dataWithContentsOfFile:defaultsPath];
Already tried:
`NSString *contents = [NSString stringWithContentsOfFile:defaultsPath encoding:NSUTF8StringEncoding error:&error];
which ends up with
The operation couldn’t be completed. (Cocoa error 261.)
Property list formats can be either binary or text. Binary plists can't be loaded into an NSString because strings are for text, not arbitrary binary data. The error you're getting seems to suggest that the file cannot be interpreted as UTF-8, which either means it is encoded using another encoding or is not text at all.
If you are certain that the property list is a text property list, you can use:
NSStringEncoding enc;
NSString *contents = [NSString stringWithContentsOfFile:defaultsPath usedEncoding:&enc error:&error];
This will allow the framework to determine the encoding of the text plist for you. If it isn't a text plist, you can convert it to one using the plutil command line utility:
plutil -convert xml1 file.plist
Or, alternatively you can do this in code by loading the plist using the NSPropertyListSerialization class, obtaining the NSData from it that represents the plist as the XML format, and then convert that to a string.
An example would be [uncompiled and untested]:
// load the file as-is into a data object
NSData *data = [NSData dataWithContentsOfFile:defaultsPath];
// convert the plist data into actual property list structure
id plistFile = [NSPropertyListSerialization propertyListWithData:data
options:0
format:NULL
error:&error];
// get the XML representation of the property list
NSData *asXML = [NSPropertyListSerialization dataWithPropertyList:plistFile
format:NSPropertyListXMLFormat_v1_0
options:0
error:&error];
// convert the NSData object into an NSString object
NSString *asString = [[NSString alloc] initWithData:asXML encoding:NSUTF8StringEncoding];
This should work whether the original plist is in XML or binary format. In this example, I am assuming that the XML representation of the property list is in fact UTF-8 encoded, as this is the most common encoding for XML data.

Save contents of NSMutableArray to file [duplicate]

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 :)

plist not giving the data the way entered

Before I start, I am creating plist programmatically
I am storing the path names of images in plist (programmatically). While saving I took NSLog and below is what I have
2013-08-04 15:25:24.044 XXX[12595:13d03] inserting data is 5===http://www.mysite.com/faces/ProjectUploadFiles/hotDeals/mobile_2013-08-04-07-42-37.25_tstdddd.png
2013-08-04 15:25:24.057 XXX[12595:13d03] inserting data is 4===http://www.mysite.com/faces/ProjectUploadFiles/hotDeals/mobile_2013-08-04-07-18-20.673_iphone_2.jpg
2013-08-04 15:25:24.086 XXX[12595:13d03] inserting data is 2===http://www.mysite.com/faces/ProjectUploadFiles/hotDeals/mobile_2013-07-21-03-14-29.292_Spare-Parts-summer-Ad-hyundai.jpg
2013-08-04 15:25:24.087 XXX[12595:13d03] inserting data is 1===http://www.mysite.com/faces/ProjectUploadFiles/hotDeals/mobile_2013-07-21-03-11-55.395_horizon.jpg
2013-08-04 15:25:24.089 XXX[12595:13d03] inserting data is 6===http://www.mysite.com/faces/ProjectUploadFiles/hotDeals/mobile_2013-08-04-08-03-29.371_2010.jpg
the data is entered with id 5,4,2,1,6.
Now when I read this data, below is what I am getting.
2013-08-04 15:27:28.251 XXX[12595:13d03] fetching URL == http://www.mysite.com/faces/ProjectUploadFiles/hotDeals/mobile_2013-08-04-07-18-20.673_iphone_2.jpg
2013-08-04 15:27:28.252 XXX[12595:13d03] fetching URL == http://www.mysite.com/faces/ProjectUploadFiles/hotDeals/mobile_2013-08-04-07-42-37.25_tstdddd.png
2013-08-04 15:27:28.252 XXX[12595:13d03] fetching URL == http://www.mysite.com/faces/ProjectUploadFiles/hotDeals/mobile_2013-07-21-03-11-55.395_horizon.jpg
2013-08-04 15:27:28.252 XXX[12595:13d03] fetching URL == http://www.mysite.com/faces/ProjectUploadFiles/hotDeals/mobile_2013-08-04-08-03-29.371_2010.jpg
2013-08-04 15:27:28.252 XXX[12595:13d03] fetching URL == http://www.mysite.com/faces/ProjectUploadFiles/hotDeals/mobile_2013-07-21-03-14-29.292_Spare-Parts-summer-Ad-hyundai.jpg
means while fetching I get data with 4,5,1,6,2
When I double click plist, I have data with id sorted i.e. I have data as 1,2,4,5,6.
Any idea why plist is providing random data?
Below is the screen shot of plist that is getting generated.
Edit 1
For Storing data, below is the code I have.
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
news = [NSJSONSerialization JSONObjectWithData:data options:nil error:nil];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"Offers.plist"];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
if(![[NSFileManager defaultManager] removeItemAtPath:path error:&error])
{
//TODO: Handle/Log error
NSLog(#"files not deleted...");
} else {
NSLog(#"files deleted...");
}
if (![fileManager fileExistsAtPath: path])
{
path = [documentsDirectory stringByAppendingPathComponent: [NSString stringWithFormat: #"Offers.plist"] ];
}
NSMutableDictionary *data002 = [[NSMutableDictionary alloc] initWithContentsOfFile: path];
if ([fileManager fileExistsAtPath: path])
{
data002 = [[NSMutableDictionary alloc] initWithContentsOfFile: path];
}
else
{
// If the file doesn’t exist, create an empty dictionary
data002 = [[NSMutableDictionary alloc] init];
}
int count;
for (count = 0; count < (int)[news count]; count++)
{
[data002 setObject:[[news objectAtIndex:count] objectForKey:#"imagePath"] forKey:[[news objectAtIndex:count] objectForKey:#"id"]];
[data002 writeToFile:path atomically:YES];
}
[data002 release];
}
Answer
What I did is instead of NSMutableDictionary I used NSMutableArray and all worked perfectly.
You plist is a dictionary, it has no order, just keys and values. If you need to store the order then change to use an array (of dictionaries) or use an additional file to store the order.
data002 is a dictionary. That's what makes your plist a dictionary (when you call writeToFile:). If you want to maintain the relative order you need to decide which approach you want to take and then create an NSArray of the items in order and use writeToFile: on the array to save it.
Typically, in order to maintain sequential data you would have to use an array of strings in the plist file rather than directly inserting strings to the root element (which is a dictionary). Alternatively, if you wish to store more complex entities, I would recommend indexing them and referring to the indexes alone in a separate array to maintain order.
Tap on the "+" sign to add an element, then select "Array" from the drop down menu under "type"
If you're doing this in code, simply insert an NSArray as an object in the NSDictionary you're saving as a plist:
[data002 setObject:#[path1,path2,path3] forKey:#"orderedArray"];

iOS persistence: storing and retrieving items in directory

I want to save a file to a folder, and then retrieve all the contents of that folder, so I do:
NSString *dataPath = [documentsDirectory stringByAppendingPathComponent:storageFolder];
// StorageFolder is just a string like: "#"/FavoritesFolder"
// Filename is just a title given like "myTune.mp3"
NSString *destinationString = [NSString stringWithFormat:#"%#/%#",dataPath,filename];
BOOL success = [object writeToFile:destinationString atomically:YES];
Then I want to retrieve the object, so I do
NSArray *dirContents = [[NSFileManager defaultManager]contentsOfDirectoryAtPath:dataPath error:nil];
But all I get an array of filename (like myTune.mp3). not the Object which is a NSDictionary. What am I doing wrong?
You aren't strictly 'doing' anything wrong. It's your expectations that are wrong. The method you're using (contentsOfDirectoryAtPath:)
Performs a shallow search of the specified directory and returns the paths of any contained items.
If you want to load the file back into memory you need to:
Decide which file you want
Get the full path to the file (directory path and file name)
Load the file (if it's a dictionary, dictionaryWithContentsOfFile:)

What is the correct way to save user data using NSFileManager?

I am having trouble initializing dictionaries I use throughout my program to store user achievements and scores.
I have almost identical code for the two dictionaries and only the gameCenterData dictionary seems to be working properly. I have tried altering the plist file name and contents yet nothing seems to make the playerData dictionary properly load info from the file as it should
In the Root View Controller I have the following code (playerData and gameCenterData are both NSMutableDictionaries and the plist files are in the proper place)
-(NSString *)scoreFilePath
{
NSArray *scorePath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [scorePath objectAtIndex:0];
return [documentsDirectory stringByAppendingPathComponent:#"PlayerScoreData.plist"];
}
-(NSString *)gameCenterFilePath
{
NSArray *gameCenterPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [gameCenterPath objectAtIndex:0];
return [documentsDirectory stringByAppendingPathComponent:#"GameCenterData.plist"];
}
then the view did load
- (void)viewDidLoad
{
[super viewDidLoad];
NSString *playerDataPath = [self scoreFilePath];
if (! [[NSFileManager defaultManager] fileExistsAtPath:playerDataPath])
{
playerData = [NSMutableDictionary dictionaryWithContentsOfFile:[[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:#"scoreData.plist"]];
[playerData writeToFile:[self scoreFilePath] atomically:YES];
NSLog(#"Player data file does not exist");
}
else
{
playerData = [[NSMutableDictionary alloc] initWithContentsOfFile:[self scoreFilePath]];
NSLog(#"player data file exists");
}
NSLog(#"scoreData is %#",playerData);
NSString *gameCenterPath = [self gameCenterFilePath];
if (! [[NSFileManager defaultManager] fileExistsAtPath:gameCenterPath])
{
gameCenterData = [NSMutableDictionary dictionaryWithContentsOfFile:[[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:#"gameCenterData.plist"]];
[gameCenterData writeToFile:[self gameCenterFilePath] atomically:YES];
NSLog(#"game center data file does not exist");
}
else
{
gameCenterData = [[NSMutableDictionary alloc] initWithContentsOfFile:[self gameCenterFilePath]];
NSLog(#"game center data file exists");
}
NSLog(#"gameCenterData is %#",gameCenterData);
the output is as follows
2012-08-05 11:46:49.991 GlobeRoller[6410:1be03] Player data file does not exist
2012-08-05 11:46:49.992 GlobeRoller[6410:1be03] playerData is (null)
2012-08-05 11:46:50.061 GlobeRoller[6410:1be03] game center data file does not exist
2012-08-05 11:46:50.062 GlobeRoller[6410:1be03] gameCenterData is {
"Career Odometer" = 0;
"Career Score" = 0;
"Cities Found" = 0;
"Offline Games Played" = 0;
"Online Games Played" = 0;
"Online Games Won" = 0;
}
I have searched all of the questions and answers to see if I can find out why this isn't working for both methods. Any help you could offer, or resources you could point me to I would greatly appreciate.
Thank you,
CF
The plist file you are trying to load from the bundle is either not there, or has been created improperly. Directly from the documentation of dictionaryWithContentsOfFile:.
Return Value
A new dictionary that contains the dictionary at path, or
nil if there is a file error or if the contents of the file are an
invalid representation of a dictionary.
You should make sure you are using the proper file name, and then open your plist in Xcode to see if it is properly formatted.
iOS is case sensitive. Are you sure that your file in the bundle is lower case, i.e. "#"scoreData.plist", and not upper case like the name your code uses? Also, verify that these two files are in your bundle - check the build phase or select the files (one at a time) and look in the 3rd Xcode pane in the file attribute section (to verify they are included in your target). If all that looks good then when you try to retrieve the files from your bundle:
Also, don't try to find the file at the root level of the bundle - you should be using:
NSString *path = [[NSBundle mainBundle] pathForResource:#"GameCenterData" ofType:#"plist"];
NSLog(#"PATH is %#", path);
...then use path instead of the code you are using now

Resources