Вы можете сделать настройки в настройках.пакет по умолчанию, даже если вы не открываете приложение Настройки


У меня есть приложение для iPhone с настройками.пакет, который обрабатывает различные настройки для моего приложения. Я могу установить значения по умолчанию в моем корне.plist-файл (с использованием свойства DefaultValue), но они используются только при первом открытии пользователем приложения "Настройки". Есть ли способ получить эти значения, записанные при установке приложения? Я знаю, что могу просто написать код, который проверяет первый запуск моего приложения, а затем записать их, но тогда они находятся в двух разных места.

вот запись из моего корня.plist в качестве примера:

<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>

конечным результатом должно быть то, что если я попрошу "open_to_top_location", я получу да, а не его вообще не будет, пока пользователь не откроет приложение настроек.

какие идеи?

8 55

8 ответов:

Если я правильно понял вас, вы хотите, чтобы избежать значения по умолчанию, указанные дважды (один раз как "DefaultValue" ключи в настройках.связка / корень.plist-файл и один раз в коде инициализации приложения), поэтому вам не нужно синхронизировать их.

С Настройки.пакет хранится в самом пакете приложения, вы можете просто прочитать значения по умолчанию, приведенные там. Я собрал некоторый пример кода, который смотрит на пакет настроек и читает значения по умолчанию для каждого ключа там. Обратите внимание, что это не записывает ключи по умолчанию; если они не существуют, вам нужно будет читать и регистрировать их при каждом запуске (не стесняйтесь изменять это). Я только сделал несколько беглых тестов, чтобы убедиться, что он работает для вас во всех случаях.

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

вот мой код, основанный на @PCheese ответ, который добавляет поддержку для ключей без значений по умолчанию " и " детьми.

- (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;
}

вот версия Swift: звоните из:

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

    return true
}

преобразованной функции:

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()
}

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)
}

A 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()
}

еще одна версия той же темы. Я сохранил поддержку Лоуренса Джонстона для детских панелей и добавил поддержку 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"]];
}

вызов [self registerDefaultsFromSettingsBundle]; С - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

if(x) {NSAssert(x);return nil;} выглядит глупо, но мне лень что-то с этим делать.

другой подход: генерация кода

далее создается файл Objective-C с одной функцией, которая регистрирует значения по умолчанию для Root.файл plist.

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

In можно запустить автоматически, используя этап сборки "Run Script" в XCode. Этап должен быть помещен перед "компиляцией источников". (xsltproc поставляется с OS X.)

"Run Script" screenshot

это несколько простой и не обрабатывает вложенные файлы, но, возможно, кто-то имеет использование для оно.

настройки.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>

Он основан на работе Бенджамин Рагебе.

гораздо более чистая версия swift 2.2, требует быстрого расширения на строку для восстановления 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)
}