array can't read data from plist - iphone

I have found couple of similar topic about this but none of them solve my problem.
basically I want to read nsstring from my array in plist and then I want to overwrite the nsstring value.
NSArray *directoriespaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
NSString *documentsDir = [directoriespaths objectAtIndex:0];
NSString *fullnamefordir = [documentsDir stringByAppendingPathComponent:#"data.plist"];
NSArray *turns = [NSArray arrayWithContentsOfFile:fullnamefordir];
NSString *resultofarray = [turns objectAtIndex:0];
NSLog(#"%#",turns);
NSLog(#"%#",resultofarray);
nslog always shows null and i'm not trying to save anything so far because i can't even read the value :/
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
<string>8</string>
</array>
</plist>
i also tried with nsdictionary but it also doesn't work
thanks in advance :)

First Check whether your plist file exist in Document Directory or not.
NSFileMangaer *objFileManager=[[NSFileManager alloc]init];
BOOL isfileExist=[objFileManager fileExistsAtPath:yourDocumentDirectoryPath];
IF Bool Value is true then check you content of plist.
May be the file doesnt exist in document directory ,it is in resource.
If you didnot copied you plist from resource folder to document directory then first copy it there and then try to access it.

Related

Downloading a plist as NSDictionary returns (null)

I have checked many questions on here about downloading a plist file from my server and converting it to a usable NSDictionary. For whatever reason, when I NSLog the NSDictionary created from the plist file, I get this in the console:
Timestamp: (null)
I know that it is a real file: http://www.faithlifefellowship.us/iOS/sermons.plist
Code:
NSDictionary* plist = [NSDictionary dictionaryWithContentsOfURL:[NSURL URLWithString:#"http://www.faithlifefellowship.us/iOS/sermons.plist"]];
NSLog(#"%#",plist);
What is the problem?
Could it have anything to do with the fact that the plist file is minified?
UPDATE:
Getting the file with the following code:
NSString* pl = [NSString stringWithContentsOfURL:[NSURL URLWithString:#"http://www.faithlifefellowship.us/iOS/sermons.plist"] encoding:NSUTF8StringEncoding error:nil];
NSLog(#"%#",pl);
returns:
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"><plist version="1.0"><dict><key>1</key><array><integer>1</integer><string>I've Been Redeemed</string><string>Ive Been Redeemed</string><array><string></string></array><array><string>03/20/09</string><string>04/02/09</string><string>04/10/09</string><string>04/24/09</string><string>05/13/09</string><string>05/23/09</string><string>06/06/09</string><string>06/13/09</string><string>06/20/09</string></array><string>http://faithlifefellowship.us/Sermons/Banners/Banner-IBR.png</string><integer>9</integer><array><string>Many Christians allow things in their lives, not realizing that they have been redeemed from them and because of ignorance of God's Word, do not walk in total freedom. This series will set you free in every area of your life!</string><string>It is important to be able to differentiate between a curse in our lives and suffering tribulation for the Lord's sake.</string><string>The same exact blessing that was on Abraham's life is now on our lives because of Jesus Christ. Don't let ignorance or unbelief keep you from all that God has for you!</string><string>Well, I guess God wants me to be sick to teach me a lesson, ever heard someone say that? Well that does not work EVER in the light of God's Word. Gal 3:13 Christ HAS redeemed us...</string><string>As we age the world says that we get weaker mentally, but God's Word does not agree with this. Part of what we have been redeemed from is an unsound and unstable mind.</string><string>Get up early, work late, still nothing ever seems to go your way why? Becasue of the curse. The Good News is that we have been redeemed from loss and failure in our lives!</string>
Blah blah blah and on and on...it's all there
There are unescaped ampersands (i.e. 'Pt 1 & 2') in this file, which is restricted. You should either replace them with & on your server, or download contents of the file to NSString and then replace & with &:
NSString *error;
NSString *str = [NSString stringWithContentsOfURL:[NSURL URLWithString:#"http://www.faithlifefellowship.us/iOS/sermons.plist"]
encoding:NSUTF8StringEncoding
error:&error];
str = [str stringByReplacingOccurrencesOfString:#" & " withString:#" & "];
NSData *plistData = [str dataUsingEncoding:NSUTF8StringEncoding];
NSPropertyListFormat format;
NSDictionary *plist = [NSPropertyListSerialization propertyListFromData:plistData
mutabilityOption:NSPropertyListImmutable
format:&format
errorDescription:&error];
Plus, there's a big problem with tags matching. You can validate your plist with http://www.xmlvalidation.com

write into plist file using NSDictionary object

Sorry I saw similar questions but they don't seem to have some full answers for me. And i try to put it in order so that people will not hate me or my poor english.
I am working with Xcode 4.2 with storyboard and ARC
I can read from my plist file. My task is simply to write back the updated value(s) to my plist file.
My plist is contain in "supporting files" sub folder of the main folder (where story-board is things goes). the file is call Global.plist and GlobalValue2 is a element of the file type string.
So the read file part looks like this
NSString *plistfile = [[NSBundle mainBundle] pathForResource:#"Global" ofType:#"plist"];
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:plistfile];
FirstValueTextBox.text = [[dict valueForKey:#"GlobalValue1"] stringValue];
learn it from some handy youtube video works just fine. updates the value to my text box.
The real problem comes in when I write back my plist file. When i try the following
NSString *plistfile = [[NSBundle mainBundle] pathForResource:#"Global" ofType:#"plist"];
NSMutableDictionary *dict = [NSMutableDictionary
dictionaryWithContentsOfFile:plistfile];
[dict setValue:#"ABC" forKey:#"GlobalValue2"];
SecondValueTextBox.text = [dict valueForKey:#"GlobalValue2"];
[dict writeToFile:plistfile atomically:YES];
the result is I really saw a updated value pop up on the second text box, but the plist file remain unchanged.
The following are the break down of my questions and my guess for the problem
I try to use NSDictionary(not NSMutableDictionary) and call setValue (crash in runtime)
my guess: NSDictionary object itself is readonly so it crash me when i say add value. But why don't it error me when in coding time? if the object is readonly
I use NSMutableDictionary can call setValue. it doesn't crash me and when i call the updated value at "SecondValueTextBox.text = [dict valueForKey:#"GlobalValue2"];" it really return me the updated value. but the content inside of the plist file is not changed. Which is the result I have right now.
my guess: after some search here and there I think "supporting files" is read only too. pure guess did see anyone directly talk about it.
I did try to move on a little more and some people talks about a "document folder" in Xcode that is a read and write place. I think people also talk about write a code to access that folder. Can someone show me the code here.
My last question, can I hook up my Xcode to that "document folder" or where can i see it(the real file folder structure is different from inside Xcode I think). So that i can see and edit my plist file for testing, and i can see the real result without using codes and stuff
I will be much appreciated if people can tell me my guess is right or wrong and the answer to my 3 and 4 question.
In order for your changes to be persisted in your plist, you would indeed need ot copy it from the resource bundle to the documents dirtectory on launch of the application, then use the plist in the documents to read and write.
Here is how you can copy the file:
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *plistPath = [documentsDirectory stringByAppendingPathComponent:#"Global.plist"];
if ([fileManager fileExistsAtPath:plistPath] == NO) {
NSString *resourcePath = [[NSBundle mainBundle] pathForResource:#"Global" ofType:#"plist"];
[fileManager copyItemAtPath:resourcePath toPath:plistPath error:&error];
}

How do I clear the data in a plist created in Xcode?

I have been using a plist to store data in my app. I have been able to write and read from the plist with no problem. I created this plist in Xcode, adding the rows of numbers, dictionaries, and arrays myself. However, I would like to be able to reset the plist to the original state, and there must be an easier way to do this than writing a 0 or nil value to every entry in the plist. So what is the easiest way to reset the plist to its initial default state?
The simplest thing would be to delete the file using NSFileManager, like this:
[[NSFileManager defaultManager] removeItemAtPath:plistPath error:NULL];
Or if you don't want to do that, assuming the plist is a dictionary, just load the one from your application bundle and then overwrite the one in your documents, like this:
NSDictionary *originalPlist = [NSDictionary dictionaryWithContentsOfFile:bundleFile];
[originalPlist writeToFile:documentsFile atomically:YES];
Which will overwrite the saved file with the original file.
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *fullPath = [documentsDirectory stringByAppendingPathComponent:#"mobile-watchlist.plist"];
[fileManager removeItemAtPath: fullPath error:NULL];
You could also try to just rename your Plist. Thats the least work i think.

i have xml file in my application that i want to write data into it

I have this xml file:
<?xml version="1.0" encoding="UTF-8"?>
<Favorites>
<favorite id="1">
<title>My first favorite</title>
<latitude>31.369834</latitude>
<longitude>34.798207</longitude>
</favorite>
</Favorites>
and I want to write more "favorite"s into it.
I have all the data I need as strings in my project.
But I can't figure out how to really do it - although I have tried a lot.
can you please help me do it ?
thanks.
I agree with the responders that you should consider using a .plist file. Creating NSDictionary objects of your "Favorite" objects, and saving the array of them.
But, to your immediate question, you could save a NSString representing your XML like this (but again, saving a string based XML means a lot of parsing and interaction methods created by you):
// Build The Path
//
NSArray * paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
NSUserDomainMask,
YES);
NSString * filePath = [paths objectAtIndex:0];
filePath = [filePath stringByAppendingPathComponent:#"favorites"];
filePath = [filePath stringByAppendingPathExtension:#"xml"];
// You Should Have A "Favorite" Objects That Know How To Desribe Themselves...
//
NSString * badWayToDoThis = #"<ThinkAboutUsingDictionaries>Saving The Data to a plist</seriously>";
[badWayToDoThis writeToFile:filePath
atomically:YES
encoding:NSUTF8StringEncoding
error:NULL];
Hope that at the very least gets you towards where you're headed.
You could use a plist and create functions that:
First - creates an array or dictionary from the plist
Second - adds new values to your array/dictionary
Third - saves the dictionary to the same file, overwriting and updating the old version.
So rather that inserting values you just overwrite with an updated version

How do I store a string as an array in a Cocoa property list?

I am trying to save two strings. One string needs to be saved as type ARRAY in the pList and the second string needs to be saved as String in the Array.
I can use the code:
[dictionary setObject:(id)anObject forKey:(id)aKey>]
but it doesn't save it correctly. I can cast one of the strings as an array, but it still doesn't work right.
What is the proper method for saving an array to the pList?
Here is what my pList looks like:
<dict>
<key>Test One</key>
<array>
<string>A13#</string>
</array>
<key>Another Test</key>
<array>
<string>1111111111</string>
</array>
<key>Test Three</key>
<array>
<string>2222222222</string>
</array>
<key>Final Test</key>
<array>
<string>3333333333</string>
</array>
</dict>
here is the method I am using to try to
-(void)writeToFile:(NSString *)s1{
NSBundle *bundle = [NSBundle mainBundle];
NSString *plistPath = [bundle pathForResource:#"saved" ofType:#"plist"];
NSMutableDictionary *dictionary = [[[NSDictionary alloc] initWithContentsOfFile:plistPath] mutableCopy];
NSString *tempString = [NSString stringWithFormat:#"1234567"];
[dictionary setObject:tempString forKey:s1];
[dictionary writeToFile:plistPath atomically:YES];
}
You can't cast or otherwise convert a string into an array; they're separate, distinct objects. It's the same as if in real life you try to turn your dog into a station wagon, it isn't happening.
Instead, put your dog inside the station wagon (or put your string(s) inside an array). You can create the array with [NSArray arrayWithObjects:#"string1", #"string2", nil];. Stick that inside your dictionary for a given key, along with your final string for another key, save it, and you'll have a plist with an array of one or more strings.
Also, in your code example your dictionary is leaking memory. Read up on memory management in Objective-C, you're going to run into lots of crashes and performance issues until you understand it well.
You an convert a string to a single element array with
[NSArray arrayWithObject:str];
So if you want your plist to contain entries as arrays of strings, and you want just a single string as an element, then you do something like:
[dictionary setObject:[NSArray arrayWithObject:tempString] forKey:s1];
I don't actually no why you would want it this way unless you want to allow for multiple strings per key at some other time.
Also, as Marc mentioned, you are leaking the initial (unmutable) dectionary you create. Read the memory management rules at http://developer.apple.com/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html.
Further, you should never write inside your application's bundle. For one thing, your application may be on a write protected volume or the current user may not have permissions to change it. For another, the file would then be shared by all users. And for a third, it would break the code signing. Instead, write the file to either the Preferences folder or the Application Support folder.
And finally, if these are intended to be user preferences of some sort, then you should use the preferences system, which allows configuring defaults and stores the preferences in the preferences folder for you. See http://developer.apple.com/documentation/Cocoa/Conceptual/UserDefaults/UserDefaults.html for more information.
The correct way to save an NSArray (by itself) to a plist file is as follows:
NSArray* anArray = ...;
[anArray writeToFile:#"/path/to/file.plist" atomically:YES];
However, you can't save an NSString as an array. Given the XML plist you provided, if you want to add entries with the same format, you can use this much simpler code:
- (void) writeToFile:(NSString *)string {
NSString *plistPath = [[NSBundle mainBundle] pathForResource:#"saved" ofType:#"plist"];
NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithContentsOfFile:plistPath];
[dictionary setObject:[NSArray arrayWithObject:#"1234567"] forKey:string];
[dictionary writeToFile:plistPath atomically:YES];
}
This also avoids a memory leak in your code, where the receiver of -mutableCopy escapes with a retain count of 1. (This isn't a problem under GC, but it's still bad practice.) You shouldn't need to use +[NSString stringWithFormat:], just use a string literal. If you want to use a different as the string in the array, you can either pass it in as an additional parameter, grab it from another method, etc.
This method is still brittle in that it only stores one string in the array matched with the given key — also, the method name would be better if it were more indicative of exactly what it does. Also, if there will only ever be one string value for each key, you might consider revising the plist to omit the arrays entirely, since it just chews up space and complicates the code.