注:代碼基于Swift4.0
導(dǎo)讀:Swift 4 現(xiàn)在可以支持很方便的轉(zhuǎn)模型了。例:
- Book結(jié)構(gòu)體 遵守Decodable協(xié)議
struct Book: Decodable {
var title: String
var author: String
var rating: Float
}
- 自動解碼
override func viewDidLoad() {
super.viewDidLoad()
let jsonString = """
{ "title": "War and Peace: A protocol oriented approach to diplomacy",
"author": "A. Keed Decoder",
"rating": 5.0
}
"""
if let data = jsonString.data(using: .utf8) {
let decoder = JSONDecoder()
if let book = try? decoder.decode(Book.self, from: data) {
print(book.title) //War and Peace: A protocol oriented approach to diplomacy
} else {
print("decode failed")
}
}
}
正題:老代碼可能出現(xiàn)手動Decoder怔鳖,那就探索一下吧吗铐。
一: 可選明垢,解碼用decodeIfPresent
struct Book: Decodable {
var title: String
var author: String
var rating: Float?
init(from decoder: Decoder) throws {
let keyedContainer = try decoder.container(keyedBy: CodingKeys.self)
title = try keyedContainer.decode(String.self, forKey: .title)
author = try keyedContainer.decode(String.self, forKey: .author)
rating = try keyedContainer.decodeIfPresent(Float.self, forKey: CodingKeys.rating)
}
enum CodingKeys: String, CodingKey {
case title
case author
case rating
}
}
二: 非可選员辩,但json數(shù)據(jù)為nil時解碼失敗怎么辦(不想用可選撞鹉,就設(shè)置個默認(rèn)值吧)
struct Book: Decodable {
var title: String
var author: String
var rating: Float
init(from decoder: Decoder) throws {
let keyedContainer = try decoder.container(keyedBy: CodingKeys.self)
title = try keyedContainer.decode(String.self, forKey: .title)
author = try keyedContainer.decode(String.self, forKey: .author)
if let ratingValue = try keyedContainer.decodeIfPresent(Float.self, forKey: CodingKeys.rating) {
rating = ratingValue
} else {
rating = 0
}
}
enum CodingKeys: String, CodingKey {
case title
case author
case rating
}
}
三:decode失敗, rating為非可選痛悯,但服務(wù)給的json偏偏就不返這個字段余黎,或者返的這個字段為nil
客戶端這么寫rating
struct Book: Decodable {
var title: String
var author: String
var rating: Float
init(from decoder: Decoder) throws {
let keyedContainer = try decoder.container(keyedBy: CodingKeys.self)
title = try keyedContainer.decode(String.self, forKey: .title)
author = try keyedContainer.decode(String.self, forKey: .author)
rating = try keyedContainer.decode(Float.self, forKey: .rating)
}
enum CodingKeys: String, CodingKey {
case title
case author
case rating
}
}
服務(wù)端就想這么給數(shù)據(jù)
let jsonString = """
{ "title": "War and Peace: A protocol oriented approach to diplomacy",
"author": "A. Keed Decoder",
"rating":
}
"""
或者這么返
let jsonString = """
{ "title": "War and Peace: A protocol oriented approach to diplomacy",
"author": "A. Keed Decoder",
}
"""
好的。 print("decode failed")
失敗倒也沒啥载萌,反正不是崩潰惧财,但是Fabric上面捕獲到N多條Non-Fatals log(就是研究這個log才有了這篇記錄)。 當(dāng)然還是要處理啦扭仁。 所以盡量用可選垮衷,或者不想給可選至少也給個默認(rèn)值吧。
四:如何通過Crashlytics捕獲異常乖坠。
在book結(jié)構(gòu)體中加一個出版日期屬性
struct Book: Decodable {
var title: String
var author: String
var rating: Float
let publishedAt: Date
private static func dateDecode(_ container: KeyedDecodingContainer<CodingKeys>, key: CodingKeys) throws -> Date {
let date: Date
do {
date = try container.decode(Date.self, forKey: key)
}
catch {
date = Date(timeIntervalSince1970: 0)
let dateAtString = try container.decode(String.self, forKey: key)
let bookTitle = try container.decode(String.self, forKey: CodingKeys.title)
let error = CrashlyticsError.bookDateParsingFailed(codingPath: key,
bookTitle: bookTitle,
dateValue: dateAtString)
print(error)
// Crashlytics.sharedInstance().recordError(error) //Fabric 可以直接記錄
}
return date
}
init(from decoder: Decoder) throws {
let keyedContainer = try decoder.container(keyedBy: CodingKeys.self)
title = try keyedContainer.decode(String.self, forKey: .title)
author = try keyedContainer.decode(String.self, forKey: .author)
rating = try keyedContainer.decode(Float.self, forKey: .rating)
publishedAt = try Book.dateDecode(keyedContainer, key: CodingKeys.publishedAt)
}
enum CodingKeys: String, CodingKey {
case title
case author
case rating
case publishedAt
}
//string 轉(zhuǎn)date會用到
static func dateFormatter() -> DateFormatter {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
dateFormatter.timeZone = TimeZone(abbreviation: "UTC")
return dateFormatter
}
}
加一個捕獲異常CrashlyticsError文件
import UIKit
enum CrashlyticsError: CustomNSError {
case bookParsingFailed(codingPath: [CodingKey], debugDescription: String)
case bookDateParsingFailed(codingPath: CodingKey, bookTitle: String, dateValue: String)
static var errorDomain: String {
return "XXDecoderDemo"
}
var errorCode: Int {
switch self {
case .bookParsingFailed(_, _):
return 7780
case .bookDateParsingFailed(_,_,_):
return 7781
}
}
var errorUserInfo: [String : Any] {
switch self {
case .bookParsingFailed(let codingPath, let debugDescription):
var userInfo = [NSLocalizedDescriptionKey : "Book Parsing",
NSLocalizedFailureReasonErrorKey : "Can't parse",
"Description" : debugDescription]
for (index, element) in codingPath.enumerated() {
userInfo["Coding Key \(index)"] = element.stringValue
}
return userInfo
case .bookDateParsingFailed(let codingPath, let bookTitle, let dateValue):
return [NSLocalizedDescriptionKey : "Book Date Parsing",
NSLocalizedFailureReasonErrorKey : "Can't parse \(codingPath.stringValue)",
"Book Title" : bookTitle,
"Date value" : dateValue]
}
}
}
viewDidLoad代碼搀突。這里publishedAt的jsonString故意寫成錯的格式,然后進(jìn)入到捕獲異常程序熊泵。正常格式應(yīng)是:2018-01-01T00:00:00.000Z(和dateFormatter保持一致)
override func viewDidLoad() {
super.viewDidLoad()
let jsonString = """
{ "title": "War and Peace: A protocol oriented approach to diplomacy",
"author": "A. Keed Decoder",
"rating": 5.0,
"publishedAt": "019-04-16T9:24:37TPM.000Z"
}
"""
if let data = jsonString.data(using: .utf8) {
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(Book.dateFormatter())
if let book = try? decoder.decode(Book.self, from: data) {
print(book.title) //War and Peace: A protocol oriented approach to diplomacy
} else {
print("decode failed")
}
}
}
可想而知仰迁,最終解碼不會失敗,還是會打印出書名顽分。
總結(jié):雖然獲取到的publishedAt是錯的格式轩勘,轉(zhuǎn)化成date會不成功,但是進(jìn)行了異常捕獲怯邪,在catch代碼塊中,將date設(shè)置成date = Date(timeIntervalSince1970: 0)花墩,也相當(dāng)于是設(shè)置默認(rèn)值了悬秉。解碼不失敗澄步, 同時還記錄了為什么沒有轉(zhuǎn)化成功的log。 可以快速定位到出問題的那條信息和泌。 完美村缸!
捕獲到的error信息如下(Fabric記錄的話,會更加清晰):
print(error) //bookDateParsingFailed(codingPath: CodingKeys(stringValue: "publishedAt", intValue: nil), bookTitle: "War and Peace: A protocol oriented approach to diplomacy", dateValue: "019-04-16T9:24:37TPM.000Z")
空了會將demo上傳武氓。
每天學(xué)習(xí)一點(diǎn)點(diǎn)梯皿,加油??!