Swift 4 Settings Bundle, get defaults
Here's an answer based on @Kamil (many thanks for that) that doesn't rely on NSDictionary
and uses PropertyListSerialization
.
Swift 5
func registerDefaultsFromSettingsBundle() {
let settingsName = "Settings"
let settingsExtension = "bundle"
let settingsRootPlist = "Root.plist"
let settingsPreferencesItems = "PreferenceSpecifiers"
let settingsPreferenceKey = "Key"
let settingsPreferenceDefaultValue = "DefaultValue"
guard let settingsBundleURL = Bundle.main.url(forResource: settingsName, withExtension: settingsExtension),
let settingsData = try? Data(contentsOf: settingsBundleURL.appendingPathComponent(settingsRootPlist)),
let settingsPlist = try? PropertyListSerialization.propertyList(
from: settingsData,
options: [],
format: nil) as? [String: Any],
let settingsPreferences = settingsPlist[settingsPreferencesItems] as? [[String: Any]] else {
return
}
var defaultsToRegister = [String: Any]()
settingsPreferences.forEach { preference in
if let key = preference[settingsPreferenceKey] as? String {
defaultsToRegister[key] = preference[settingsPreferenceDefaultValue]
}
}
UserDefaults.standard.register(defaults: defaultsToRegister)
}
So for a Root.plist
like this:
... the defaultsToRegister
would be:
There's also the new compactMapValues()
API in Swift 5 that may or may not be helpful here.
this does not get me the default values (which are all true, but they all return false)
Looks like you have a toggle switch which displays as ON in the setting bundle and when you read bundle you get all the false value.
If this is the case then you are missing something here.
In setting bundle(Root.plist) we have "Default Value" field which is nothing to do with the actual default value of toggle switch. This is just a visual indicator to switch. You may have "Default value" set as "YES" in plist but when you try to read the value you will end up getting false.
Here I have set Default Value for Reminder in Root.plist as YES and for Update NO So that when app launch it shows as above.
But when I tried to read these defaults - it gives both as false.
func getDefaults() {
let stanDefaults = UserDefaults.standard
print("Default value of Update - \(stanDefaults.bool(forKey: "update_lot_pref"))")
print("\nDefault value of Reminder - \(stanDefaults.bool(forKey: "reminder_pref"))")
}
Default value of Update - false Default value of Reminder - false
Now, if you want to sync these values - default value in Root.plist and value of default - then you have to set it programmatically.
func setApplicationDefault() {
let stanDefaults = UserDefaults.standard
let appDefaults = ["reminder_pref": true]
stanDefaults.register(defaults: appDefaults)
stanDefaults.synchronize()
}
Here in my Root.plist I have default value as YES and also when viewDidload I set this preference value as true. When I run it gives me
Default value of Reminder - true
And this is how my Root.plist looks.
Hope it helps.
For the sake of the demonstration let's assume that you have two switches in the settings bundle. One with the default value set to YES
and one with the default value set to NO
.
If you want to be able to access default values defined in the Settings.bundle
from the UserDefaults
in your app you have to register them first. Unfortunately, iOS won't do it for you and you have to take care of it by yourself.
The following method scans the Root.plist
associated with the Settings.bundle
and registers the default values for the identifiers of your preferences.
func registerDefaultsFromSettingsBundle()
{
let settingsUrl = Bundle.main.url(forResource: "Settings", withExtension: "bundle")!.appendingPathComponent("Root.plist")
let settingsPlist = NSDictionary(contentsOf:settingsUrl)!
let preferences = settingsPlist["PreferenceSpecifiers"] as! [NSDictionary]
var defaultsToRegister = Dictionary<String, Any>()
for preference in preferences {
guard let key = preference["Key"] as? String else {
NSLog("Key not found")
continue
}
defaultsToRegister[key] = preference["DefaultValue"]
}
UserDefaults.standard.register(defaults: defaultsToRegister)
}
I recommend running it as early as possible. You will be sure that the defaults are there for all parts of your app.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool
{
registerDefaultsFromSettingsBundle()
let one = UserDefaults.standard.bool(forKey: "switch_one")
let two = UserDefaults.standard.bool(forKey: "switch_two")
NSLog("One: \(one), Two: \(two)")
return true
}