web-dev-qa-db-fra.com

Pouvez-vous définir les paramètres par défaut dans Settings.bundle même si vous n'ouvrez pas l'application Paramètres

J'ai une application iPhone avec un settings.bundle qui gère divers paramètres pour mon application. Je peux définir des valeurs par défaut dans mon fichier root.plist (à l'aide de la propriété DefaultValue), mais celles-ci ne sont utilisées que la première fois que l'utilisateur ouvre l'application de configuration. Existe-t-il un moyen d’écrire ces valeurs lors de l’installation de votre application? Je sais que je peux simplement écrire du code qui vérifie le premier lancement de mon application, puis les écrire, mais ils se trouvent ensuite à deux endroits différents.

Voici une entrée de mon root.plist à titre d'exemple:

<dict>
    <key>Type</key>
    <string>PSToggleSwitchSpecifier</string>
    <key>Title</key>
    <string>Open To Top Location</string>
    <key>Key</key>
    <string>open_top_location</string>
    <key>DefaultValue</key>
    <string>YES</string>
    <key>TrueValue</key>
    <string>YES</string>
    <key>FalseValue</key>
    <string>NO</string>
</dict>

Le résultat final devrait être que si je demande 'open_to_top_location', je reçois un OUI, au lieu qu'il ne soit pas disponible du tout jusqu'à la première fois que l'utilisateur ouvre l'application Paramètres.

Des idées?

55
rustyshelf

Si je vous ai bien compris, vous voulez éviter de spécifier deux fois les valeurs par défaut (une fois en tant que clés "DefaultValue" dans votre fichier Settings.bundle/Root.plist et une fois dans le code d'initialisation de votre application) afin que vous n'ayez pas à les conserver sync.

Comme Settings.bundle est stocké dans l’application elle-même, il vous suffit de lire les valeurs par défaut indiquées ici. J'ai mis en place un exemple de code qui examine l'ensemble de paramètres et lit les valeurs par défaut pour chaque clé. Notez que cela n’écrit pas les clés par défaut; s'ils n'existent pas, vous devrez les lire et les enregistrer à chaque lancement (n'hésitez pas à changer cela). J'ai seulement fait quelques tests superficiels, alors assurez-vous que cela fonctionne pour vous dans tous les cas.

- (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];
}
93
PCheese

Voici mon code basé sur answer de @ PCheese qui ajoute le support pour les clés sans valeur par défaut et les sous-fenêtres enfants.

- (void)registerDefaultsFromSettingsBundle {
    [[NSUserDefaults standardUserDefaults] registerDefaults:[self defaultsFromPlistNamed:@"Root"]];
}

- (NSDictionary *)defaultsFromPlistNamed:(NSString *)plistName {
    NSString *settingsBundle = [[NSBundle mainBundle] pathForResource:@"Settings" ofType:@"bundle"];
    NSAssert(settingsBundle, @"Could not find Settings.bundle while loading defaults.");

    NSString *plistFullName = [NSString stringWithFormat:@"%@.plist", plistName];

    NSDictionary *settings = [NSDictionary dictionaryWithContentsOfFile:[settingsBundle stringByAppendingPathComponent:plistFullName]];
    NSAssert1(settings, @"Could not load plist '%@' while loading defaults.", plistFullName);

    NSArray *preferences = [settings objectForKey:@"PreferenceSpecifiers"];
    NSAssert1(preferences, @"Could not find preferences entry in plist '%@' while loading defaults.", plistFullName);

    NSMutableDictionary *defaults = [NSMutableDictionary dictionary];
    for(NSDictionary *prefSpecification in preferences) {
        NSString *key = [prefSpecification objectForKey:@"Key"];
        id value = [prefSpecification objectForKey:@"DefaultValue"];
        if(key && value) {
            [defaults setObject:value forKey:key];
        } 

        NSString *type = [prefSpecification objectForKey:@"Type"];
        if ([type isEqualToString:@"PSChildPaneSpecifier"]) {
            NSString *file = [prefSpecification objectForKey:@"File"];
            NSAssert1(file, @"Unable to get child plist name from plist '%@'", plistFullName);
            [defaults addEntriesFromDictionary:[self defaultsFromPlistNamed:file]];
        }        
    }

    return defaults;
}
12
Lawrence Johnston

Voici la version de Swift: Appelez-la de:

func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
    // Override point for customization after application launch.
    self.registerDefaultsFromSettingsBundle()

    return true
}

fonction convertie:

