如何从 Objective-c/Swift 中的 settings.bundle 中检索值?

Posted

技术标签:

【中文标题】如何从 Objective-c/Swift 中的 settings.bundle 中检索值?【英文标题】:How to retrieve values from settings.bundle in Objective-c/Swift? 【发布时间】:2011-09-11 14:22:21 【问题描述】:

我创建了一个从 settings.bundle 设置和检索值的项目。我还在 settings.bundle 文件中设置了一些默认值。现在的问题是当我检索值时

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
loginName.text = [defaults objectForKey:@"login_name"];

第一次显示为 null,但值是在 iPhone 应用程序设置中设置的。 如果我更改值或手动设置它,则可以正确检索值。

帮帮我

【问题讨论】:

【参考方案1】:

虽然您定义了默认设置,但它们并没有真正存储为值。它们被存储为默认值。如果您尝试读取它,则该值为空。默认设置与值一样是另一个属性。但这并不意味着将默认值写入默认值。

我要做的是,首先,检查某个设置(我确定应该有一个值)是否存储了任何内容。如果它没有任何东西,那么我写所有的默认值。

这是一个例子。

在 AppDelegate.m 上,我检查 email_notificaciones_preference 是否有值,如果没有,我将所有默认设置写入每个设置。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    NSUserDefaults * standardUserDefaults = [NSUserDefaults standardUserDefaults];
    NSString * email_notificaciones_preference = [standardUserDefaults objectForKey:@"email_notificaciones_preference"];
    if (!email_notificaciones_preference) 
        [self registerDefaultsFromSettingsBundle];
    


这个函数是我用来给每个元素写默认值的。

#pragma NSUserDefaults
- (void)registerDefaultsFromSettingsBundle 
    // this function writes default settings as settings
    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) 
            [defaultsToRegister setObject:[prefSpecification objectForKey:@"DefaultValue"] forKey:key];
            NSLog(@"writing as default %@ to the key %@",[prefSpecification objectForKey:@"DefaultValue"],key);
        
    

    [[NSUserDefaults standardUserDefaults] registerDefaults:defaultsToRegister];


希望对您有所帮助。

【讨论】:

感谢您节省大量时间! 非常好的解决方案 - 我将它翻译成 Swift 并在有人需要时将其添加为答案。 是否也应该添加[[NSUserDefaults standardUserDefaults] synchronize]; 我使用 [[NSUserDefaults standardUserDefaults] 同步] 在检索数据之前而不是之后。【参考方案2】:

如果有人需要 - 我将答案从 MIQUEL 翻译成 Swift(尽我所能):

var standardUserDefaults = NSUserDefaults.standardUserDefaults()
var us: AnyObject? = standardUserDefaults.objectForKey("your_preference")
if us==nil 
    self.registerDefaultsFromSettingsBundle();

还有函数 registerDefaultsFromSettingsBundle:

func registerDefaultsFromSettingsBundle() 
    // this function writes default settings as settings
    var settingsBundle = NSBundle.mainBundle().pathForResource("Settings", ofType: "bundle")
    if settingsBundle == nil 
        NSLog("Could not find Settings.bundle");
        return
    
    var settings = NSDictionary(contentsOfFile:settingsBundle!.stringByAppendingPathComponent("Root.plist"))!
    var preferences: [NSDictionary] = settings.objectForKey("PreferenceSpecifiers") as [NSDictionary];
    var defaultsToRegister = NSMutableDictionary(capacity:(preferences.count));

    for prefSpecification:NSDictionary in preferences 
        var key: NSCopying? = prefSpecification.objectForKey("Key") as NSCopying?
        if key != nil 
            defaultsToRegister.setObject(prefSpecification.objectForKey("DefaultValue")!, forKey: key!)
        
    
    NSUserDefaults.standardUserDefaults().registerDefaults(defaultsToRegister);  

【讨论】:

【参考方案3】:

为 Swift 3 更新:

func registerDefaultsFromSettingsBundle() 

    let userDefaults = UserDefaults.standard

    if let settingsURL = Bundle.main.url(forResource: "Root", withExtension: "plist", subdirectory: "Settings.bundle"),
        let settings = NSDictionary(contentsOf: settingsURL),
        let preferences = settings["PreferenceSpecifiers"] as? [NSDictionary] 

        var defaultsToRegister = [String: AnyObject]()
        for prefSpecification in preferences 
            if let key = prefSpecification["Key"] as? String,
                let value = prefSpecification["DefaultValue"] 

                defaultsToRegister[key] = value as AnyObject
                debugPrint("registerDefaultsFromSettingsBundle: (\(key), \(value)) \(type(of: value))")
            
        

        userDefaults.register(defaults: defaultsToRegister)

     else 
        debugPrint("registerDefaultsFromSettingsBundle: Could not find Settings.bundle")
    

