在Swift中将字典映射到结构数组 [英] Mapping a dictionary to an array of structs in Swift

查看:112
本文介绍了在Swift中将字典映射到结构数组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的应用需要代表一组预设,其中预设由以下结构表示:

My app needs to represent an array of presets, where a preset is represented by the following struct:

struct Preset: Codable {
    var name: String
    var value: Int
}

使用NSUserDefaultsControllerNSTableView和CocoaBindings,我可以创建一个首选项"窗口,可以在其中添加和删除预设以及对其进行编辑.它们被持久保存到UserDefaults plist中,如下所示:

Using NSUserDefaultsController, NSTableView and CocoaBindings, I was able to create a Preferences window where I can add and remove presets, as well as edit them. They are persisted to the UserDefaults plist as follows:

<plist version="1.0">
<dict>
    <key>presets</key>
    <array>
        <dict>
            <key>name</key>
            <string>A preset</string>
            <key>value</key>
            <integer>1</integer>
        </dict>
        <dict>
            <key>name</key>
            <string>Another preset</string>
            <key>value</key>
            <integer>2</integer>
        </dict>
    </array>
</dict>
</plist>

我正在寻找使用自然符号在代码中访问此数据的方法.基本上,我想让设置"单例具有这样的计算属性:

I'm looking to access this data in my code using a natural notation. Basically, I'd like to have a Settings singleton with a computed property like this:

class Settings: NSObject {
    static let sharedInstance = Settings()
    private let presetsKey = "presets"

    override private init() {
        UserDefaults.standard.register(defaults: [
            presetsKey: [ ["name": "Default preset", "value": 0] ],
        ])

        super.init()
    }

    var presets: [Preset] {
        get {
            // Missing code goes here
        }
    }
}

getter中的代码应执行从preset UserDefaults数组到[Preset]的映射,以代替// Missing code goes here注释.这是我要使用的表示法的示例:

The code in the getter should perform the mapping from the preset UserDefaults array to [Preset], in place of the // Missing code goes here comment. Here's an example of the notation I'd like to use:

let firstPresetName = Settings.sharedValue.preset[0].name
let firstPresetValue = Settings.sharedValue.preset[0].value
print("The first preset's name is \(firstPresetName\) and its value is \(firstPresetValue\)")

我写了这个,它有效:

let presets = UserDefaults.standard.array(forKey: presetsKey) as! [[String: Any]]
var result = [Preset]()
for preset in presets {
    result.append(ControlParameters(name:preset["name"] as! String, value:preset["value"] as! Int))
}
return result

但是我对这种解决方案不满意.

Yet I'm not happy with this solution. Is there a more compact and generic solution (which works for any struct in a similar context, without having to hardcode the struct property names such as name and value)?

推荐答案

允许要点我通过一些修改解决了这个问题:

This gist allowed me solve the problem, with a few modifications:

extension UserDefaults {
    func encode<T: Encodable>(_ value: T?, forKey key: String) throws {
        switch T.self {
        case is Float.Type, is Double.Type, is String.Type, is Int.Type, is Bool.Type:
            set(value!, forKey: key)
        default:
            let data = try value.map(PropertyListEncoder().encode)
            let any = data.map { try! PropertyListSerialization.propertyList(from: $0, options: [], format: nil) }

            set(any, forKey: key)
        }
    }

    func decode<T: Decodable>(_ type: T.Type, forKey key: String) throws -> T? {
        switch T.self {
        case is Float.Type, is Double.Type, is String.Type, is Int.Type, is Bool.Type:
            return (value(forKey: key) as! T)
        default:
            let any = object(forKey: key)
            let data = any.map { try! PropertyListSerialization.data(fromPropertyList: $0, format: .binary, options: 0) }

            return try data.map { try PropertyListDecoder().decode(type, from: $0) }
        }
    }
}

以此,我可以定义以下通用函数:

Using this, I can define the following generic functions:

private func getter<T: Codable>(key: String) -> T {
    let result = try! UserDefaults.standard.decode(T.self, forKey: key)
    return result!
}

private func setter<T: Codable>(_ newValue: T, forKey key: String) {
    try! UserDefaults.standard.encode(newValue, forKey: key)
}

现在我可以为我的所有属性实现计算的getter和setter方法:

Now I can implement computed getters and setters for all of my properties as such:

var presets: [Preset] {
    get {
       return getter(presetsKey)
    }

    set {
       setter(newValue, presetsKey)
    }
}

它也适用于非数组类型.

It also works for non-array types.

这篇关于在Swift中将字典映射到结构数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