How to read & write to an NSArray to plist? - iphone

I'm having several issues based around reading and writing an NSArray to and from a plist.
I have created a plist file in the 'Supporting Files' folder which I want to use to initialise the app data with upon the first load.
Here is what my plist looks like:
I then use this code to try load the plist into the app:
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
filePath = [documentsDirectory stringByAppendingPathComponent:kDataFile];
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:filePath])
{
NSString *bundle = [[NSBundle mainBundle] pathForResource:#"Data" ofType:#"plist"];
[fileManager copyItemAtPath:bundle toPath:filePath error:&error];
}
I then try to load the data from the plist file like so, however nothing seems to be displayed.
NSMutableDictionary *savedData = [[NSMutableDictionary alloc] initWithContentsOfFile:filePath];
NSMutableArray *myNSArray = [[savedData objectForKey:#"KEY_Level_1"] mutableCopy];
savedData = nil;
Sorry if this is a simple task, however I've been looking at lots of tutorials and trying to work out how to do this with no luck. I'm getting really frustrated now - I would have thought it should be a simple thing to do.
NOTE: My NSArray will contain a whole bunch of NSDictionaries.

You need to check the return value of copyItemAtPath:toPath:error: and at least log the error if the method returns false:
if (![fileManager copyItemAtPath:bundle toPath:filePath error:&error]) {
NSLog(#"error: copyItemAtPath:%# toPath:%# error:%#", bundle, filePath, error);
return;
}
-[NSDictionary initWithContentsOfFile:] has no way to report errors, so if it's failing, you cannot easily figure out why. Try reading the file into an NSData and using -[NSPropertyListSerialization propertyListWithData:options:format:error:] to parse it:
NSData *data = [NSData dataWithContentsOfFile:filePath options:0 error:&error];
if (!data) {
NSLog(#"error: could not read %#: %#", filePath, error);
return;
}
NSMutableDictionary *savedData = [NSPropertyListSerialization propertyListWithData:data options:NSPropertyListMutableContainers format:NULL error:&error];
if (!savedData) {
NSLog(#"error: could not parse %#: %#", filePath, error);
return;
}
NSMutableArray *myNSArray = [savedData objectForKey:#"KEY_Level_1"];
savedData = nil;
if (!myNSArray) {
NSLog(#"error: %#: object for KEY_Level_1 missing", filePath);
return;
}
If you do this, you'll be able to more easily see why your data is not being loaded.
UPDATE
On further inspection, it looks like the top-level dictionary in your plist contains the key "Root". The value for "Root" is a dictionary containing the key "KEY_Level_1". So you need to do this:
NSMutableArray *myNSArray = [[savedData objectForKey:#"Root"] objectForKey:#"KEY_Level_1"];

Related

Reading from text file - Objective C

I am trying to familiarize myself with objective C, and my current goal is to read a list of items in a text file and store them in a NSString array.
Currently this is what I have:
NSString *filepath = [[NSBundle mainBundle] pathForResource:#"myList" ofType:#"txt"];
NSData* data = [NSData dataWithContentsOfFile:filepath];
NSString* string = [[NSString alloc] initWithBytes:[data bytes]
length:[data length]
encoding:NSUTF8StringEncoding];
NSString* delimiter = #"\n";
listArray = [string componentsSeparatedByString:delimiter];
I am not sure if this matters, but myList.txt is in my Supporting Files.
At the moment, I only have one item in my list. However I am unable to store even that 1 item into my listArray.
I am sure it is something silly that I am missing, I am just new to Objective C.
EDIT:
I apologize for not mentioning this earlier. I AM NOT receiving any sort of error. My array is just null.
I'm going to suggest a little simplification which might solve your problem since I can't say what your problem is. From the information I'm not sure if you are getting the proper file contents when reading it in or not.
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]);
If the content of the file is just like:
[{"Title":"20","Cost":"20","Desc":""},{"Title":"10","Cost":"10.00","Desc":""},{"Title":"5","Cost":"5.00","Desc":""}]
try this
-(id)readFromDocumentDBFolderPath:(NSString *)fileName
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *appFile = [documentsDirectory stringByAppendingPathComponent:fileName];
NSFileManager *fileManager=[NSFileManager defaultManager];
if ([fileManager fileExistsAtPath:appFile])
{
NSError *error= NULL;
NSData* data = [NSData dataWithContentsOfFile:appFile];
id resultData = [NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:nil];
if (error == NULL)
{
return resultData;
}
}
return NULL;
}

Trying to write to a .plist file, but it's not working

I'm trying to write a very simple piece of data to a .plist file, and I've checked all sorts of code samples, but I'm struggling to get it to work.
First, there's the method to create the file itself and return the path:
-(NSString *)getFilePath{
NSArray *thePath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
return [[thePath objectAtIndex:0] stringByAppendingPathComponent:#"events.plist"];
}
Then this is the action I have hooked up to a button. It only inserts a line of dummy data at the moment. or at least, that's what I'm trying to do.
- (IBAction)saveData:(id)sender {
NSArray *data = [[NSArray alloc] initWithObjects:#"E3 Expo", #"05-06-2012", nil];
[data writeToFile:[self getFilePath] atomically:YES];
}
But it doesn't work. I checked the user document's directory via the Organizer in Xcode, and nothing's being created in the "Documents" folder.
Any help would be appreciated. :)
You have to set a dictionary as a root plist object.
[Edit: If you can't see the file, create it with NSFileManager]
NSError *error;
NSString *plistPath = [self getFilePath];
NSArray *data = [[NSArray alloc] initWithObjects:#"E3 Expo", #"05-06-2012", nil];
NSDictionary *plistDict = [NSDictionary dictionaryWithObject: data forKey: #"array"];
NSData *plistData = [NSPropertyListSerialization dataFromPropertyList:plistDict
format:NSPropertyListXMLFormat_v1_0
errorDescription:&error];
if(plistData) {
//not necessary
if ([[NSFileManager defaultManager] fileExistsAtPath: plistPath]) {
[[NSFileManager defaultManager] removeItemAtPath: plistPath error: &error];
}
[[NSFileManager defaultManager] createFileAtPath: plistPath contents:plistData attributes: nil];
//necessary
[plistData writeToFile: plistPath atomically:YES];
}
else {
NSLog(error);
[error release];
}
//also release data, since you retained it (sorry, memory management OCD)
[data release];
Based on Apple documentation - scroll to Write Out the Property List

modifying a plist is not working

i have to modify a BOOL value in my plist file stored with the bundle.i am able to access the dictionary which i have to modify .from nslogging i can see that dict is updated with the new value ,but the problem is when i check the plist in bundle it is not being modified.any clue on why it is not updating the plist
NSString* plistPath = nil;
NSFileManager* manager = [NSFileManager defaultManager];
if (plistPath = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:#"TopicsList.plist"])
{
if ([manager isWritableFileAtPath:plistPath])
{
NSMutableArray* dictarrays = [NSMutableArray arrayWithContentsOfFile:plistPath];
NSMutableDictionary *dict=[dictarrays objectAtIndex:indexPath.row];
NSLog(#"%# ",dict );
[dict setObject:[NSNumber numberWithBool:YES] forKey:#"isPaid"];
NSLog(#"%# ",dict );
[dict writeToFile:plistPath atomically:NO];
NSLog(#"%# ",dict );
[manager setAttributes:[NSDictionary dictionaryWithObject:[NSDate date] forKey:NSFileModificationDate] ofItemAtPath:[[NSBundle mainBundle] bundlePath] error:&error];
}
}
Is the plist a part of your resources? Not sure if we can edit a plist there. Copy the plist to your app's Documents folder and update it there.
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *plistPath = [[paths objectAtIndex:0] stringByAppendingPathComponent:#"TopicsList.plist"];
success = [fileManager fileExistsAtPath:plistPath];
if(!success){
//file does not exist. So look into mainBundle
NSString *defaultPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"TopicsList.plist"];
success = [fileManager copyItemAtPath:defaultPath toPath:plistPath error:&error];
}
Now whatever changes you need to make to the plist or read data from the plist, read it from the copy in Documents folder.
You must store your plist in the documents directory. After that, you must also save the plist upon leaving the app. Otherwise, the plist is in the main bundle and cannot be modified.
Is there any error
Check with
NSError *error = nil
[dict writeToFile:plistPath atomically:YES encoding:NSASCIIStringEncoding error:&error];
if (error) {
NSLog(#"Error: %#", [error description]);
} else {
NSLog(#"Success");
}

iPhone SDK: SQLite error: no such table:, even though a terminal query returns successful

I have a problem with sqlite database integration in my app.
My app uses a translator style function, whereby a users text entry is converted into another way of wording the same word, the word alternatives are listed in a SQLite database.
Basically the code is designed to take text, split it into individual words, and search for them on the database, and return the result for each word, however, the application ends up not returning results and I get the error "error: no such table: dictionary" Even though a query on terminal returns results successfully.
Can anyone give me a hand in identifying what's been done wrong? Thanks
This is the code I used:
-(void)translate{
//take input and break into an array
NSString *clearText = [[NSString alloc] init];
clearText=inputBox.text;
NSArray *words = [[NSArray alloc] init];
words= [clearText componentsSeparatedByString:#" "];
numOfWords=words.count;
NSString *newText=#"";
//open database
sqlite3 *db = [self getNewDBConnection];
//loop through array
for(i=0;i<numOfWords;i++){
sqlite3_stmt *resultStatement= nil;
NSString *res = [NSString stringWithFormat:#"select * from dictionary where plain='%#'",[words objectAtIndex:i]];
if((sqlite3_prepare_v2(db, [res UTF8String], -1, &resultStatement, nil))!=SQLITE_OK){
NSLog(#"Error getting result, maybe word not found\n");
//NSLog(#"tried query:%#",res);
NSLog(#"error: %s", sqlite3_errmsg(db));
}
else{
if(sqlite3_step(resultStatement)==SQLITE_ROW){
//in the line below, 1 is the column number of the replacement word
NSString *add = [[NSString alloc] initWithUTF8String: (char*)sqlite3_column_text(resultStatement,1)];
newText=[newText stringByAppendingString:add];
[add release];
}
}
sqlite3_finalize(resultStatement);
}
//output result
outputBox.text=newText;
sqlite3_close(db);
}
-(sqlite3 *) getNewDBConnection{
sqlite3 *newDBconnection;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *path = [documentsDirectory stringByAppendingPathComponent:#"data9.sqlite"];
if (sqlite3_open([path UTF8String], &newDBconnection) == SQLITE_OK) {
NSLog(#"Database Successfully Opened");
} else {
NSLog(#"Error in opening database");
}
return newDBconnection;
}
This is the code used to copy the database:
- (void)createEditableCopyOfDatabaseIfNeeded {
// First, test for existence.
BOOL success;
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *writableDBPath = [documentsDirectory stringByAppendingPathComponent:#"data9.sqlite"];
success = [fileManager fileExistsAtPath:writableDBPath];
if (success) return;
// The writable database does not exist, so copy the default to the appropriate location.
NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"data9.sqlite"];
success = [fileManager copyItemAtPath:defaultDBPath toPath:writableDBPath error:&error];
if (!success) {
NSAssert1(0, #"Failed to create writable database file with message '%#'.", [error localizedDescription]);
}
}
Based on the missing table, it looks like it is creating a new database at the path. So either the path is wrong, or it is possible that you might not have copied the master database to that location. The SQLite file is usually in the bundle and needs to be copied first.
Major issues from my perspective.
I went so far as to delete the entire database capability and then ran the app and it still opened the database.
I have this code in applicationDidFinishLaunchingWithOption in the appDelegate. It is designed for start-up to check a valid database is avaailable, so mostly it is never executed. I removed the conditional statement around the copy, and inserted a removeItemAtPath statement to delete the file. Ran it once, and my problem all went away (or at least those with the database, by biggest problem is still user error :-))
NSString *docsDir;
NSArray *dirPaths;
// Get the documents directory
dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
docsDir = [dirPaths objectAtIndex:0];
// Build the path to the database file
NSString *databasePath = [[NSString alloc] initWithString: [docsDir stringByAppendingPathComponent: #"languageDB.sqlite"]];
NSFileManager *filemgr = [NSFileManager defaultManager];
NSError *error;
NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:#"language.sqlite"];
NSString *writableDBPath = [docsDir stringByAppendingPathComponent:#"languageDB.sqlite"];
if ([filemgr fileExistsAtPath: databasePath ] == NO){
[filemgr copyItemAtPath:defaultDBPath toPath:writableDBPath error:&error];
NSLog(#"copy error %#",error);
}
Something else you might want to make sure of if you're adding a table to an existing database and then trying to re-add it to your project is that you delete the copy of the database in the location where Xcode is copying it prior to loading your app in the simulator. For instance, when I was having this problem, I had to go into /Users/<user>/Library/Application Support/iPhone Simulator/6.0/Applications/1234DFE1-5AA9-4CFF-0235-3D98961E9281/Documents/ to delete the database there. When I cleaned and re-ran the simulator, it found my new table just fine.

NSFileManager createDirectoryAtPath EXC_BAD_ACCESS

I have been working at this one for quite some time now but can't seem to resolve it. I have a core data application that also supports document sharing, therefore I'm trying to create a directory in the library folder for the sqlite db.
- (NSURL *)applicationPrivateDocumentsDirectory {
NSString *libraryDirectory = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) lastObject];
NSString *privateDocs = [libraryDirectory stringByAppendingPathComponent:#"PrivateDocuments"];
NSFileManager *fileMgr = [[NSFileManager alloc] init];
if (![fileMgr fileExistsAtPath:privateDocs]) {
NSLog(#"Does not exist");
NSError *error;
[fileMgr createDirectoryAtPath:privateDocs withIntermediateDirectories:YES attributes:nil error:&error];
NSLog(#"%#", [error description]);
}
NSURL *retURL = [NSURL fileURLWithPath:privateDocs];
return retURL;
}
The debug console outputs "Does not exist" followed by "EXC_BAD_ACCESS"
Any help is greatly appreciated.
Try to add this:
NSError *error = nil;