func registerDefaultsFromSettingsBundle(){
    //NSLog("Registering default values from Settings.bundle");
    let defs: NSUserDefaults = NSUserDefaults.standardUserDefaults()
    defs.synchronize()

    var settingsBundle: NSString = NSBundle.mainBundle().pathForResource("Settings", ofType: "bundle")!    
    if(settingsBundle.containsString("")){
        NSLog("Could not find Settings.bundle");
        return;
    }
    var settings: NSDictionary = NSDictionary(contentsOfFile: settingsBundle.stringByAppendingPathComponent("Root.plist"))!
    var preferences: NSArray = settings.objectForKey("PreferenceSpecifiers") as NSArray
    var defaultsToRegister: NSMutableDictionary = NSMutableDictionary(capacity: preferences.count)

    for prefSpecification in preferences {
        if (prefSpecification.objectForKey("Key") != nil) {
            let key: NSString = prefSpecification.objectForKey("Key")! as NSString
            if !key.containsString("") {
                let currentObject: AnyObject? = defs.objectForKey(key)
                if currentObject == nil {
                    // not readable: set value from Settings.bundle
                    let objectToSet: AnyObject? = prefSpecification.objectForKey("DefaultValue")
                    defaultsToRegister.setObject(objectToSet!, forKey: key)
                    NSLog("Setting object \(objectToSet) for key \(key)")
                }else{
                    //already readable: don't touch
                    //NSLog("Key \(key) is readable (value: \(currentObject)), nothing written to defaults.");
                }
            }
        }
    }
    defs.registerDefaults(defaultsToRegister)
    defs.synchronize()
}
9
yanko

Swift 3 version

    func registerDefaultsFromSettingsBundle(){
    guard let settingsBundle = Bundle.main.path(forResource: "Settings", ofType: "bundle") else {
        print("Could not locate Settings.bundle")
        return
    }

    guard let settings = NSDictionary(contentsOfFile: settingsBundle+"/Root.plist") else {
        print("Could not read Root.plist")
        return
    }

    let preferences = settings["PreferenceSpecifiers"] as! NSArray
    var defaultsToRegister = [String: AnyObject]()
    for prefSpecification in preferences {
        if let post = prefSpecification as? [String: AnyObject] {
            guard let key = post["Key"] as? String,
                let defaultValue = post["DefaultValue"] else {
                    continue
            }
            defaultsToRegister[key] = defaultValue
        }
    }
    UserDefaults.standard.register(defaults: defaultsToRegister)
}
4
Anders Cedronius

Une version compatible Swift 2

func registerDefaultsFromSettingsBundle(){

    let defaults = NSUserDefaults.standardUserDefaults()
    defaults.synchronize()

    let settingsBundle: NSString = NSBundle.mainBundle().pathForResource("Settings", ofType: "bundle")!
    if(settingsBundle.containsString("")){
        NSLog("Could not find Settings.bundle");
        return;
    }
    let settings = NSDictionary(contentsOfFile: settingsBundle.stringByAppendingPathComponent("Root.plist"))!
    let preferences = settings.objectForKey("PreferenceSpecifiers") as! NSArray;
    var defaultsToRegister = [String: AnyObject](minimumCapacity: preferences.count);

    for prefSpecification in preferences {
        if (prefSpecification.objectForKey("Key") != nil) {
            let key = prefSpecification.objectForKey("Key")! as! String
            if !key.containsString("") {
                let currentObject = defaults.objectForKey(key)
                if currentObject == nil {
                    // not readable: set value from Settings.bundle
                    let objectToSet = prefSpecification.objectForKey("DefaultValue")
                    defaultsToRegister[key] = objectToSet!
                    NSLog("Setting object \(objectToSet) for key \(key)")
                }
            }
        }
    }
    defaults.registerDefaults(defaultsToRegister)
    defaults.synchronize()
}
3
JTango18

Une version beaucoup plus propre de Swift 2.2 nécessite une extension rapide sur une chaîne pour restaurer stringByAppendingPathComponent:

extension String {
    func stringByAppendingPathComponent(path: String) -> String {
        let nsSt = self as NSString
        return nsSt.stringByAppendingPathComponent(path)
    }
}

func registerDefaultsFromSettingsBundle() {
    guard let settingsBundle = NSBundle.mainBundle().pathForResource("Settings", ofType: "bundle") else {
        log.debug("Could not find Settings.bundle")
        return
    }

    let settings = NSDictionary(contentsOfFile: settingsBundle.stringByAppendingPathComponent("Root.plist"))!

    let preferences = settings["PreferenceSpecifiers"] as! NSArray

    var defaultsToRegister = [String: AnyObject]()

    for prefSpecification in preferences {
        guard let key = prefSpecification["Key"] as? String,
        let defaultValue = prefSpecification["DefaultValue"] else {
            continue
        }

        defaultsToRegister[key] = defaultValue
    }
    NSUserDefaults.standardUserDefaults().registerDefaults(defaultsToRegister)
}
0
JuJoDi

