Write into Settings Bundle in Swift

Simple title output in ios settings bundle

Ok, I had this problem too. The solution (sort of) was to provide a default value field and give that a value. This is actually explicitly stated in the documentation -- Default Value is a required field for the Title property, so if you don't specify it the title won't show in the settings pane. Unfortunately, I can't seem to CHANGE the value once it's set, possibly also as designed -- the documentation also states that it's a read only property. The solution I'm going to try is to just explicitly put the version number in my Root.plist file each time I make a new build. SUPER not ideal, but will work, I think.

EDIT: Check out this post on updating version number in settings bundle

EDIT: Ok, I got this working (thanks to that post, above, and a little hacking around with bash scripts, which I had very little experience with.) Here's the script (which I just wrote inline in a 'Run Script' build phase):

#!/bin/bash

builtInfoPlistPath=${TARGET_BUILD_DIR}/${INFOPLIST_PATH}

#increment the build number
buildNumber=$(/usr/libexec/PlistBuddy -c "Print :CFBundleVersion" "$builtInfoPlistPath")
buildNumber=$(($buildNumber + 1))
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildNumber" "$builtInfoPlistPath"

#compose the version number string
versionString=$(/usr/libexec/PlistBuddy -c "Print :CFBundleShortVersionString" "$builtInfoPlistPath")
versionString+=" ("
versionString+=$(/usr/libexec/PlistBuddy -c "Print :CFBundleVersion" "$builtInfoPlistPath")
versionString+=")"

#write the version number string to the settings bundle
#IMPORTANT: this assumes the version number is the first property in the settings bundle!
/usr/libexec/PlistBuddy -c "Set :PreferenceSpecifiers:0:DefaultValue $versionString" "Settings.bundle/Root.plist"

... and that's it! Works like a charm! Hope that helps with your problem, because it solved mine. Only problem now is a slight discrepancy with the build number ...

EDIT: ... which I fixed with vakio's second comment on this post, which instead sets the path to the info.plist to the one which is already processed (before the Run Script phase!)

EDIT: Here's my more up-to-date version, which is in an external file and verifies that some source files have changed before incrementing the build number:

 #!/bin/bash

#note: for simplicity, it's assumed that there's already a bundle version (which is an integer) and a version string. set them in the Summary pane!

#get path to the BUILT .plist, NOT the packaged one! this fixes the off-by-one bug
builtInfoPlistPath=${TARGET_BUILD_DIR}/${INFOPLIST_PATH}
echo "using plist at $builtInfoPlistPath"

modifiedFilesExist=false
#this is the modification date to compare to -- there's a possible bug here, if you edit the built plist directly, for some reason. probably you shouldn't do that anyways.
compModDate=$(stat -f "%m" "$builtInfoPlistPath")

for filename in *
do
modDate=$(stat -f "%m" "$filename")
if [ "$modDate" -gt "$compModDate" ]
then
modifiedFilesExist=true;
echo "found newly modified file: $filename"
break
fi
done

if $modifiedFilesExist
then
echo "A file is new, bumping version"

#increment the build number
buildNumber=$(/usr/libexec/PlistBuddy -c "Print :CFBundleVersion" "$builtInfoPlistPath")
echo "retrieved current build number: $buildNumber"
buildNumber=$(($buildNumber + 1))
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildNumber" "$builtInfoPlistPath"
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $buildNumber" "$INFOPLIST_FILE"

#compose the version number string
versionString=$(/usr/libexec/PlistBuddy -c "Print :CFBundleShortVersionString" "$builtInfoPlistPath")
versionString+=" ("
versionString+=$(/usr/libexec/PlistBuddy -c "Print :CFBundleVersion" "$builtInfoPlistPath")
versionString+=")"

#write the version number string to the settings bundle
#IMPORTANT: this assumes the version number is the second property in the settings bundle!
/usr/libexec/PlistBuddy -c "Set :PreferenceSpecifiers:1:DefaultValue $versionString" "Settings.bundle/Root.plist"
else
echo "Version not incremented -- no newly modified files"
fi

Can you make the settings in Settings.bundle default even if you don't open the Settings App

If I understood you correctly, you want to avoid having default values specified twice (once as "DefaultValue" keys in your Settings.bundle/Root.plist file, and once in your app initialization code) so you do not have to keep them in sync.

