Is there any way to find the application identifier prefix in you iPhone application programmatically? Or even just to get the entire Application Identifier string?
I see you can find it by peeking into the "embedded.mobileprovision" file, but is there an easier way? (And I don't think that works if you're running in the simulator)
EDIT: Sorry, what I mean is the identifier PREFIX (10 characters). I've realized though that I don't actually need this, because the rest of the ID is guaranteed to be unique anyway.
The bundle id is stored in the Info.plist of the app bundle as St3fan specified, but you should not grope at the Info.plist directly. Instead, use:
[[NSBundle mainBundle] bundleIdentifier]
You can programmatically retrieve the Bundle Seed ID by looking at the access group attribute (i.e. kSecAttrAccessGroup) of an existing KeyChain item. In the code below, I look up for an existing KeyChain entry and create one if it doesn't not exist. Once I have a KeyChain entry, I extract the access group information from it and return the access group's first component separated by "." (period) as the Bundle Seed ID.
+ (NSString *)bundleSeedID {
NSDictionary *query = [NSDictionary dictionaryWithObjectsAndKeys:
kSecClassGenericPassword, kSecClass,
#"bundleSeedID", kSecAttrAccount,
#"", kSecAttrService,
(id)kCFBooleanTrue, kSecReturnAttributes,
nil];
CFDictionaryRef result = nil;
OSStatus status = SecItemCopyMatching((CFDictionaryRef)query, (CFTypeRef *)&result);
if (status == errSecItemNotFound)
status = SecItemAdd((CFDictionaryRef)query, (CFTypeRef *)&result);
if (status != errSecSuccess)
return nil;
NSString *accessGroup = [(NSDictionary *)result objectForKey:kSecAttrAccessGroup];
NSArray *components = [accessGroup componentsSeparatedByString:#"."];
NSString *bundleSeedID = [[components objectEnumerator] nextObject];
CFRelease(result);
return bundleSeedID;
}
If I understand correctly you are looking for bundle identifier? if you you can get this way.
NSString* appID = [[[NSBundle mainBundle] infoDictionary] objectForKey:#"CFBundleIdentifier"];
Related
i added a key called "App" to my AppName-Info.plist manually , i can get the value from it by calling this code
NSBundle *mainBundle;
mainBundle = [NSBundle mainBundle];
NSString *value = [mainBundle objectForInfoDictionaryKey:#"App"];
NSLog(#"App: %#",value);
But what i couldn't do is changing the value with any code.. is it possible ? and if yes how can it be done ?
thanks for help in advance :)
You should consider NSUserDefaults or if you want to modify a bundled .plist try this.
NSString* plistFilePath = nil;
NSFileManager* manager = [NSFileManager defaultManager];
if ((plistFilePath = [[[NSBundle mainBundle] bundlePath] stringByAppendingPathComponent:#"mySpecial/PathTo.plist"]))
{
if ([manager isWritableFileAtPath:plistFilePath])
{
NSMutableDictionary* infoDictioio = [NSMutableDictionary dictionaryWithContentsOfFile:plistFilePath];
[infoDictio setObject:#"foo object" forKey:#"fookey"];
[infoDictio writeToFile:plistFilePath atomically:NO];
[manager setAttributes:[NSDictionary dictionaryWithObject:[NSDate date] forKey:NSFileModificationDate] ofItemAtPath:[[NSBundle mainBundle] bundlePath] error:nil];
}
}
You should not modify your apps Info.plist file (or anything in your app's bundle) at runtime. This is bad practice and will also break your bundles code signature (which will result in not being able to launch the app anymore!).
If you want to store settings you should have a look at NSUserDefaults.
It's not possible.
By default the AppName-Info.plist isn't copied into the bundle in the Copy Bundle Resources phase of the build.
So if you want to have a plist which you can write to an option would be to create it at run time in the temporary files location and read/write to it there.
This is a great place to research how to do it.
I am currently creating a controller class for my plist.in this plist I have a root dictionary that has several types in it (Number, String and Dictionary), In my controller class I check for a plist then add it to the documents so I can read and write to it.
From here I read whats in my current plist and pass those values over to tempvars I have set up in this class.
This is what my read method looks like in my plist controller class.
-(void) readPlistData
{
// Data.plist code
// get paths from root direcory
NSArray *paths = NSSearchPathForDirectoriesInDomains (NSDocumentDirectory, NSUserDomainMask, YES);
// get documents path
NSString *documentsPath = [paths objectAtIndex:0];
// get the path to our Data/plist file
NSString *plistPath = [documentsPath stringByAppendingPathComponent:#"EngineProperties.plist"];
// check to see if Data.plist exists in documents
if (![[NSFileManager defaultManager] fileExistsAtPath:plistPath])
{
// if not in documents, get property list from main bundle
plistPath = [[NSBundle mainBundle] pathForResource:#"EngineProperties" ofType:#"plist"];
}
// read property list into memory as an NSData object
NSData *plistXML = [[NSFileManager defaultManager] contentsAtPath:plistPath];
NSString *errorDesc = nil;
NSPropertyListFormat format;
// convert static property liost into dictionary object
NSDictionary *temp = (NSDictionary *)[NSPropertyListSerialization propertyListFromData:plistXML mutabilityOption:NSPropertyListMutableContainersAndLeaves format:&format errorDescription:&errorDesc];
if (!temp)
{
NSLog(#"Error reading plist: %#, format: %d", errorDesc, format);
}
// assign values
self.protocolSignature = [temp objectForKey:#"Protocol"];
self.requestNumber = [temp objectForKey:#"RequestNumber"];
//How do I add the dictionary values here?
}
The reason I put the data into variables is because latter I am going to use these values to test against checks I want to perform against my db.. making sure of things like i am receiving the correct request number etc.
UPDATE:: my idea to add them to the dictionary inside the root dictionary would be something like this. which i think is not even close but it might give you a better clue to what I am trying to do.
self.cacheValue = [temp objectForKey:#"Cache Value"];
self.manufacturers = [cacheValue objectForKey:#"Manufacturers"];
self.models = [cacheValue objectForKey:#"Model"];
self.subModels = [cacheValue objectForKey:#"SubModels"];
any help would be greatly appreciated.
I believe you want to do the following:
Define your cacheValue property in the .h as a mutable dictionary.
NSMutableDictionary *cacheValue;
Serialize the plistXml as a NSMutableDictionary:
// This is the root Dictionary
NSMutableDictionary *temp = (NSMutableDictionary *)[NSPropertyListSerialization propertyListWithData:plistXML options:NSPropertyListMutableContainersAndLeaves format:NSPropertyListXMLFormat_v1_0 error:&error];
Since everything is mutable, you can now read, update, insert, delete any part of the dictionary or its subcontents. For instance, grabbing the Mutable Dictionary "Cache Value" is just:
self.cacheValue = [temp objectForKey:#"Cache Value"];
Remember to check that the object is not nil in case there isn't a value for the key. The key needs to be exactly as it appears in the plist.
Updating a value in the Mutable Dictionary is easy:
[self.cache setValue:#"New Value" forKey:#"Sub"];
And finally, to save the changes in the root Mutable Dictionary back to the plist:
/*
The flag "atomically" specifies whether the file should be written atomically or not.
If flag is YES, the receiver is written to an auxiliary file, and then the auxiliary file is renamed to path.
If flag is NO, the dictionary is written directly to path.
The YES option guarantees that path will not be corrupted even if the system crashes during writing.
*/
[self.temp writeToFile:plistPath atomically:YES];
Hope this helps, cheers!
I was planning on writing some code whose logic was based upon testing the creation date of a particular file in my app's Documents folder. Turns out, when I call -[NSFileManager attributesOfItemAtPath:error:], NSFileCreationDate isn't one of the provided attributes.
Is there no way to discover a file's creation date?
Thanks.
The fileCreationDate is indeed part of the dictionary. Here's a method that gets passed a file URI and grabs some of the attributes from the file:
- (NSDictionary *) attributesForFile:(NSURL *)anURI {
// note: singleton is not thread-safe
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *aPath = [anURI path];
if (![fileManager fileExistsAtPath:aPath]) return nil;
NSError *attributesRetrievalError = nil;
NSDictionary *attributes = [fileManager attributesOfItemAtPath:aPath
error:&attributesRetrievalError];
if (!attributes) {
NSLog(#"Error for file at %#: %#", aPath, attributesRetrievalError);
return nil;
}
NSMutableDictionary *returnedDictionary =
[NSMutableDictionary dictionaryWithObjectsAndKeys:
[attributes fileType], #"fileType",
[attributes fileModificationDate], #"fileModificationDate",
[attributes fileCreationDate], #"fileCreationDate",
[NSNumber numberWithUnsignedLongLong:[attributes fileSize]], #"fileSize",
nil];
return returnedDictionary;
}
According to Apple's reference, NSFileCreationDate is available in 2.0+:
NSFileCreationDate The key in a file
attribute dictionary whose value
indicates the file's creation date.
The corresponding value is an NSDate
object.
Available in iPhone OS 2.0 and later.
Declared in NSFileManager.h.
I've got a default settings plist file in the resources folder of my app, and on the first launch that gets copied to the documents folder.
In successive versions of the app, how can I merge the plist settings in their documents with any new keys & values (possibly nested) that have been added since the previous version?
I've seen a pattern where properties are actually created as an NSDictionary in the app (with all default settings), and then the current settings saved in the plist file is merged with that dictionary, and that is then saved over the current plist.
Is that a good approach? If so, how do you go about merging NSDictionary's that could have several nested values with sub arrays and sub dictionaries?
Also, is it advised to have a separate custom plist file for settings, or should you always use NSUserDefaults? Does NSUserDefaults handle versioning and changing defaults?
Many thanks,
Mike
Okay I think I've come up with the best way to do it:
- (void)readSettings {
// Get Paths
NSString *defaultSettingsPath = [[NSBundle mainBundle] pathForResource:#"DefaultSettings" ofType:#"plist"];
NSString *settingsPath = [self.applicationDocumentsPath stringByAppendingPathComponent:#"Settings.plist"];
// Read in Default settings
self.settings = [NSMutableDictionary dictionaryWithContentsOfFile:defaultSettingsPath];
// Read in Current settings and merge
NSDictionary *currentSettings = [NSDictionary dictionaryWithContentsOfFile:settingsPath];
for (NSString *key in [currentSettings allKeys]) {
if ([[self.settings allKeys] indexOfObject:key] != NSNotFound) {
if (![[currentSettings objectForKey:key] isEqual:[self.settings objectForKey:key]]) {
// Different so merge
[self.settings setObject:[currentSettings objectForKey:key] forKey:key];
}
}
}
}
- (void)saveSettings {
if (self.settings) {
NSString *settingsPath = [self.applicationDocumentsPath stringByAppendingPathComponent:#"Settings.plist"];
[self.settings writeToFile:settingsPath atomically:YES];
}
}
My iphone app writes key-value pairs to a dictionary in a plist file. I'm basically saving the user's score when they play the game. This is all fine and dandy, but each time I run the app and get new scores, the new values get saved over the old values. How do I add information to the plist each time the user accesses the app instead of rewriting the file? I want to keep all of the scores, not just the most recent one.
code:
-(void)recordValues:(id)sender {
//read "propertyList.plist" from application bundle
NSString *path = [[NSBundle mainBundle] bundlePath];
NSString *finalPath = [path
stringByAppendingPathComponent:#"propertyList.plist"];
dictionary = [NSMutableDictionary dictionaryWithContentsOfFile:finalPath];
//create an NSNumber object containing the
//float value userScore and add it as 'score' to the dictionary.
NSNumber *number=[NSNumber numberWithFloat:userScore];
[dictionary setObject:number forKey:#"score"];
//dump the contents of the dictionary to the console
for (id key in dictionary) {
NSLog(#"memory: key=%#, value=%#", key, [dictionary
objectForKey:key]);
}
//write xml representation of dictionary to a file
[dictionary writeToFile:#"/Users/rthomas/Sites/propertyList.plist" atomically:NO];
}
You are setting the object to a number for key score
NSNumber *number=[NSNumber numberWithFloat:userScore];
[dictionary setObject:number forKey:#"score"];
Instead of this what you want to do is have an array or something of the sort so
NSNumber *number=[NSNumber numberWithFloat:userScore];
NSMutableArray *array=[dictionary objectForKey:#"score"]
[array addObject:number]
[dictionary setObject:array forKey:#"score"]
this should do what you are asking
You want to load the old values first, in a NSArray or NSDictionary like Daniel said.
The you add the new value to the collection. (maybe do some sorting or something also)
Then you write the new collection back to disk.