如何为可编码对象提供自定义代码功能
Posted
技术标签:
【中文标题】如何为可编码对象提供自定义代码功能【英文标题】:how to provide a custom code func to encodable object 【发布时间】:2018-12-26 13:47:25 【问题描述】:有一种方法可以使我的 on code 函数适用于我的可编码结构, 我面临的问题是我可以从网络对其进行编码,但不能从用户默认值解码。这是结构的实现。
struct profile: Codable
var id: Int?
var firstName: String?
var lastName: String?
var description: String?
var gender: String?
var birthDate: Date?
var smoke: Bool?
var findHouse: Bool?
var hasRoom: Bool?
var sociable: Int?
var ordered: Int?
var athlete: Int?
var party: Int?
var domestic:Int?
var gamer: Int?
var usersId: Int?
var imageId: Int?
var createdAt: Date?
var updatedAt: Date?
var image: imageModel?
var tags: [String]?
enum CodingKeys: String, CodingKey
case id
case firstName = "first_name"
case lastName = "last_name"
case description
case gender
case birthDate = "birth_date"
case smoke
case findHouse = "find_house"
case hasRoom = "has_room"
case sociable
case ordered
case athlete
case party
case domestic
case gamer
case usersId = "users_id"
case imageId = "image_id"
case createdAt = "created_at"
case updatedAt = "updated_at"
case image
case tags
init()
init(from decoder: Decoder) throws
print("going to decode from decoder showing results")
let values = try decoder.container(keyedBy: CodingKeys.self)
self.id = (try values.decodeIfPresent(Int.self, forKey: .id))
print("id value \(self.id)")
self.firstName = (try values.decodeIfPresent(String.self, forKey: .firstName))
print("name value \(self.firstName)")
self.lastName = (try values.decodeIfPresent(String.self, forKey: .lastName))
print("lastname value \(lastName)")
self.description = (try values.decodeIfPresent(String.self, forKey: .description))
print("description value \(description)")
self.gender = (try values.decodeIfPresent(String.self, forKey: .gender))
print("gender value \(gender)")
if let date = try values.decodeIfPresent(String.self, forKey: .birthDate)
print("date value \(date)")
self.birthDate = Date(fromString: String(date.dropLast(10)), format: .custom("yyyy-MM-dd"))
print("after conversion \(self.birthDate)")
else
self.birthDate = nil
self.smoke = (try values.decodeIfPresent(Int.self, forKey: .smoke)) == 0 ? false : true
print("smoke value \(smoke)")
self.findHouse = (try values.decodeIfPresent(Int.self, forKey: .findHouse)) == 0 ? false : true
print("findhouse value \(findHouse)")
self.hasRoom = (try values.decodeIfPresent(Int.self, forKey: .hasRoom)) == 0 ? false : true
print("has romm value \(hasRoom)")
self.sociable = (try values.decodeIfPresent(Int.self, forKey: .sociable))
print("sociable value\(sociable)")
self.ordered = (try values.decodeIfPresent(Int.self, forKey: .ordered))
print("orderder value \(ordered)")
self.athlete = (try values.decodeIfPresent(Int.self, forKey: .athlete))
print("athlete value \(athlete)")
self.party = (try values.decodeIfPresent(Int.self, forKey: .party))
self.domestic = (try values.decodeIfPresent(Int.self, forKey: .domestic))
self.gamer = (try values.decodeIfPresent(Int.self, forKey: .gamer))
self.usersId = (try values.decodeIfPresent(Int.self, forKey: .usersId))
self.imageId = (try values.decodeIfPresent(Int.self, forKey: .imageId))
if let date = try values.decodeIfPresent(String.self, forKey: .createdAt)
self.createdAt = Date(fromString: String(date.dropLast(10)), format: .custom("yyyy-MM-dd"))
else
self.createdAt = nil
if let date = try values.decodeIfPresent(String.self, forKey: .updatedAt)
self.updatedAt = Date(fromString: String(date.dropLast(10)), format: .custom("yyyy-MM-dd"))
else
self.updatedAt = nil
self.image = (try values.decodeIfPresent(imageModel.self, forKey: .image))
self.tags = (try values.decodeIfPresent([String].self, forKey: .tags))
这是保存到 userdefaults.standard 的代码:
let encoder = JSONEncoder()
if let encoded = try? encoder.encode(requestManager.instance.user)
UserDefaults.standard.set(encoded, forKey: "user")
if let json = String(data: encoded, encoding: .utf8)
print("reading value in userDefault \(json)")
所以当我尝试使用此代码从 userDefaults 解码时:
let decoder = JSONDecoder()
if let user = UserDefaults.standard.data(forKey: "user")
print("data for user \(user)")
do
let question = try decoder.decode(profile.self, from: user)
print("birthDate from decoder \(question.birthDate)")
print("id from decoder \(question.id)")
print("name from decoder \(question.firstName)")
if question.id != nil
print("the id is not Nil \(question)")
requestManager.instance.user = question
catch
print(error.localizedDescription)
print("el valor de birthDate in \(requestManager.instance.user.birthDate)")
在此之后,我在控制台中收到了这些消息
将从解码器解码。 id 值 可选(3)。 名称值可选(“Yoel”)。 由于格式不正确,无法读取数据。
我猜是birthDate、createdAt和updatedAt字段中的日期类型。
所以我想尝试的是创建一个自定义编码器,因为在解码器中需要一个字符串并且应该返回一个日期值。
【问题讨论】:
如果您更改init(from
中的类型,您必须在 encode(
中执行相反的操作
不需要创建编码功能,我已经完成了编码功能,但这并不能解决保存到用户默认设置的主要问题。
确实有必要。例如,您将hasRoom
编码为Bool
(使用合成编码器),但您将其解码为Int
,这会导致类型不匹配。尊重,没有冒犯,但你的整个结构是一团糟。 ??????
这里的问题是我从端点得到的,如果是布尔值 true 或 false,那会很好,但确实是布尔值 0 和 1,所以如果我这样做,那么 Int 必须对只有 0 和 1 所以很明显一个布尔值只是没有从端点恢复
尽管如此,如果您使用完整的Codable
协议,所有编码和解码类型都必须匹配。
【参考方案1】:
日期本身符合可编码协议。为什么您尝试在您的init(from decoder: Decoder)
中自己进行一些自定义解码?您不应该尝试将其解码为字符串。我只是尝试了一个像这样的非常小的实现:
//: Playground - noun: a place where people can play
import Foundation
var str = "Hello, playground"
struct Person:Codable
var name:String
var birthDate:Date
func test ()
let pascal = Person(name: "Pascal", birthDate: Date(timeIntervalSinceNow: -26.6 * 365 * 24 * 3600))
print(pascal)
let encoder = JSONEncoder()
if let encoded = try? encoder.encode(pascal)
UserDefaults.standard.set(encoded, forKey: "person")
let decoder = JSONDecoder()
guard let data = UserDefaults.standard.data(forKey: "person"),
let person = try? decoder.decode(Person.self, from: data) else
print("damn")
return
print(person)
test()
【讨论】:
事情是生日来自端点的字符串,所以我必须对其进行格式化以制作自定义日期,否则编码器和解码器会失败。生日格式类似于“yyyy-MM-dd HH:mm:ss” 对不起,我不太明白。您在解码中使用了不同的格式:Date(fromString: String(date.dropLast(10)), format: .custom("yyyy-MM-dd"))
。所以你得到了一个 JSON 对象,你只是想把它转换成一个 swift 对象?
我只是调整到从网络请求接收到的日期格式,我得到的是一个格式为“yyyy-MM-dd HH:mm:ss”的字符串,所以我做了一个 droplast(10)编码为 mmmm-YY-dd 不使用时间部分
现在我明白你的问题了。您实现了 init(from: aDecoder) 以便使用从服务器获得的 json 响应来初始化您的结构。现在您遇到了无法解码从 UserDefaults 读取的 json 的问题,因为您没有按照帖子下的 cmets 中的建议实现编码功能。这会导致生日被编码为日期,而您想在解码方法中解码字符串。
在这种情况下,覆盖解码器不是要走的路。只需在您的结构中使用编码器函数实现编码,并将您的日期编码为与您从服务器获取的格式相同的字符串。否则,您可以简单地将日期字符串一直保存在结构中,并在需要时将字符串转换为日期对象。以上是关于如何为可编码对象提供自定义代码功能的主要内容,如果未能解决你的问题,请参考以下文章
如何为 ios 本地通知内容提供自定义辅助功能文本 - UNNotificationContent
Swift Firebase - 将估计的 ServerTimestamp 与可编码的自定义对象相结合