如何在 Codable 中使用 List 类型? (RealmSwift)
Posted
技术标签:
【中文标题】如何在 Codable 中使用 List 类型? (RealmSwift)【英文标题】:How to use List type with Codable? (RealmSwift) 【发布时间】:2017-08-02 06:17:02 【问题描述】:问题是List类型不符合Codable,下面的类不能插入Realm。
例如,
class Book: Codable
var name: String = ""
var author: String = ""
var tags = [String]()
考虑到上面的类符合Codable,如果将这个类存储到Realm,需要使用List<Object>
类型而不是[String]
class Book: Object, Codable
@objc dynamic var name: String = ""
@objc dynamic var author: String = ""
var tags = List<Tag>()
required convenience init(from decoder: Decoder) throws
self.init()
let container = try decoder.container(keyedBy: CodingKeys.self)
name = try container.decode(String.self, forKey: .name)
author = try container.decode(String.self, forKey: .author)
tags = try container.decode(List<Tag>.self, forKey: .tags) // this is problem.
class Tag: Object, Codable
@objc dynamic var string: String = ""
required convenience init(from decoder: Decoder) throws
self.init()
let container = try decoder.container(keyedBy: CodingKeys.self)
string = try container.decode(String.self, forKey: .string)
为了符合 Codable,它应该实现Decodable
协议。 (required convenience init(from decoder: Decoder) throws
)
但是,List
类型不符合Codable
(Decodable
),如果类有List
类型,则无法使用 Codable。
如何解决这个问题?
谢谢,
【问题讨论】:
为 List 写一个扩展,使其符合Codable
。
【参考方案1】:
我们可以使用扩展使 List 符合 Codable:
extension List : Decodable where Element : Decodable
public convenience init(from decoder: Decoder) throws
self.init()
var container = try decoder.unkeyedContainer()
while !container.isAtEnd
let element = try container.decode(Element.self)
self.append(element)
extension List : Encodable where Element : Encodable
public func encode(to encoder: Encoder) throws
var container = encoder.unkeyedContainer()
for element in self
try element.encode(to: container.superEncoder())
如果其他人需要,我还获得了 RealmOptional 的扩展。
https://gist.github.com/ansonyao/41137bb3cbbca8ef31a13b6bc96ee422
【讨论】:
【参考方案2】:你快到了。在初始化器中,您可以使用解码后的数组初始化列表。基本上,改变
tags = try container.decode(List<Tag>.self, forKey: .tags) // this is problem.
到
let tagsArray = try container.decode([Tag].self, forKey: .tags)
tags = List(tagsArray) // Now you are good
正如 cmets 中指出的那样,List
构造函数不再像这样工作
你现在想要:
tags.append(objectsIn: tagsArray)
【讨论】:
Realm 3.13.1 中似乎不存在此构造函数【参考方案3】:接受的答案只有一个问题。 Realm 指定列表应为 let
(Constants) 因此,要修改解决方案以遵循最佳实践,您只需将列表设为 let
,然后循环将结果附加到您的数组。
// Change this Line in [Your Code]
// to a let (Constant)
var tags = List<Tag>()
到 let tags = List<Tag>()
然后改变
tags = try container.decode(List<Tag>.self, forKey: .tags)
到
let tagsArray = try container.decode([Tag].self, forKey: .tags)
tagsArray.forEach tags.append($0)
【讨论】:
不错的答案,但有一个问题:如果我们将标签附加到此处的列表中,如果对象以某种方式被重用,是否有可能保留旧标签? 我不认为有这样的机会,因为这只在init
上运行。 tagsArray
被实例化为一个新的空 Array
现在如果你有另一个方法可以让你附加到这个 tagsArray
你将拥有所有现有的标签,如果你这样做了,你可能想要一个方法这也可以让你删除标签。但我认为一般来说你应该是安全的
应该使用tags.append(objectsIn: tagsArray)
而不是tagsArray.forEach...
【参考方案4】:
这就是您创建 Realm / Swift 4 Codable 模型的方式。
有关 JSON 响应、模型和 URLSession 的更多详细信息:Read this article
import RealmSwift
class VendorsList : Object, Decodable
@objc dynamic var id : Int = 0
@objc dynamic var name : String?
@objc dynamic var logo : String?
// Create your Realm List.
var kitchensList = List<VendorKitchens>()
override class func primaryKey() -> String?
return "id"
private enum CodingKeys: String, CodingKey
case id
case name
case logo
// Set JSON Object Key
case kitchensList = "kitchens"
public required convenience init(from decoder: Decoder) throws
self.init()
let container = try decoder.container(keyedBy: CodingKeys.self)
self.id = try container.decode(Int.self, forKey: .id)
self.name = try container.decode(String.self, forKey: .name)
self.logo = try container.decode(String.self, forKey: .logo)
// Map your JSON Array response
let kitchens = try container.decodeIfPresent([VendorKitchens].self, forKey: .kitchensList) ?? [VendorKitchens()]
kitchensList.append(objectsIn: kitchens)
class VendorKitchens : Object, Decodable
@objc dynamic var id : Int = 0
@objc dynamic var name : String?
override class func primaryKey() -> String?
return "id"
private enum CodingKeys: String, CodingKey
case id
case name
【讨论】:
这是可解码不可编码的!如果你可以使用 Codable,你会得到错误:“类型 'VendorsList' 不符合协议 'Encodable'”。并且列表不应该是 var。以上是关于如何在 Codable 中使用 List 类型? (RealmSwift)的主要内容,如果未能解决你的问题,请参考以下文章