【问题标题】:How can I store a Dictionary with RealmSwift?如何使用 RealmSwift 存储字典?
【发布时间】:2016-02-21 03:18:57
【问题描述】:

考虑以下模型:

class Person: Object {
    dynamic var name = ""
    let hobbies = Dictionary<String, String>()
}

我正在尝试在 Realm 中存储一个 [String:String] 类型的对象,我从 Alamofire 请求中获得,但由于 hobbies 必须通过 let 定义,所以不能到 RealmSwift 文档,因为它是一种 List&lt;T&gt;/Dictionary&lt;T,U&gt; 类型。

let hobbiesToStore: [String:String]
// populate hobbiestoStore
let person = Person()
person.hobbies = hobbiesToStore

我也尝试重新定义init(),但总是以致命错误或其他错误告终。

如何在 RealSwift 中简单地复制或初始化字典? 我在这里错过了一些琐碎的事情吗?

【问题讨论】:

    标签: ios swift dictionary realm


    【解决方案1】:

    Dictionary 不支持作为 Realm 中的属性类型。 您需要引入一个新类,其对象描述每个键值对和一对多关系,如下所示:

    class Person: Object {
        dynamic var name = ""
        let hobbies = List<Hobby>()
    }
    
    class Hobby: Object {
        dynamic var name = ""
        dynamic var descriptionText = ""
    }
    

    对于反序列化,您需要将 JSON 中的字典结构映射到 Hobby 对象,并将键和值分配给适当的属性。

    【讨论】:

    • 谢谢!我也想过这个解决方案(因为它是最干净的解决方案),但不能在 RealmSwift 中使用任何 Swift 结构真的很令人沮丧......(甚至不是元组:()。因为我的数据真的是静态的,而且很简单,我最终将两个字符串与一个分隔符合并在一起,并创建了一个 List&lt;String&gt;
    • 有一些限制使我们无法支持任何通用的 Swift 结构,尤其是元组。其中包括我们必须能够在运行时确定类型并能够通过动态访问器返回值。这不适用于元组。
    • 2021 年更新:现在支持 Map 类型。请参阅下面的my answer
    【解决方案2】:

    也许效率有点低,但对我有用(来自 Int->String 的示例字典,与您的示例类似):

    class DictObj: Object {
       var dict : [Int:String] {
          get {
             if _keys.isEmpty {return [:]} // Empty dict = default; change to other if desired
             else {
                var ret : [Int:String] = [:];
                Array(0..<(_keys.count)).map{ ret[_keys[$0].val] = _values[$0].val };
                return ret;
             }
          }
          set {
             _keys.removeAll()
             _values.removeAll()
             _keys.appendContentsOf(newValue.keys.map({ IntObj(value: [$0]) }))
             _values.appendContentsOf(newValue.values.map({ StringObj(value: [$0]) }))
          }
       }
       var _keys = List<IntObj>();
       var _values = List<StringObj>();
    
       override static func ignoredProperties() -> [String] {
          return ["dict"];
       }
    }
    

    Realm 无法存储 Strings/Ints 列表,因为它们不是对象,所以制作“假对象”:

    class IntObj: Object {
       dynamic var val : Int = 0;
    }
    
    class StringObj: Object {
       dynamic var val : String = "";
    }
    

    受另一个关于堆栈溢出的答案的启发,类似地存储数组(帖子目前正在躲避我)...

    【讨论】:

    • 如果你有一个带有 time Int 或 Double 等的值,你会怎么做?最好的解决方案是使用 Data 对象和 JSONSerialization。
    【解决方案3】:

    我目前正在模拟这个,方法是在我的模型上公开一个被忽略的 Dictionary 属性,由一个私有的、持久的 NSData 支持,该 NSData 封装了字典的 JSON 表示:

    class Model: Object {
        private dynamic var dictionaryData: NSData?
        var dictionary: [String: String] {
            get {
                guard let dictionaryData = dictionaryData else {
                    return [String: String]()
                }
                do {
                    let dict = try NSJSONSerialization.JSONObjectWithData(dictionaryData, options: []) as? [String: String]
                    return dict!
                } catch {
                    return [String: String]()
                }
            }
    
            set {
                do {
                    let data = try NSJSONSerialization.dataWithJSONObject(newValue, options: [])
                    dictionaryData = data
                } catch {
                    dictionaryData = nil
                }
            }
        }
    
        override static func ignoredProperties() -> [String] {
            return ["dictionary"]
        }
    }
    

    这可能不是最有效的方法,但它让我可以继续使用Unbox 快速轻松地将传入的 JSON 数据映射到我的本地 Realm 模型。

    【讨论】:

    • 请注意额外的 JSON(反)序列化对性能的影响,并且您失去了以这种方式查询字典的能力。
    • 嗨@mar​​ius,当然。这是一种解决方法,正如我所说,这不是最有效的方法,但它适用于我需要在我的领域有字典引用的情况(我真的不需要查询)。希望我们能在某个时候看到对字典的原生支持,在这种情况下就不再需要它了。
    【解决方案4】:

    我会将字典保存为 Realm 中的 JSON 字符串。然后检索 JSON 并转换为字典。使用以下扩展。

    extension String{
    func dictionaryValue() -> [String: AnyObject]
    {
        if let data = self.data(using: String.Encoding.utf8) {
            do {
                let json = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments) as? [String: AnyObject]
                return json!
    
            } catch {
                print("Error converting to JSON")
            }
        }
        return NSDictionary() as! [String : AnyObject]
    } }
    

    extension NSDictionary{
        func JsonString() -> String
        {
            do{
            let jsonData: Data = try JSONSerialization.data(withJSONObject: self, options: .prettyPrinted)
            return String.init(data: jsonData, encoding: .utf8)!
            }
            catch
            {
                return "error converting"
            }
        }
    }
    

    【讨论】:

    • 很好的解决方案!
    • 这是最简单的解决方案,使在 Realm 中存储字典变得容易。
    • 也很慢,会使用keyarchiver
    【解决方案5】:

    2021 年更新

    从 Realm 10.8.0 开始,可以使用 Map 类型将字典存储在 Realm 对象中。

    来自official documentation的示例:

    class Dog: Object {
        @objc dynamic var name = ""
        @objc dynamic var currentCity = ""
        // Map of city name -> favorite park in that city
        let favoriteParksByCity = Map<String, String>()
    }
    

    【讨论】:

    • 如何解码/编码地图?我写 Map 时出错:Type 'QuestionList' does not conform to protocol 'Decodable' || Type 'QuestionList' does not conform to protocol 'Encodable'
    猜你喜欢
    • 1970-01-01
    • 2017-09-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-23
    • 2017-07-29
    • 2020-07-28
    • 2017-01-19
    相关资源
    最近更新 更多