【讨论】:

【参考方案4】:

swift 2.1 的更新版本:

 func registerDefaultsFromSettingsBundle() 
    let userDefaults = NSUserDefaults.standardUserDefaults()

    if let settingsURL = NSBundle.mainBundle().URLForResource("Root", withExtension: "plist", subdirectory: "Settings.bundle"),
           settings = NSDictionary(contentsOfURL: settingsURL),
           preferences = settings["PreferenceSpecifiers"] as? [NSDictionary] 

        var defaultsToRegister = [String: AnyObject]()
        for prefSpecification in preferences 
            if let key = prefSpecification["Key"] as? String,
                   value = prefSpecification["DefaultValue"] 

                defaultsToRegister[key] = value
                NSLog("registerDefaultsFromSettingsBundle: (\(key), \(value)) \(value.dynamicType)")
            
        

        userDefaults.registerDefaults(defaultsToRegister);
     else 
        NSLog("registerDefaultsFromSettingsBundle: Could not find Settings.bundle");
    

【讨论】:

Swift 3 有什么用吗?【参考方案5】:

您可以像这样使用简单的属性包装器:

用法

@SettingsBundleStorage(key: "storageUsage_preference")
var storageUsage: Double

请注意,只需在变量前添加 @objc,即可 100% 兼容 Objective-c。


这背后的代码实现:

设置捆绑包值存在于UserDefaults 中,因此您可以为其使用自定义PropertyWrapper。以下包装器适用于任何UserDefault 值,包括SettingsBundle 的所有值。

属性包装器

@propertyWrapper
public struct SettingsBundleStorage<T>     
    private let key: String

    public init(key: String) 
        self.key = key
        setBundleDefaults(plist: .root) // This is the main plist
        setBundleDefaults(plist: .child(name: "DeveloperOptions")) // This is an example child.
    

    public var wrappedValue: T 
        get  UserDefaults.standard.value(forKey: key) as! T 
        set  UserDefaults.standard.set(newValue, forKey: key) 
    

根和孩子

您应该为根和子plists 传递以下枚举:

extension SettingsBundleStorage 

    enum PList 
        case root
        case child(name: String)

        var name: String 
            var file: String
            switch self 
            case .root: file = "Root"
            case .child(let name): file = name.replacingOccurrences(of: ".plist", with: "")
            
            file.append(".plist")
            return file
        
    

如果需要,查找并设置默认值。

此包装器使用此函数查找捆绑键的默认值:

extension SettingsBundleStorage 

    func setBundleDefaults(plist: PList = .root) 
        let settingsName                    = "Settings"
        let settingsExtension               = "bundle"
        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(plist.name)),
              let settingsPlist = try? PropertyListSerialization.propertyList(
                from: settingsData,
                options: [],
                format: nil) as? [String: Any],
              let settingsPreferences = settingsPlist?[settingsPreferencesItems] as? [[String: Any]] else 
            return assertionFailure("Can not get the \(plist.name) from the bundle: \(settingsName)")
        

        var defaultsToRegister = [String: Any]()

        settingsPreferences.forEach  preference in
            if let key = preference[settingsPreferenceKey] as? String 
                defaultsToRegister[key] = preference[settingsPreferenceDefaultValue]
            
        

        UserDefaults.standard.register(defaults: defaultsToRegister)
    

此包装器可以将任何类型的可编码存储到/从用户默认值中存储/恢复,包括已经符合可编码的所有 Swift 标准数据类型。


此外,您可以找到一个类似但代码更少的版本,用于从任何用户默认访问任何键值,您可以查看this answer here

【讨论】:

这真是一个绝妙的答案!这应该是从现在开始使用属性包装器的方式【参考方案6】:

试试这个

注册设置包的默认值

NSDictionary *appDefaults = [NSDictionary dictionaryWithObject:defaultValue forKey:@"key"];
 [[NSUserDefaults standardUserDefaults] registerDefaults:appDefaults];

在检索设置包值之前同步数据

[[NSUserDefaults standardUserDefaults] synchronize]

【讨论】:

以上是关于如何从 Objective-c/Swift 中的 settings.bundle 中检索值?的主要内容,如果未能解决你的问题,请参考以下文章

Objective-C:Swift Package 函数中的多参数方法语法

混合objective-c/swift项目中的故事板颜色问题

『干货』分享你最喜欢的技巧和提示(Xcode,objective-c,swift,c...等等)

Objective-C/Swift (iOS) 啥时候在 View/ViewController 工作流程中应用自动约束?

Using Swift with Cocoa and Objective-C(Swift 2.0版):开始--基础设置-备

Objective-C Swift 桥接头