Since Settings.bundle is stored within the app bundle itself, you can just read the default values given there. I put together some sample code that looks at the Settings bundle and reads the default values for every key there. Note that this does not write out the default keys; if they don't exist, you'll need to read and register them at every launch (feel free to change this). I've only done some cursory tests, so make sure it works for you in all cases.

- (void)applicationDidFinishLaunching:(UIApplication *)application {    
NSString *name = [[NSUserDefaults standardUserDefaults] stringForKey:@"name"];
NSLog(@"name before is %@", name);

// Note: this will not work for boolean values as noted by bpapa below.
// If you use booleans, you should use objectForKey above and check for null
if(!name) {
[self registerDefaultsFromSettingsBundle];
name = [[NSUserDefaults standardUserDefaults] stringForKey:@"name"];
}
NSLog(@"name after is %@", name);
}

- (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 && [[prefSpecification allKeys] containsObject:@"DefaultValue"]) {
[defaultsToRegister setObject:[prefSpecification objectForKey:@"DefaultValue"] forKey:key];
}
}

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

iphone - how to add normal text to settings bundle

You can add text at the bottom of your Groups, that is commonly used as a hint about the preferences in that group.

  1. Add a new item to a Group (Yes!, additionally to the default Title and Type Keys).
  2. Replace the text under the Key column that reads "New Item" for FooterText.
  3. Under the Type column set the type for you new Key as String.
  4. Write whatever you want on the field under the Value column for the new Key.

And... voilà!

Next time you build and launch the App go to Settings and to your App's preferences to see the text under the Group you placed it.

Settings.bundle Swift 5

Add this to the ViewController:

        NotificationCenter.default.addObserver(self, selector: #selector(settingChanged(notification:)), name: UserDefaults.didChangeNotification, object: nil)

}

Then create an @objc func with the following:

    @objc func settingChanged(notification: NSNotification) {
let userDefaults = UserDefaults.standard

let btn1settings = userDefaults.bool(forKey: "btn1_onoff")

if !btn1settings {
btn1.isHidden = true
btn1_label.isHidden = true
btn1_count.isHidden = true


}
else {
btn1.isHidden = false
btn1_label.isHidden = false
btn1_count.isHidden = false
}
let btn1name = userDefaults.string(forKey: "btn1_name")
btn1_label.text = btn1name

This will update the app when the settings are updated in the "App Settings"

Defining Settings bundle based on the build configuration

I have worked it out this way:

1) Add 2 settings bundle (one for test and one for prod) to the project and add them to the target.

2) Add a Run script on the Runner target to selectively copy the required Settings bundle into the build file.

if [IS_PRODUCTION]; then
cp -r ${PROJECT_DIR}/Settings/Prod/Settings.bundle ${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app
fi

if [IS_TEST]; then
cp -r ${PROJECT_DIR}/Settings/Test/Settings.bundle ${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.app
fi

3) Once done, just run on different schemes to see the desired results.

How to retrieve values from settings.bundle in Objective-c/Swift?

Although you define the defaults settings, they are not really stored as a value. They are stored as default. If you try to read it, the value is null. Default setting is another property as value is. But it doesnt mean that will write the default value as a default.

What I do is, first, check if some setting,(that I'm sure that should have a value) has anything stored on it. If it doesn't have anything then I write all the defaults.

Here is an example.

on AppDelegate.m I check if email_notificaciones_preference has a value, if not, I write ALL default settings to each setting.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
NSUserDefaults * standardUserDefaults = [NSUserDefaults standardUserDefaults];
NSString * email_notificaciones_preference = [standardUserDefaults objectForKey:@"email_notificaciones_preference"];
if (!email_notificaciones_preference) {
[self registerDefaultsFromSettingsBundle];
}

}

This function is what I use to write defaults to each element.

#pragma NSUserDefaults
- (void)registerDefaultsFromSettingsBundle {
// this function writes default settings as settings
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];
NSLog(@"writing as default %@ to the key %@",[prefSpecification objectForKey:@"DefaultValue"],key);
}
}

[[NSUserDefaults standardUserDefaults] registerDefaults:defaultsToRegister];

}

Hope that helps.



Related Topics



Leave a reply



Submit