Вы можете сделать настройки в настройках.пакет по умолчанию, даже если вы не открываете приложение Настройки
У меня есть приложение для 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 ответов:
Если я правильно понял вас, вы хотите, чтобы избежать значения по умолчанию, указанные дважды (один раз как "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.)
это несколько простой и не обрабатывает вложенные файлы, но, возможно, кто-то имеет использование для оно.
настройки.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) }