版本記錄
版本號(hào) | 時(shí)間 |
---|---|
V1.0 | 2019.07.22 星期一 |
前言
這個(gè)專題我們就一起看一下Swfit相關(guān)的基礎(chǔ)知識(shí)泽疆。感興趣的可以看上面幾篇容握。
1. Swift基礎(chǔ)知識(shí)相關(guān)(一) —— 泛型(一)
開(kāi)始
首先看下主要內(nèi)容
主要內(nèi)容:在本教程中,您將學(xué)習(xí)Swift中的所有編碼和解碼馍迄,探索自定義日期和自定義編碼等基礎(chǔ)知識(shí)和高級(jí)主題尿庐。
然后看些寫(xiě)作環(huán)境
Swift 5, iOS 12, Xcode 10
iOS應(yīng)用程序的一項(xiàng)常見(jiàn)任務(wù)是保存數(shù)據(jù)并通過(guò)網(wǎng)絡(luò)發(fā)送數(shù)據(jù)玩祟。 但在此之前靡馁,您需要通過(guò)稱為編碼或序列化(encoding or serialization)
的過(guò)程將數(shù)據(jù)轉(zhuǎn)換為合適的格式欲鹏。
在應(yīng)用中使用之前,您還需要將通過(guò)網(wǎng)絡(luò)發(fā)送的已保存數(shù)據(jù)轉(zhuǎn)換為合適的格式臭墨。 該反向過(guò)程稱為解碼或反序列化(decoding or deserialization)
赔嚎。
在本教程中,您將通過(guò)管理自己的toy store
了解有關(guān)Swift編碼和解碼的所有信息胧弛。 您將在此過(guò)程中探索以下主題:
- 在
snake case and camel case
之間切換尤误。 - 定義自定義編碼key。
- 使用
keyed, unkeyed and nested containers
结缚。 - 處理嵌套類(lèi)型袄膏,日期,子類(lèi)和多態(tài)類(lèi)型掺冠。
有很多東西可以了解,所以是時(shí)候開(kāi)始了码党!
注意:本教程假定您具有
JSON
的基本知識(shí)德崭。 如果您需要快速瀏覽,請(qǐng)查看此 cheat sheet揖盘。
打開(kāi)起始項(xiàng)目眉厨,通過(guò)轉(zhuǎn)到View ? Navigators ? Show Project Navigator
,確保在Xcode中可以看到Project navigator
兽狭。 打開(kāi)Nested types
憾股。
為Toy
和Employee
添加Codable
遵守:
struct Toy: Codable {
...
}
struct Employee: Codable {
...
}
Codable
本身不是協(xié)議,而是另外兩個(gè)協(xié)議的別名:Encodable
和Decodable
箕慧。 正如您可能猜到的那樣服球,這兩個(gè)協(xié)議聲明類(lèi)型可以編碼為不同的格式并從其中解碼。
您不需要再做任何事情颠焦,因?yàn)?code>Toy和Employee
的所有存儲(chǔ)屬性(stored properties)
都是可編碼(codable)
的斩熊。 默認(rèn)情況下,Swift標(biāo)準(zhǔn)庫(kù)和基礎(chǔ)類(lèi)型(Swift Standard Library and Foundation )
中的許多基本類(lèi)型(例如伐庭,String
和URL
)都是可編碼的粉渠。
注意:您可以將可編碼類(lèi)型編碼為各種格式分冈,例如
Property Lists (PLists)
,XML
或JSON
霸株,但是對(duì)于本教程雕沉,您只能使用JSON。
添加JSONEncoder
和JSONDecoder
來(lái)處理toys
和employees
的JSON
編碼和解碼:
let encoder = JSONEncoder()
let decoder = JSONDecoder()
這就是使用JSON所需的全部?jī)?nèi)容去件。 第一次編碼和解碼挑戰(zhàn)的時(shí)間坡椒!
Encoding and Decoding Nested Types
Employee
包含一個(gè)Toy
屬性 - 它是一個(gè)嵌套類(lèi)型(nested type)
。 編碼employee
的JSON結(jié)構(gòu)與Employee
結(jié)構(gòu)匹配:
{
"name" : "John Appleseed",
"id" : 7,
"favoriteToy" : {
"name" : "Teddy Bear"
}
}
public struct Employee: Codable {
var name: String
var id: Int
var favoriteToy: Toy
}
JSON
在favoriteToy
中嵌套name
箫攀,所有JSON鍵與Employee
和Toy
存儲(chǔ)屬性相同肠牲,因此您可以根據(jù)數(shù)據(jù)類(lèi)型層次結(jié)構(gòu)輕松理解JSON結(jié)構(gòu)。 如果您的屬性名稱與您的JSON字段名稱匹配靴跛,并且您的屬性都是Codable
缀雳,那么您可以非常輕松地轉(zhuǎn)換為JSON或從JSON轉(zhuǎn)換。 你現(xiàn)在就試試梢睛。
Gifts
部門(mén)為員工提供他們喜歡的玩具作為生日禮物肥印。 添加以下代碼以將員工的數(shù)據(jù)發(fā)送到禮品部門(mén):
// 1
let data = try encoder.encode(employee)
// 2
let string = String(data: data, encoding: .utf8)!
以下是此代碼的工作原理:
- 1) 使用
encode(_:)
將employee
編碼為JSON
(我告訴過(guò)你這很簡(jiǎn)單!)绝葡。 - 2) 從編碼
data
創(chuàng)建一個(gè)字符串以使其可視化深碱。
注意:按
Shift-Return
可將playground
運(yùn)行到當(dāng)前行,或單擊藍(lán)色play
按鈕藏畅。 要查看結(jié)果敷硅,可以將值打印Show Result
按鈕愉阎。
編碼過(guò)程生成有效數(shù)據(jù)绞蹦,因此禮品部門(mén)可以重新創(chuàng)建員工:
let sameEmployee = try decoder.decode(Employee.self, from: data)
在這里,您已經(jīng)使用decode(_:from :)
將data
解碼回Employee
......您已經(jīng)讓您的員工非常開(kāi)心榜旦。 按藍(lán)色play
按鈕以運(yùn)行Playground
并查看結(jié)果幽七。
是時(shí)候進(jìn)行下一次挑戰(zhàn)!
Switching Between Snake Case and Camel Case Formats
禮品部門(mén)API已經(jīng)從camel case
(looksLikeThis
)轉(zhuǎn)換到snake case
(looks_like_this_instead
)以格式化其JSON的鍵溅呢。
但是Employee
和Toy
的所有存儲(chǔ)屬性都只使用camel case
的情況澡屡! 幸運(yùn)的是,Foundation
為您提供服務(wù)咐旧。
打開(kāi)Snake case vs camel case
并在創(chuàng)建編碼器和解碼器之后添加以下代碼驶鹉,然后再使用它們:
encoder.keyEncodingStrategy = .convertToSnakeCase
decoder.keyDecodingStrategy = .convertFromSnakeCase
在這里,您將keyEncodingStrategy
設(shè)置為.convertToSnakeCase
以對(duì)employee
進(jìn)行編碼铣墨。 您還將keyDecodingStrategy
設(shè)置為.convertFromSnakeCase
以解碼snakeData
梁厉。
運(yùn)行playground
并檢查snakeString
。 在這種情況下,編碼的employee
看起來(lái)像這樣(雙關(guān)語(yǔ)):
{
"name" : "John Appleseed",
"id" : 7,
"favorite_toy" : {
"name" : "Teddy Bear"
}
}
JSON中的格式現(xiàn)在是favorite_toy
词顾,并且您已將其轉(zhuǎn)換回Employee
結(jié)構(gòu)中的favoriteToy
八秃。 你再次保存了(員工的出生日!)
Working With Custom JSON Keys
禮品部門(mén)再次更改其API
以使用與您的Employee
和Toy
存儲(chǔ)屬性不同的JSON key
:
{
"name" : "John Appleseed",
"id" : 7,
"gift" : {
"name" : "Teddy Bear"
}
}
現(xiàn)在肉盹,API
用gift
取代了favoriteToy
昔驱。
這意味著JSON
中的字段名稱將不再與您的類(lèi)型中的屬性名稱匹配。 您可以定義自定義編碼鍵(custom coding keys )
以提供屬性的編碼名稱上忍。 您可以通過(guò)向類(lèi)型添加特殊枚舉來(lái)完成此操作骤肛。 打開(kāi)custom coding keys
并在Employee
類(lèi)型中添加此代碼:
enum CodingKeys: String, CodingKey {
case name, id, favoriteToy = "gift"
}
CodingKeys
是上面提到的特殊枚舉。 它符合CodingKey
并具有String
原始值窍蓝。 這里是您將favoriteToy
映射到gift
的地方腋颠。
如果此枚舉存在,則只有此處出現(xiàn)的情況將用于編碼和解碼吓笙,因此即使您的屬性不需要映射淑玫,它也必須包含在枚舉中,如name
和id
在此處所示面睛。
運(yùn)行playground
并查看編碼的字符串值 - 您將看到正在使用的新字段名稱絮蒿。 由于自定義編碼密鑰custom coding keys
,JSON不再依賴于您存儲(chǔ)的屬性叁鉴。
是時(shí)候進(jìn)行下一次挑戰(zhàn)土涝!
Working With Flat JSON Hierarchies
現(xiàn)在,Gifts部門(mén)的API不希望其JSON中有任何嵌套類(lèi)型幌墓,因此它們的代碼如下所示:
{
"name" : "John Appleseed",
"id" : 7,
"gift" : "Teddy Bear"
}
這與您的模型結(jié)構(gòu)不匹配但壮,因此您需要編寫(xiě)自己的編碼邏輯并描述如何編碼每個(gè)Employee
和Toy
存儲(chǔ)的屬性。
首先常侣,打開(kāi)Keyed containers
蜡饵。 您將看到一個(gè)聲明為Encodable
的Employee
類(lèi)型。 它也在擴(kuò)展中聲明為Decodable
袭祟。 這種拆分是為了保持你使用Swift結(jié)構(gòu)體獲得的free member-wise
初始化程序。 如果在主定義中聲明了init
方法捞附,則會(huì)丟失該方法巾乳。 在Employee
中添加此代碼:
// 1
enum CodingKeys: CodingKey {
case name, id, gift
}
func encode(to encoder: Encoder) throws {
// 2
var container = encoder.container(keyedBy: CodingKeys.self)
// 3
try container.encode(name, forKey: .name)
try container.encode(id, forKey: .id)
// 4
try container.encode(toy.name, forKey: .gift)
}
對(duì)于您在上面看到的簡(jiǎn)單情況,編譯器會(huì)自動(dòng)為您實(shí)現(xiàn)encode(to :)
鸟召。 現(xiàn)在胆绊,你自己做了。 這是代碼正在做的事情:
- 1) 創(chuàng)建一組編碼鍵來(lái)表示您的
JSON
字段欧募。 因?yàn)槟鷽](méi)有進(jìn)行任何映射压状,所以您不需要將它們聲明為字符串,因?yàn)闆](méi)有原始值。 - 2) 創(chuàng)建
KeyedEncodingContainer
种冬。 這就像您可以在編碼時(shí)存儲(chǔ)屬性的字典镣丑。 - 3) 將
name
和id
屬性直接編碼到容器中。 - 4) 使用禮品密鑰將
toy
的名稱直接編碼到容器中
運(yùn)行playground
并檢查編碼字符串的值 - 它將匹配本節(jié)頂部的JSON
娱两。 能夠選擇對(duì)哪些鍵進(jìn)行編碼的屬性為您提供了很大的靈活性莺匠。
解碼過(guò)程與編碼過(guò)程相反。 用這個(gè)替換可怕的fatalError("To do")
:
// 1
let container = try decoder.container(keyedBy: CodingKeys.self)
// 2
name = try container.decode(String.self, forKey: .name)
id = try container.decode(Int.self, forKey: .id)
// 3
let gift = try container.decode(String.self, forKey: .gift)
favoriteToy = Toy(name: gift)
與編碼一樣十兢,對(duì)于簡(jiǎn)單的情況趣竣,編譯器會(huì)自動(dòng)為您生成init(from :)
,但是您自己就是這樣做的旱物。 這是代碼正在做的事情:
- 1) 從解碼器獲取一個(gè)鍵控容器遥缕,它將包含JSON中的所有屬性。
- 2) 使用適當(dāng)?shù)念?lèi)型和編碼key從容器中提取
name and id
宵呛。 - 3) 提取禮物的名稱单匣,并使用它來(lái)構(gòu)建
Toy
并將其分配給正確的屬性。
添加一行以從平面JSON重新創(chuàng)建employee
:
let sameEmployee = try decoder.decode(Employee.self, from: data)
這一次烤蜕,您選擇了哪些屬性來(lái)解碼哪些鍵封孙,并有機(jī)會(huì)在解碼過(guò)程中進(jìn)一步工作。 手動(dòng)編碼和解碼功能強(qiáng)大讽营,為您提供靈活性虎忌。 您將在接下來(lái)的挑戰(zhàn)中了解更多相關(guān)信息。
Working With Deep JSON Hierarchies
禮品部門(mén)希望確保員工的生日禮物只能是玩具橱鹏,因此其API會(huì)生成如下所示的JSON:
{
"name" : "John Appleseed",
"id" : 7,
"gift" : {
"toy" : {
"name" : "Teddy Bear"
}
}
}
你在toy
和gift
里面的toy
同時(shí)嵌入name
膜蠢。 與Employee
層次結(jié)構(gòu)相比,JSON結(jié)構(gòu)添加了額外級(jí)別的縮進(jìn)莉兰,因此在這種情況下您需要使用嵌套的鍵控容器(nested keyed containers)
作為禮物挑围。
打開(kāi)嵌套的鍵控容器并將以下代碼添加到Employee
:
// 1
enum CodingKeys: CodingKey {
case name, id, gift
}
// 2
enum GiftKeys: CodingKey {
case toy
}
// 3
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(name, forKey: .name)
try container.encode(id, forKey: .id)
// 4
var giftContainer = container
.nestedContainer(keyedBy: GiftKeys.self, forKey: .gift)
try giftContainer.encode(toy, forKey: .toy)
}
這就是上面代碼的工作原理:
- 1) 創(chuàng)建
top-level coding keys
。 - 2) 創(chuàng)建另一組編碼鍵糖荒,您將使用它來(lái)創(chuàng)建另一個(gè)容器杉辙。
- 3) 按照您習(xí)慣的方式對(duì)
name and id
進(jìn)行編碼。 - 4) 創(chuàng)建一個(gè)嵌套容器
nestedContainer(keyedBy:forKey :)
并用它編碼toy
捶朵。
運(yùn)行playground
并檢查編碼的字符串以查看多級(jí)JSON蜘矢。 您可以使用盡可能多的嵌套容器,因?yàn)槟腏SON具有縮進(jìn)級(jí)別综看。 在現(xiàn)實(shí)世界的API中使用復(fù)雜而深入的JSON層次結(jié)構(gòu)時(shí)品腹,這很方便。
在這種情況下红碑,解碼很簡(jiǎn)單舞吭。 添加以下擴(kuò)展名:
extension Employee: Decodable {
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
name = try container.decode(String.self, forKey: .name)
id = try container.decode(Int.self, forKey: .id)
let giftContainer = try container
.nestedContainer(keyedBy: GiftKeys.self, forKey: .gift)
favoriteToy = try giftContainer.decode(Toy.self, forKey: .toy)
}
}
let sameEmployee = try decoder.decode(Employee.self, from: nestedData)
您已使用嵌套解碼容器(nested decoding container)
將nestedData
解碼為Employee
。
Encoding and Decoding Dates
禮品部門(mén)需要知道員工的生日才能發(fā)送禮物,因此他們的JSON看起來(lái)像這樣:
{
"id" : 7,
"name" : "John Appleseed",
"birthday" : "29-05-2019",
"toy" : {
"name" : "Teddy Bear"
}
}
日期沒(méi)有JSON
標(biāo)準(zhǔn)羡鸥,這對(duì)于每個(gè)與之合作過(guò)的程序員而言都是如此蔑穴。 JSONEncoder
和JSONDecoder
默認(rèn)使用日期的timeIntervalSinceReferenceDate
的雙重表示,這在并不常見(jiàn)兄春。
您需要使用日期策略(date strategy)
澎剥。 在try encoder.encode(employee)
語(yǔ)句之前,將此代碼塊添加到日期(Dates)
:
// 1
extension DateFormatter {
static let dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "dd-MM-yyyy"
return formatter
}()
}
// 2
encoder.dateEncodingStrategy = .formatted(.dateFormatter)
decoder.dateDecodingStrategy = .formatted(.dateFormatter)
這是代碼的作用:
- 1) 創(chuàng)建與所需格式匹配的日期格式化程序赶舆。 它作為
DateFormatter
的靜態(tài)屬性添加哑姚,因?yàn)檫@是您的代碼的良好實(shí)踐,因此格式化程序是可重用的芜茵。 - 2) 將
dateEncodingStrategy
和dateDecodingStrategy
設(shè)置為.formatted(.dateFormatter)
叙量,告訴編碼器和解碼器在編碼和解碼日期時(shí)使用格式化程序。
檢查dateString
并檢查日期格式是否正確九串。 您已確保禮品部門(mén)將按時(shí)交付禮品 - 即將推出绞佩!
還有一些挑戰(zhàn),你已經(jīng)完成了猪钮。
Encoding and Decoding Subclasses
Gifts部門(mén)API可以根據(jù)類(lèi)層次結(jié)構(gòu)(class hierarchies)
處理JSON:
{
"toy" : {
"name" : "Teddy Bear"
},
"employee" : {
"name" : "John Appleseed",
"id" : 7
},
"birthday" : 580794178.33482599
}
employee
匹配沒(méi)有toy or birthday
的基類(lèi)結(jié)構(gòu)品山。 打開(kāi)Subclasses
并使BasicEmployee
符合Codable
:
class BasicEmployee: Codable {
這會(huì)給你一個(gè)錯(cuò)誤,因?yàn)?code>GiftEmployee還不是Codable
烤低。 通過(guò)向GiftEmployee
添加以下內(nèi)容來(lái)糾正:
// 1
enum CodingKeys: CodingKey {
case employee, birthday, toy
}
// 2
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
birthday = try container.decode(Date.self, forKey: .birthday)
toy = try container.decode(Toy.self, forKey: .toy)
// 3
let baseDecoder = try container.superDecoder(forKey: .employee)
try super.init(from: baseDecoder)
}
此代碼涵蓋解碼:
- 1) 添加相關(guān)的編碼密鑰肘交。
- 2) 解碼特定于子類(lèi)的屬性。
- 3) 使用
superDecoder(forKey :)
獲取一個(gè)適合傳遞給超類(lèi)的init(from :)
方法的解碼器實(shí)例扑馁,然后初始化超類(lèi)涯呻。
現(xiàn)在在GiftEmployee
中實(shí)現(xiàn)編碼:
override func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(birthday, forKey: .birthday)
try container.encode(toy, forKey: .toy)
let baseEncoder = container.superEncoder(forKey: .employee)
try super.encode(to: baseEncoder)
}
它是相同的模式,但您使用superEncoder(forKey :)
為超類(lèi)準(zhǔn)備編碼器腻要。 將以下代碼添加到playground
的末尾以測(cè)試您的可編碼子類(lèi):
let giftEmployee = GiftEmployee(name: "John Appleseed", id: 7, birthday: Date(),
toy: toy)
let giftData = try encoder.encode(giftEmployee)
let giftString = String(data: giftData, encoding: .utf8)!
let sameGiftEmployee = try decoder.decode(GiftEmployee.self, from: giftData)
檢查giftString
的值以查看您的工作复罐! 您可以在應(yīng)用程序中處理更復(fù)雜的類(lèi)層次結(jié)構(gòu)。 是時(shí)候進(jìn)行下一次挑戰(zhàn)雄家!
Handling Arrays With Mixed Types
Gifts
部門(mén)API公開(kāi)了適用于不同類(lèi)型員工的JSON:
[
{
"name" : "John Appleseed",
"id" : 7
},
{
"id" : 7,
"name" : "John Appleseed",
"birthday" : 580797832.94787002,
"toy" : {
"name" : "Teddy Bear"
}
}
]
此JSON數(shù)組是多態(tài)的效诅,因?yàn)樗J(rèn)和自定義employees
。 打開(kāi)Polymorphic types
趟济,您將看到不同類(lèi)型的員工由枚舉表示乱投。 首先,聲明枚舉是Encodable
:
enum AnyEmployee: Encodable {
然后將此代碼添加到枚舉的主體:
// 1
enum CodingKeys: CodingKey {
case name, id, birthday, toy
}
// 2
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
switch self {
case .defaultEmployee(let name, let id):
try container.encode(name, forKey: .name)
try container.encode(id, forKey: .id)
case .customEmployee(let name, let id, let birthday, let toy):
try container.encode(name, forKey: .name)
try container.encode(id, forKey: .id)
try container.encode(birthday, forKey: .birthday)
try container.encode(toy, forKey: .toy)
case .noEmployee:
let context = EncodingError.Context(codingPath: encoder.codingPath,
debugDescription: "Invalid employee!")
throw EncodingError.invalidValue(self, context)
}
}
以下是此代碼的用途:
- 1) 定義足夠的編碼密鑰以涵蓋所有可能的情況咙好。
- 2) 對(duì)有效員工進(jìn)行編碼篡腌,并對(duì)無(wú)效員工拋出
EncodingError.invalidValue(_:_ :)
褐荷。
通過(guò)將以下內(nèi)容添加到playground
的末尾來(lái)測(cè)試您的編碼:
let employees = [AnyEmployee.defaultEmployee("John Appleseed", 7),
AnyEmployee.customEmployee("John Appleseed", 7, Date(), toy)]
let employeesData = try encoder.encode(employees)
let employeesString = String(data: employeesData, encoding: .utf8)!
檢查employeesString
的值以查看混合數(shù)組勾效。
解碼有點(diǎn)復(fù)雜,因?yàn)樵跊Q定如何繼續(xù)之前,你必須弄清楚JSON中的內(nèi)容层宫。 將以下代碼添加到playground
:
extension AnyEmployee: Decodable {
init(from decoder: Decoder) throws {
// 1
let container = try decoder.container(keyedBy: CodingKeys.self)
let containerKeys = Set(container.allKeys)
let defaultKeys = Set<CodingKeys>([.name, .id])
let customKeys = Set<CodingKeys>([.name, .id, .birthday, .toy])
// 2
switch containerKeys {
case defaultKeys:
let name = try container.decode(String.self, forKey: .name)
let id = try container.decode(Int.self, forKey: .id)
self = .defaultEmployee(name, id)
case customKeys:
let name = try container.decode(String.self, forKey: .name)
let id = try container.decode(Int.self, forKey: .id)
let birthday = try container.decode(Date.self, forKey: .birthday)
let toy = try container.decode(Toy.self, forKey: .toy)
self = .customEmployee(name, id, birthday, toy)
default:
self = .noEmployee
}
}
}
// 4
let sameEmployees = try decoder.decode([AnyEmployee].self, from: employeesData)
這就是它的工作原理:
- 1) 像往常一樣獲取一個(gè)鍵控容器杨伙,然后檢查
allKeys
屬性以確定JSON中存在哪些鍵。 - 2) 檢查
containerKeys
是否與默認(rèn)員工或自定義員工所需的密鑰匹配萌腿,并提取相關(guān)屬性限匣;否則,建立一個(gè).noEmployee
毁菱。 如果沒(méi)有合適的默認(rèn)值米死,您可以選擇在此處拋出錯(cuò)誤。 - 3) 將
employeesData
解碼為[AnyEmployee]
贮庞。
您可以根據(jù)具體類(lèi)型對(duì)employeesData
中的每個(gè)employee
進(jìn)行解碼峦筒,就像編碼一樣。
只留下兩個(gè)挑戰(zhàn) - 下一個(gè)挑戰(zhàn)窗慎!
Working With Arrays
禮品部門(mén)為員工的生日禮物添加標(biāo)簽物喷;他們的JSON看起來(lái)像這樣:
[
"teddy bear",
"TEDDY BEAR",
"Teddy Bear"
]
JSON
數(shù)組包含小寫(xiě),大寫(xiě)和常規(guī)標(biāo)簽名稱遮斥。 這次你不需要任何密鑰峦失,所以你使用一個(gè)unkeyed container
。
打開(kāi)Unkeyed container
容器并將編碼代碼添加到Label
:
func encode(to encoder: Encoder) throws {
var container = encoder.unkeyedContainer()
try container.encode(toy.name.lowercased())
try container.encode(toy.name.uppercased())
try container.encode(toy.name)
}
UnkeyedEncodingContainer
就像你目前使用的容器一樣工作术吗,除了......你猜對(duì)了尉辑,沒(méi)有鍵。 可以將其視為寫(xiě)入JSON數(shù)組而不是JSON字典藐翎。 您將三個(gè)不同的字符串編碼到容器中材蹬。
運(yùn)行playground
并檢查labelString
以查看您的數(shù)組。
這是解碼的外觀吝镣。 將以下代碼添加到playground
的末尾:
extension Label: Decodable {
// 1
init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
var name = ""
while !container.isAtEnd {
name = try container.decode(String.self)
}
toy = Toy(name: name)
}
}
// 2
let sameLabel = try decoder.decode(Label.self, from: labelData)
這就是上面代碼的工作原理:
- 1) 獲取解碼器的無(wú)鍵解碼容器
(unkeyed decoding container)
堤器,并使用decode(_ :)
循環(huán)解碼,以解碼最終的末贾,格式正確的標(biāo)簽名稱闸溃。 - 2) 使用未加密碼的解碼容器
(unkeyed decoding container)
將labelData
解碼為Label
。
您遍歷整個(gè)解碼容器拱撵,因?yàn)樽詈髸?huì)出現(xiàn)正確的標(biāo)簽名稱辉川。
你最后一次挑戰(zhàn)的時(shí)間!
Working With Arrays Within Objects
禮品部門(mén)想要查看員工生日禮物的名稱和標(biāo)簽拴测,因此其API生成的JSON如下所示:
{
"name" : "Teddy Bear",
"label" : [
"teddy bear",
"TEDDY BEAR",
"Teddy Bear"
]
}
您將標(biāo)簽名稱嵌套在label
內(nèi)乓旗。 與前一個(gè)挑戰(zhàn)相比,JSON結(jié)構(gòu)增加了額外的縮進(jìn)級(jí)別集索,因此在這種情況下屿愚,您需要使用嵌套的無(wú)鍵容器(nested unkeyed containers)
作為label
汇跨。
打開(kāi)Nested unkeyed containers
并將以下代碼添加到Toy
:
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(name, forKey: .name)
var labelContainer = container.nestedUnkeyedContainer(forKey: .label)
try labelContainer.encode(name.lowercased())
try labelContainer.encode(name.uppercased())
try labelContainer.encode(name)
}
在這里,您將創(chuàng)建一個(gè)嵌套的無(wú)鍵容器妆距,并使用三個(gè)標(biāo)簽值填充它穷遂。 運(yùn)行playground
并檢查string
以檢查結(jié)構(gòu)是否正確。
如果JSON具有更多縮進(jìn)級(jí)別娱据,則可以使用更多嵌套容器蚪黑。 將解碼代碼添加到playground
頁(yè)面:
extension Toy: Decodable {
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
name = try container.decode(String.self, forKey: .name)
var labelContainer = try container.nestedUnkeyedContainer(forKey: .label)
var labelName = ""
while !labelContainer.isAtEnd {
labelName = try labelContainer.decode(String.self)
}
label = labelName
}
}
let sameToy = try decoder.decode(Toy.self, from: data)
這遵循與以前相同的模式,通過(guò)數(shù)組并使用最終值來(lái)設(shè)置label
的值中剩,但是來(lái)自嵌套的未鍵控容器(nested unkeyed container)
忌穿。
恭喜您完成所有挑戰(zhàn)!
后記
本篇主要講述了Swift編碼和解碼结啼,感興趣的給個(gè)贊或者關(guān)注~~~