Você pode fazer as configurações no padrão Settings.bundle mesmo que você não abra o aplicativo Configurações

Eu tenho um aplicativo para iPhone com um settings.bundle que lida com várias configurações para o meu aplicativo. Posso definir valores padrão no meu arquivo root.plist (usando a propriedade DefaultValue), mas eles só são usados ​​na primeira vez em que o usuário abre o aplicativo de configurações. Existe alguma maneira de obter esses valores escritos quando o aplicativo é instalado? Eu sei que posso escrever código que verifique a primeira boot do meu aplicativo e depois escrevê-los, mas eles estão em dois lugares diferentes.

Aqui está uma input do meu root.plist como um exemplo:

 Type PSToggleSwitchSpecifier Title Open To Top Location Key open_top_location DefaultValue YES TrueValue YES FalseValue NO  

O resultado final deve ser que, se eu pedir ‘open_to_top_location’, recebo um YES, em vez de ele não estar lá até a primeira vez em que o usuário abrir o aplicativo Settings.

Alguma ideia?

Se eu entendi corretamente, você quer evitar ter valores padrão especificados duas vezes (uma vez como “DefaultValue” chaves em seu arquivo Settings.bundle / Root.plist, e uma vez em seu código de boot do aplicativo) para que você não tem que mantê-los em sincronizar.

Como o arquivo Settings.bundle é armazenado no próprio pacote de aplicativos, você pode apenas ler os valores padrão fornecidos. Eu reuni um código de exemplo que analisa o pacote Configurações e lê os valores padrão para cada chave lá. Observe que isso não grava as chaves padrão; se eles não existirem, você precisará lê-los e registrá-los a cada lançamento (sinta-se à vontade para mudar isso). Eu só fiz alguns testes superficiais, então tenha certeza que funciona para você em todos os casos.

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

Aqui está o meu código baseado na resposta do @Pheese que adiciona suporte a chaves sem um valor padrão e painéis filhos.

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

Aqui está a versão Swift: chame de:

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

function convertida:

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

Versão Swift 3

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

Uma versão compatível com o 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() } 

Mais uma versão do mesmo tema. Eu mantive o apoio de Lawrence Johnston para painéis de criança e adicionei o suporte a 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"]]; } 

Ligue para [self registerDefaultsFromSettingsBundle]; from - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

if(x) {NSAssert(x);return nil;} parece estúpido, mas eu tenho preguiça de fazer algo sobre isso.

Uma abordagem diferente: geração de código

O seguinte gera um arquivo Objective-C com uma única function que registra os padrões para Root.plist.

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

In pode ser executado automaticamente usando uma fase de compilation “Run Script” no XCode. A fase deve ser colocada antes de “Compilar Fontes”. (xsltproc vem com o OS X.)

Isso é um pouco básico e não manipula arquivos nesteds, mas talvez alguém tenha um uso para isso.

settings.xslt

      @"YES",   @"NO",   @"",   @"",   void registerDefaults() { NSDictionary *defaults = [NSDictionary dictionaryWithObjectsAndKeys:  nil]; [[NSUserDefaults standardUserDefaults] registerDefaults: defaults]; }   

O baseia-se no trabalho de Benjamin Ragheb .

Uma versão 2.2 muito mais limpa e rápida, requer uma extensão rápida na string para restaurar 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) }