Une approche différente: la génération de code

Ce qui suit génère un fichier Objective-C avec une fonction unique qui enregistre les valeurs par défaut pour Root.plist. 

xsltproc settings.xslt Settings.bundle/Root.plist > registerDefaults.m

In peut être exécuté automatiquement en utilisant une phase de construction "Run Script" dans XCode. La phase doit être placée avant "Compiler les sources". (xsltproc est fourni avec OS X.)

"Run Script" screenshot

Ceci est quelque peu basique et ne gère pas les fichiers imbriqués, mais peut-être que quelqu'un en a une utilisation.

settings.xslt

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="text" encoding="UTF-8" omit-xml-declaration="yes" indent="no" />

  <xsl:template match="dict">
    <xsl:choose>
      <xsl:when test="key[.='DefaultValue']/following-sibling::*[position()=1 and self::true]">
        @"YES",
      </xsl:when>
      <xsl:when test="key[.='DefaultValue']/following-sibling::*[position()=1 and self::false]">
        @"NO",
      </xsl:when>
      <xsl:otherwise>
        @"<xsl:value-of select="key[.='DefaultValue']/following-sibling::*[1]"/>",
      </xsl:otherwise>
    </xsl:choose>
    @"<xsl:value-of select="key[.='Key']/following-sibling::*[1]"/>",
  </xsl:template>

  <xsl:template match="/">
    void registerDefaults() {

    NSDictionary *defaults =
    [NSDictionary dictionaryWithObjectsAndKeys:
    <xsl:apply-templates select="descendant::key[.='DefaultValue']/.."/>
    nil];

    [[NSUserDefaults standardUserDefaults] registerDefaults: defaults];
    }
  </xsl:template>

</xsl:stylesheet>

Le est basé sur le travail de Benjamin Ragheb .

0
nschum

Une version de plus du même thème. J'ai gardé le soutien de Lawrence Johnston pour les vitrages enfants et ajouté le support i18n/l10n.

// This code is folklore, first created by an unknown person and copied, pasted
// and published by many different programmers, each (hopefully) of whom added
// some improvemrnts. (c) the People of the Earth
- (NSDictionary *)defaultsFromPlistNamed:(NSString *)plistName {
    NSString *settingsBundlePath = [[NSBundle mainBundle] pathForResource:@"Settings" ofType:@"bundle"];
    if (!settingsBundlePath) {
        NSAssert(settingsBundlePath, @"Could not find Settings.bundle while loading defaults.");
        return nil;
    }

    NSBundle *settingsBundle = [NSBundle bundleWithPath:settingsBundlePath];
    if (!settingsBundlePath) {
        NSAssert(settingsBundle, @"Could not load Settings.bundle while loading defaults.");
        return nil;
    }

    NSString *plistFullName = [settingsBundle pathForResource:plistName ofType:@"plist"];
    if (!plistName) {
        NSAssert1(settings, @"Could not find plist '%@' while loading defaults.", plistFullName);
        return nil;
    }

    NSDictionary *settings_dic = [NSDictionary dictionaryWithContentsOfFile:plistFullName];
    if (!settings_dic) {
        NSAssert1(settings_dic, @"Could not load plist '%@' while loading defaults.", plistFullName);
        return nil;
    }

    NSArray *preferences = [settings_dic objectForKey:@"PreferenceSpecifiers"];
    NSAssert1(preferences, @"Could not find preferences entry in plist '%@' while loading defaults.", plistFullName);

    NSMutableDictionary *defaults = [NSMutableDictionary dictionary];
    for(NSDictionary *prefSpecification in preferences) {
        NSString *key = [prefSpecification objectForKey:@"Key"];
        if (key) {
            id value = [prefSpecification objectForKey:@"DefaultValue"];
            if(value) {
                [defaults setObject:value forKey:key];
                NSLog(@"setting %@ = %@",key,value);
            } 
        }

        NSString *type = [prefSpecification objectForKey:@"Type"];
        if ([type isEqualToString:@"PSChildPaneSpecifier"]) {
            NSString *file = [prefSpecification objectForKey:@"File"];
            NSAssert1(file, @"Unable to get child plist name from plist '%@'", plistFullName);
            if (file) {
                [defaults addEntriesFromDictionary:[self defaultsFromPlistNamed:file]];
            }
        }        
    }

    return defaults;
}

- (void)registerDefaultsFromSettingsBundle {
    [[NSUserDefaults standardUserDefaults] registerDefaults:[self defaultsFromPlistNamed:@"Root"]];
}

Appelez [self registerDefaultsFromSettingsBundle]; à partir de - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

if(x) {NSAssert(x);return nil;} a l'air stupide, mais je me sens paresseux de faire quelque chose à ce sujet.

0