Accessing a plist with a console application - Xcode 4 - objective-c

I've created a plist (File->New File->Resource plist) file that has a dictionary that stores floating point numbers for a console application. I've pretty much created this plist to store a few numbers to be returned by class methods, but could change so hardcoding isn't really that great of an idea. However, when I try to access the plist file, I always get "nil" for the file path and I'm not sure what I'm doing wrong.
<plist version="1.0">
<dict>
<key>minFloatNumber</key>
<real>520.0</real>
</dict>
</plist>
+ (double) minFloatingPointNunber
{
NSString *path = [[NSBundle mainBundle] pathForResource: #"FloatDictionary"
ofType: #"plist"];
NSDictionary *dict = [[NSDictionary alloc] initWithContentsOfFile: path];
[path release];
double f = [[dict objectForKey: #"minFloatNumber"] doubleValue];
[dict release];
return f;
}
I've read various articles discussing looking at the project's "Build Phases" and looking at "Copy Bundle Resources," but I don't see any of that anywhere. I'm pretty sure there has to be something wrong with my setup, but not sure what I'm missing, or the steps I need to take to remedy the situation. Thanks in advance for the tips.

The "Console Application" template will be set up to produce a single binary — not included inside a proper bundle, so you can't use the NSBundle APIs to access files. If you want to include a file, you could make an actual application (where you'll be able to use [NSBundle mainBundle]), or you could include the data inside the binary (as a compile-time constant or some such).

Related

Xcode - Write and Delete to Plist

I have a plist with two arrays that looks like this:
I currently load the plist like this:
// Find path of plist
NSString *path = [[NSBundle mainBundle] pathForResource:#"nameofPList" ofType:#"plist"];
// Read the data into arrays
NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithContentsOfFile:path];
NSMutableArray *tableData = [dict objectForKey:#"array2"];
How do I write new items to the "array2" array in the plist (code, not manually)
How do I delete items from "array2"? (code, not manually)
Thank you.
You probably want:
NSMutableArray *tableData = [dict[#"array2"] mutableCopy];
^^^^^^^^^^^
(otherwise you'll get an immutable array).
Add items with:
[tableData addObject:#"new item"];
Delete with:
[tableData removeObjectAtIndex:someIndex];
or:
[tableData removeObject:someObject];
and anything else you can find in the NSMutableArray Class Reference.
However "write back" sounds like you are attempting to write the .plist back into the app bundle. While this will work in the iOS Simulator, it will fail on a real devices.
You cannot write to the app bundle at runtime; write to the Documents folder instead and be flexible about where to read from.

Objective c save to my plist

I have the plist file under the resources folder which is name is logical-app.plist
In ios8 if you want Gps will work then NSLocationAlwaysUsageDescription or NSLocationWhenInUseUsageDescription keys should be added into the Info.plist
I want to add NSLocationAlwaysUsageDescription or NSLocationWhenInUseUsageDescription keys in code in my appdelegate class.
Im trying to add the following code for solving this issue:
//write
filePath = #"/System/Library/CoreServices/SystemVersion.plist";
plistDict = [[NSMutableDictionary alloc] initWithContentsOfFile:filePath];
[plistDict setValue:#"1.1.1" forKey:#"NSLocationWhenInUseUsageDescription"];
[plistDict writeToFile:filePath atomically: YES];
//endwrite
//read plist
filePath = #"/System/Library/CoreServices/SystemVersion.plist";
plistDict = [[NSMutableDictionary alloc] initWithContentsOfFile:filePath];
value = [plistDict objectForKey:#"NSLocationWhenInUseUsageDescription"];
im trying to get
filePath = [[NSBundle mainBundle] pathForResource:#"ClickMobileCDV-Info" ofType:#"plist"];
and it is nil too....
But with no success.... when i read it from the plist it was nil...
How can i solve this issue?
You've completely got the wrong end of the stick. In order to add NSLocationAlwaysUsageDescription and NSLocationWhenInUseUsageDescription, you edit the app's plist file within Xcode and it's included in the app bundle.
There is no code needed to read/write system .plist files at runtime (the writing of which would fail anyway, due to permission errors).

plist wont pull into my NSMutableArray. am i missing something?

So here's my code. I'm trying to display content from a plist into a table and i keep running into errors. ive been at it all day so i'm probably missing something fairly easy. hope you guys can figure out why when i build, the table is empty.
here is my plist (without headers):
<array>
<dict>
<key>word</key>
<string>AWord1</string>
<key>definition</key>
<string>here is a definition</string>
<key>partOfSpeech</key>
<string>here is a part of speech</string>
</dict>
<dict>
<key>word</key>
<string>Bword1</string>
<key>definition</key>
<string>here is a definition</string>
<key>partOfSpeech</key>
<string>here is a part of speech</string>
</dict>
</array>
</plist>
edit: closing tags were missing due to misformatting - fixed
ok. here is my new code. i NSLog'd and decided to back up to before i went to plist. so, if you could help. how would you suggest i pull this data from plist instead (assuming the data is in the plist)
//Initialize the array.
listOfItems = [[NSMutableArray alloc] init];
NSArray *countriesToLiveInArray = [NSArray arrayWithObjects:#"Iceland", #"Greenland", #"Switzerland", #"Norway", #"New Zealand", #"Greece", #"Italy", #"Ireland", nil];
NSDictionary *countriesToLiveInDict = [NSDictionary dictionaryWithObject:countriesToLiveInArray forKey:#"Countries"];
NSArray *countriesLivedInArray = [NSArray arrayWithObjects:#"India", #"U.S.A", #"Test", #"Test", #"Test", #"Test", #"Test", #"Test", #"Test", #"Test", #"Test", #"Test", #"Test", #"Test", #"Test", #"Test", nil];
NSDictionary *countriesLivedInDict = [NSDictionary dictionaryWithObject:countriesLivedInArray forKey:#"Countries"];
[listOfItems addObject:countriesToLiveInDict];
[listOfItems addObject:countriesLivedInDict];
//Initialize the copy array.
copyListOfItems = [[NSMutableArray alloc] init];
//Set the title
self.navigationItem.title = #"Countries";
//Add the search bar
self.tableView.tableHeaderView = searchBar;
searchBar.autocorrectionType = UITextAutocorrectionTypeNo;
searching = NO;
letUserSelectRow = YES;
i feel as though i'm still not actually telling it to parse the data from the plist. i've only init'd it. thoughts?
Your code seems very incomplete, it doesn't bear much relation to the plist. Here are a few issues
as noted by H2CO3 and Fogmeister, the plist as you show it is missing a closing </array> tag (but lets assume that is a typo in the question)
you load the entire array into a single entry in a countriesToLiveIn dictionary (against the key "definition")
countriesToLiveIn becomes a single object in the array listOfItems
you initialise a copyOfListItems array but then don't do anything with it
Then you do not indicate where the table is getting it's date from.
If it is from copyOfListItems, well that array is empty .: empty table.
If it is from listOfItems you will be parsing the countriesToLiveIn dictionary somehow, but you don't show how.
If it is directly from the plist array countriesToLiveInArray then that missing </array> tag could be a clue. You would also need to be parsing the array's contained dictionaries somehow, and you don't show us that code, there could be something wrong there.
To get further with this I suggest copious use of NSLog to find out how far the data gets in to your objects. Perhaps you could expand your question with the results if you are still getting stuck.
update
Following your updated code, my first suspicion is that there was a problem with your URL.
in your previous edit this is the suspect line
NSMutableArray *wordListArray = [[NSMutableArray alloc] initWithContentsOfURL: [NSURL URLWithString:#"http://url.com/dictpPlist.plist"]];
You need to NSLog right after it:
NSLog (#"wordListArray %#", wordListArray);
To confirm that you are not picking up the plist here. At least then we isolate the problem to this point in your code.
Then we need to question the URL - is it correct? how did the plist get there? can you verify it independently? Is it a local file URL or a network URL?
If you are accessing a network resource, this is quite likely your problem. The initWithContentsOfURL methods should only really be used with local filesystem URLs. See for example
initwithcontentsofurl methods considered harmful.
You should be using NSURLConnection asynchronously instead. Apple is a little cagey about this also:
aURL: The location of a file containing a string representation of an array produced by the writeToURL:atomically: method.
Assuming this is your issue, why don't you get it working with a local filesystem plist first.
Write one in code from your listOfItems array:
- (void) createPlist
{
NSFileManager* fManager = [NSFileManager defaultManager];
NSError* error;
NSURL* docsURL = [fManager URLForDirectory:NSDocumentDirectory
inDomain:NSUserDomainMask
appropriateForURL:nil
create:NO
error:&error];
NSURL* plistURL = [docsURL URLByAppendingPathComponent:#"testlist.plist"];
[listOfItems writeToURL:plistURL atomically:YES];
}
Read it back in, verify it works, then sort out how to do the same over a network.
try below code
// Path to the plist (in the application bundle)
NSString *path = [[NSBundle mainBundle] pathForResource:
#"dictpPlist" ofType:#"plist"];
// Build the array from the plist
NSMutableArray *array2 = [[NSMutableArray alloc] initWithContentsOfFile:path];
// Show the string values
for (NSString *str in array2)
NSLog(#"--%#", str);

A smart way to retrieve allowed file types from plist

Scenario:
I like to define the allowed file types (content types) in the Info.plist file of my Cocoa application. Therefore, I added them like the following example shows.
# Extract from Info.plist
[...]
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeName</key>
<string>public.png</string>
<key>CFBundleTypeIconFile</key>
<string>png.icns</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSIsAppleDefaultForType</key>
<true/>
<key>LSItemContentTypes</key>
<array>
<string>public.png</string>
</array>
</dict>
[...]
Further, my application allows to open files using an NSOpenPanel. The panel allows to set the allowed file types through the following selector: setAllowedFileTypes:. The documentation states that UTI can be used.
The file type can be a common file extension, or a UTI.
A custom solution:
I wrote the following helper method to extract the UTI from the Info.plist file.
/**
Returns a collection of uniform type identifiers as defined in the plist file.
#returns A collection of UTI strings.
*/
+ (NSArray*)uniformTypeIdentifiers {
static NSArray* contentTypes = nil;
if (!contentTypes) {
NSArray* documentTypes = [[[NSBundle mainBundle] infoDictionary] objectForKey:#"CFBundleDocumentTypes"];
NSMutableArray* contentTypesCollection = [NSMutableArray arrayWithCapacity:[documentTypes count]];
for (NSDictionary* documentType in documentTypes) {
[contentTypesCollection addObjectsFromArray:[documentType objectForKey:#"LSItemContentTypes"]];
}
contentTypes = [NSArray arrayWithArray:contentTypesCollection];
contentTypesCollection = nil;
}
return contentTypes;
}
Instead of [NSBundle mainBundle] also CFBundleGetInfoDictionary(CFBundleGetMainBundle()) can be used.
Questions:
Do you know a smarter way to extract the content type information
from the Info.plist file? Is there a Cocoa-build-in function?
How do you deal with the definition of folders that can contained
there, e.g. public.folder?
Note:
Throughout my research, I found this article quite informative: Simplifying Data Handling with Uniform Type Identifiers.
Here is how I read information from a plist (it can be the info.plist or any other plist you have in you project provided you set the correct path)
NSString *resourcePath = [[NSBundle mainBundle] resourcePath];
NSString *fullPath = [NSString stringWithFormat:#"%#/path/to/your/plist/my.plist", resourcePath];
NSData *plistData = [NSData dataWithContentsOfFile:fullPath];
NSDictionary *plistDictionary = [NSPropertyListSerialization propertyListFromData:plistData mutabilityOption:NSPropertyListImmutable format:0 errorDescription:nil];
NSArray *fileTypes = [plistDictionary objectForKey:#"CFBundleDocumentTypes"];

How can I read the iTunesMetadata.plist in Objective-C? (ipa file is on disk)

Ok I can get the list of IPA files in my iTunes folder. what I want to do is be able to read the plist file in the IPA file.
I'm very new to Objective-C so sorry if this is an obvious question (I did look).
I tried the following but it comes back null.
// i = NSURL. value eg. "file://localhost/Users/jiyeon/Desktop/test/test.ipa"
NSDictionary *plistInfo = [NSDictionary dictionaryWithContentsOfFile:[[i absoluteString]
stringByAppendingPathComponent:#"iTunesMetadata.plist"]];
However plistInfo just ends up being null.
Try -(NSString *)path NSURL
NSDictionary *plistInfo = [NSDictionary dictionaryWithContentsOfFile:[[i path]
stringByAppendingPathComponent:#"iTunesMetadata.plist"]];
this should work