13

I am working on an iPhone app

I read a key from root.plist like this :

NSString *Key1Var = [[NSUserDefaults standardUserDefaults] stringForKey:@"Key1"];

("Key1" is a PSMultiValueSpecifier for which a default string value has been set already in root.plist)

That works fine, once the user makes settings. But if the user runs the app before he does any setting, he will get nil for "Key1". In such case, I was expecting the default value that i had set for "Key1". what i need to do, so that the user does not have to do setting, to make application run for the first time?

Regards, Harish

Mike Weller
  • 45,401
  • 15
  • 131
  • 151
Harish J
  • 525
  • 1
  • 4
  • 16
  • possible duplicate of [Can you make the settings in Settings.bundle default even if you don't open the Settings App](http://stackoverflow.com/questions/510216/can-you-make-the-settings-in-settings-bundle-default-even-if-you-dont-open-the) – Mike Weller May 08 '12 at 11:38
  • By 'possible', I mean 'exact'. – Mike Weller May 08 '12 at 11:45

7 Answers7

15

See this question for a complete solution.

You essentially want to run this code before accessing the setting:

- (void)registerDefaultsFromSettingsBundle {
    NSString *settingsBundle = [[NSBundle mainBundle] pathForResource:@"Settings" ofType:@"bundle"];
    if(!settingsBundle) {
        NSLog(@"Could not find Settings.bundle");
        return;
    }

    NSDictionary *settings = [NSDictionary dictionaryWithContentsOfFile:[settingsBundle stringByAppendingPathComponent:@"Root.plist"]];
    NSArray *preferences = [settings objectForKey:@"PreferenceSpecifiers"];

    NSMutableDictionary *defaultsToRegister = [[NSMutableDictionary alloc] initWithCapacity:[preferences count]];
    for(NSDictionary *prefSpecification in preferences) {
        NSString *key = [prefSpecification objectForKey:@"Key"];
        if(key) {
            [defaultsToRegister setObject:[prefSpecification objectForKey:@"DefaultValue"] forKey:key];
        }
    }

    [[NSUserDefaults standardUserDefaults] registerDefaults:defaultsToRegister];
    [defaultsToRegister release];
}

This will load the default values into the standardUserDefaults object so you will no longer get back nil values, and you don't have to duplicate the default settings in your code.

Community
  • 1
  • 1
Mike Weller
  • 45,401
  • 15
  • 131
  • 151
  • Unfortunately this code doesn't work in Swift 2.2 on Xcode 7.3, I get errors about `stringByAppendingPathComponent` being unavailable. – Dai Mar 27 '16 at 23:38
4

I do this early after launch, before I try to get my settings:

    userDefaultsValuesPath=[[NSBundle mainBundle] pathForResource:@"UserDefaults"
                                                           ofType:@"plist"];
    userDefaultsValuesDict=[NSDictionary dictionaryWithContentsOfFile:userDefaultsValuesPath];

    // set them in the standard user defaults
    [[NSUserDefaults standardUserDefaults] registerDefaults:userDefaultsValuesDict];

    if (![[NSUserDefaults standardUserDefaults] synchronize])
        NSLog(@"not successful in writing the default prefs");
mahboudz
  • 39,196
  • 16
  • 97
  • 124
  • But what if your path is the default "root.plist" in the settings.bundle? – jowie Aug 31 '10 at 14:38
  • Joe: Then you get a reference to that bundle and send it `-pathForResource:`. If you can't do that for whatever reason, you could just copy the plist into your app bundle's resources as well as into the settings bundle's. – Jeremy W. Sherman Sep 25 '10 at 00:06
4

A Swift 3 version based on Mike Weller's original solution if anyone needs it:

static func registerDefaultsFromSettingsBundle() {

    guard let settingsBundle = Bundle.main.url(forResource: "Settings", withExtension: "bundle") else {
        print("Could not find Settings.bundle")
        return
    }
    guard let settings = NSDictionary(contentsOf: settingsBundle.appendingPathComponent("Root.plist")) else {
        print("Couldn't find Root.plist in settings bundle")
        return
    }

    guard let preferences = settings.object(forKey: "PreferenceSpecifiers") as? [[String: AnyObject]] else {
        print("Root.plist has an invalid format")
        return
    }

    var defaultsToRegister = [String: AnyObject]()
    for var p in preferences {
        if let k = p["Key"] as? String, let v = p["DefaultValue"] {
            print("Registering " + v.debugDescription + " for key " + k)
            defaultsToRegister[k] = v as AnyObject
        }
    }

    UserDefaults.standard.register(defaults: defaultsToRegister)
}
Community
  • 1
  • 1
Andrew Lombard
  • 419
  • 4
  • 18
3

Here is the code I use in iOS 7, based heavily on Mike Weller's code above.

Put this method in your AppDelegate.m:

- (void)registerDefaultsFromSettingsBundleWithPlist:(NSString *)plist {
NSString *settingsBundle = [[NSBundle mainBundle] pathForResource:@"Settings" ofType:@"bundle"];
if(!settingsBundle) {
    NSLog(@"Could not find Settings.bundle");
    return;
}
NSString *bundle = [NSString stringWithFormat:@"%@.plist",plist];
NSDictionary *settings = [NSDictionary dictionaryWithContentsOfFile:[settingsBundle stringByAppendingPathComponent:bundle]];
NSArray *preferences = [settings objectForKey:@"PreferenceSpecifiers"];

NSMutableDictionary *defaultsToRegister = [[NSMutableDictionary alloc] initWithCapacity:[preferences count]];
for(NSDictionary *prefSpecification in preferences) {
    NSString *key = [prefSpecification objectForKey:@"Key"];
    if(key) {
        [defaultsToRegister setObject:[prefSpecification objectForKey:@"DefaultValue"] forKey:key];
    }
}

[[NSUserDefaults standardUserDefaults] registerDefaults:defaultsToRegister];
//[defaultsToRegister release];
}

And then call it for every settings file you're using (for nested settings), from some place early in your code like didFinishLaunchingWithOptions:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//register default settings into NSUserDefaults
    @try {
        [self registerDefaultsFromSettingsBundleWithPlist:@"Root"];
        [self registerDefaultsFromSettingsBundleWithPlist:@"Chat"];
        [self registerDefaultsFromSettingsBundleWithPlist:@"IVR"];
        [self registerDefaultsFromSettingsBundleWithPlist:@"Video"];
    }
    @catch (NSException * e) {
        NSLog(@"Exception: %@", e);
        NSLog(@"Try adding the Default Value field to each preference item in the Settings.bundle plist files.");
    }
    @finally {

    }
    ...
Community
  • 1
  • 1
2

I've translated Mike Weller's solution into Swift 2.0/iOS 9 and made it work for my App:

func registerDefaultsFromSettingsBundle() {
    guard let settingsBundle = NSBundle.mainBundle().URLForResource("Settings", withExtension:"bundle") else {
        NSLog("Could not find Settings.bundle")
        return;
    }

    guard let settings = NSDictionary(contentsOfURL: settingsBundle.URLByAppendingPathComponent("Root.plist")) else {
        NSLog("Could not find Root.plist in settings bundle")
        return
    }

    guard let preferences = settings.objectForKey("PreferenceSpecifiers") as? [[String: AnyObject]] else {
        NSLog("Root.plist has invalid format")
        return
    }

    var defaultsToRegister = [String: AnyObject]()
    for var p in preferences {
        if let k = p["Key"] as? String, v = p["DefaultValue"] {
            NSLog("%@", "registering \(v) for key \(k)")
            defaultsToRegister[k] = v
        }
    }

    NSUserDefaults.standardUserDefaults().registerDefaults(defaultsToRegister)
}
Community
  • 1
  • 1
BaseZen
  • 8,650
  • 3
  • 35
  • 47
0
NSBundle* mainBundle = [NSBundle mainBundle]; 

 

// Reads the value of the custom key I added to the Info.plist
NSString *value = [mainBundle objectForInfoDictionaryKey:@"key"];

//Log the value
NSLog(@"Value = %@", value);

// Get the value for the "Bundle version" from the Info.plist
[mainBundle objectForInfoDictionaryKey:@"CFBundleVersion"];

// Get the bundle identifier
[mainBundle bundleIdentifier];
Nikhil Dinesh
  • 3,359
  • 2
  • 38
  • 41
0

In my application delegate, I override the +initialize method and register new application default preferences.

For example:

+ (void) initialize {
    if ([self class] == [MyAppDelegate class]) {        
        // initialize user defaults dictionary
        BOOL isFirstTimeRun = YES;
        BOOL isKeychainTurnedOn = NO;
        BOOL isSSLTurnedOn = YES;
        NSString *testURLString = @"http://stackoverflow.com";
        NSMutableDictionary *resourceDict = [NSMutableDictionary dictionary];
        [resourceDict setObject:[NSNumber numberWithBool:isFirstTimeRun] forKey:kIsFirstTimeRunKey];
        [resourceDict setObject:[NSNumber numberWithBool:isKeychainTurnedOn] forKey:kIsKeychainTurnedOnKey];
        [resourceDict setObject:[NSNumber numberWithBool:isSSLTurnedOn] forKey:kIsSSLTurnedOnKey];
        [resourceDict setObject:testURLString forKey:kTestURLString];
        [[NSUserDefaults standardUserDefaults] registerDefaults:resourceDict];
    }
}
Alex Reynolds
  • 95,983
  • 54
  • 240
  • 345
  • 1
    what if you have an actual settings.bundle and root.plist resource? – jowie Aug 31 '10 at 14:38
  • 1
    @jowie sadly this is required even though you have a Settings.bundle and Root.plist. The "Default Values" specified there are ignored until the user opens your Settings pane. After a fresh install of your app, before opening the settings pane, all keys will return nil unless you call 'registerDefaults' manually. – gmcnaughton Dec 31 '13 at 17:26