I am having some trouble populating a large plist into an array. Here is the snippet of code giving me problems:
// Populate the routes.
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"routes" ofType:#"plist"];
NSMutableArray *routes = [NSMutableArray arrayWithContentsOfFile:filePath];
NSLog(#"Routes: %#", routes);
// Populate the trips.
NSString *filePath2 = [[NSBundle mainBundle] pathForResource:#"trips" ofType:#"plist"];
NSMutableArray *trips = [NSMutableArray arrayWithContentsOfFile:filePath2];
NSLog(#"Trips: %#", trips);
My issue is that after displaying each mutable array in the logs, the log for the routes array displays just fine, but the log for the trips array simply doesn't appear at all. Usually when an issue occurs then the log will show something like "Trips: ( )", but that line doesn't appear at all in this case. The only difference I can see between the two instances is that the routes plist is an array with about 1000 dictionary objects and the trips plist has nearly 92,000 objects. Is there some sort of limit on the size of plists?
Thanks in advance.
"Is there some sort of limit on the size of plists?"
There isn't a limit to to the size of plists, but there is a limit to the amount of data that you can feed to an NSLog() command.
If trips were actually nil, the NSLog() call would succeed, and simply print out (null). The trips array is populated, however, which is why it's not printing out at all: NSLog() is saying, "sorry, there's no way I'm going to let you to print out all that".
I believe this has likely changed in more recent versions of OS X due to possible security concerns or performance issues. (In the past, users' hard drives would fill up with log files that were GB in size, caused by one process logging an error message hundreds of times a second; that is now limited to 500 logs per second). It's kind of confusing why nothing is printed out and you get no feedback from Xcode or anything, but I guess the system has no way of knowing whether your use of NSLog() is with good intentions or not.
According to the doc, if there is a parse problem or there is an issue with opening the file, nil will be returned. Is it possible in that massive file there is a bad character or a typo that might break the xml?
"
Return Value -->
An array containing the contents of the file specified by aPath. Returns nil if the file can’t be opened or if the contents of the file can’t be parsed into an array.
Related
I have a text file created in documents directory with 200 logs/lines (with delimiter to separate data using a string). I want to TRANSFER first 50 records into another file and keep the remaining 150 records in the same file.
I thought of a logic, say
NSString *logString = [[NSString alloc]initWithContentsOfFile:logFilePath encoding:NSUTF8StringEncoding error:nil];
NSArray *logsArray = [logString componentsSeparatedByString:#"[;]"];
[#"" writeToFile:logFilePath atomically:YES encoding:NSUTF8StringEncoding error:&error];
for (int i=0; i<=[logsArray count];i++){
if(i<=50){
"write to new file"
}
else{
"write to old file"
}
}
This logic will work fine. My concern is I am at a heavy risk of losing my data since I am emptying and rewriting my file at one point. I am already creating too much of files to back up my data. Is there any solution to handle this case ?
Thanks -
Arun.AR
Load all the 200 lines into an NSArray (like you do now).
Create a new file called something like "file_50_new".
Write the first 50 objects of the NSArray to "file_50_new".
Create a new file called something like "file_150_new".
Write the other 150 objects of the NSArray to "file_150_new".
Remove the original file.
Rename the new files.
This way, you always keep around the original file just until the end. If something happens (crash etc.) you always have all the lines around. They could be in different files, but you can always get them back later with a little more logic.
Actually, the writing of the last 150 objects can be done atomically by the Cocoa framework for you, but these steps are the gist of it.
I have a plist file called playerData that includes a number object at index 0 indicating the highest level completed. After loading the view I read this object's integer value that is used throughout the game logic. If the player wins the game I would like to increment this number and write it to the plist file. here is the code I have (contained in an if statement)
levelNumber++;
NSNumber *levelNSNum = [[NSNumber alloc] initWithInteger:levelNumber];
[playerData replaceObjectAtIndex:0 withObject:levelNSNum];
[playerData writeToFile:[[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:#"PlayerData.plist"] atomically:YES];
NSLog(#"%i should be written to file", levelNumber);
the log works so I know the conditions of the if statement have been met and the value is not the same as the one that was previously in the plist file, but for some reason this data is not being written over that data.
I am relatively new to this so I could be making an easy, stupid mistake I just can't seem to track down an answer. Thank you for your help!
You're trying to write to your bundle, which is read-only after the app is installed. You should write to somewhere in your app sandbox instead, such as in your Library/Application Support directory. (You can use - [NSFileManager URLsForDirectory:inDomains:] with NSApplicationSupportDirectory to find this path; be sure to create the directory before you try to write to it.)
I am making an Iphone drinking card game app.
All the card mean something different and i want the user to be able to press an info button and then show a new screen with information about the current card. How can i make a document to load text from instead of using a bunch og long strings?
Thanks
You could look into plist files - they can be loaded quite easily into the various collection objects and edited with the plist editor in Xcode.
For instance, if you organize your data as a dictionary, the convenience constructor
+ (id)dictionaryWithContentsOfURL:(NSURL *)aURL
from NSDictionary would provide you with as many easily accessible strings as you need.
This method is useful if you consider your strings primarily data as opposed to UI elements.
Update:
As #Alex Nichol suggested, here is how you can do it in practice:
To create a plist file:
In your Xcode project, for instance in the Supporting Files group, select New File > Resource > Property List
You can save the file in en.lproj, to aid in localization
In the Property list editing pane, select Add Row (or just hit return)
Enter a key name (for instance user1) and a value (for instance "Joe")
To read the contents:
NSURL *plistURL = [[NSBundle mainBundle] URLForResource:#"Property List" withExtension:#"plist"];
NSLog(#"URL: %#", plistURL);
NSDictionary *strings = [NSDictionary dictionaryWithContentsOfURL:plistURL];
NSString *user1 = [strings objectForKey:#"user1"];
NSLog(#"User 1: %#", user1);
A plist, a JSON string, and an SQLite database walked into a bar ...
Oops!! I mean those are the three most obvious alternatives. The JSON string is probably the easiest to create and "transport", though it's most practical to load the entire thing into an NSDictionary and/or NSArray, vs read from the file as each string is accessed.
The SQLite DB is the most general, and most speed/storage efficient for a very large number (thousands) of strings, but it takes some effort to set it up.
In my other answer, I suggest the use of a dictionary if your texts are mostly to be considered as data. However, if your strings are UI elements (alert texts, window titles, etc.) you might want to look into strings files and NSBundle's support for them.
Strings files are ideally suited for localization, the format is explained here.
To read them into you app, use something like this:
NSString *text1 = NSLocalizedStringFromTable(#"TEXT1", #"myStringsFile", #"Comment");
If you call your file Localizable.strings, you can even use a simpler form:
NSString *str1 = NSLocalizedString(#"String1", #"Comment on String1");
A useful discussion here - a bit old, but still useful.
I'm implementing CHCSVParser into my iPhone app (thanks Dave!) however I'm really confused on how to use it. I've read the read-me and searched some questions on SO but still not 100% sure what to do.
I have a .CSV file with maybe 5000 rows of data and 3-4 columns.
I want this data to in return, load my UITableView along with its corresponding detailViewController.
So I'm assuming I need to somehow implement the API's array method but can anyone help get me started?
I'm glad you like it :)
Basically, CHCSVParser only parses CSV files. You give it a path to a CSV file, and it'll give you back a whole bunch of NSStrings. What you do beyond that point is entirely up to you.
So let's say you've included a CSV file in your iOS app called "Data.csv". Here's how you'd use CHCSVParser to parse it:
NSString *path = [[NSBundle mainBundle] pathForResource:#"Data" ofType:#"csv"];
NSError *error = nil;
NSArray *rows = [NSArray arrayWithContentsOfCSVFile:path encoding:NSUTF8StringEncoding error:&error];
if (rows == nil) {
//something went wrong; log the error and exit
NSLog(#"error parsing file: %#", error);
return;
}
At this point, rows is an array. Each element in rows is itself an array representing a single row in the CSV file. And each element of that array is an NSString.
So let's say your CSV file looks like this:
Barringer,Arizona,United States,Earth
"Chicxulub, Extinction Event Crater",,Mexico,Earth
Tycho,,,Moon
Lonar,Maharashtra,India,Earth
If you run it through the parser, you'll get back the equivalent of this:
[NSArray arrayWithObjects:
[NSArray arrayWithObjects:#"Barringer",#"Arizona",#"United States",#"Earth",nil],
[NSArray arrayWithObjects:#"Chicxulub, Extinction Event Crater",#"",#"Mexico",#"Earth",nil],
[NSArray arrayWithObjects:#"Tycho",#"",#"",#"Moon",nil],
[NSArray arrayWithObjects:#"Lonar",#"Maharashtra",#"India",#"Earth",nil],
nil];
What you do with it then is your business. The CSV parser doesn't know anything about UITableView, so you get to take this data and re-structure it in a way that you're comfortable dealing with and that fits in to your data model.
Also, remember that by using CHCSVParser, you agree to abide the terms under which it is made available. :)
What I'm doing:
I am reading some data off a file several times while my app runs. I use the following code to do so:
NSString *dataPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"data.txt"];
NSString *data = [NSString stringWithContentsOfFile:dataPath encoding:NSStringEncodingConversionExternalRepresentation error:NULL];
NSArray *components = [data componentsSeparatedByString:#"|||||"];
The first time this is called, it works as expected - I get an array of length 5, each section containing a relevant string.
What goes wrong:
This file is never modified. Yet, when I call the same procedure a second time (or third, fourth etc) I don't get the same result. Instead, an array of length 1 is returned, containing only part of the necessary data.
Why? I can't see any reason for this to go wrong... any help is much appreciated!
Since the file is in you AppBundle this means that you can't modify this file at all.
Are you sure that, where ever this code is called, the autorelease object are retained correctly?
If you call this block of code every time you want this data, it might be an idea to save the results of the first time and use that every time. This will speed things up a bit.
Turns out that the code works fine. The problem was elsewhere in my code, where I was (accidentally) accessing protected directories. As a result, iOS blocked my app from accessing any files at all :o