Swift 中给遵循 Decodable 的结构体或类建立 Key 别名

一般来说,一个好的命名可以让开发者更快速的明白这个字段所代表的意思,不过有些时候会事与愿违

诡异的命名

image.png

这是什么…

  • yhm
  • yhnl
  • yhyxdz
  • yhsjhm

恩…对于一个母语为汉语的人或许阅读起来都有一些困难,他们分别代表着

  • 用户名
  • 用户年龄
  • 用户邮箱地址
  • 用户手机号码

如果不希望在代码中继续沿用这样子的命名,转而采用以下命名

  • username
  • userAge
  • userEmail
  • userPhone

那该如何让 Swift 这件事

接下来,将介绍 Swift 中的 CodingKey

对该数据建模中,将想使用的名字填入

1
2
3
4
struct User: Codable {
let username, userEmail, userPhone: String
let userAge: Int
}

就像这样,很简单对吧

然后,在结构体 (或者类) 的内部建立一个叫做 CodingKeys 的枚举,该枚举将会是 private 属性修饰并且遵循 CodingKey 协议

完整的结构体如下所示

1
2
3
4
5
6
7
8
9
10
11
struct User: Codable {
let username, userEmail, userPhone: String
let userAge: Int

private enum CodingKeys: String, CodingKey {
case username = "yhm"
case userAge = "yhnl"
case userEmail = "yhyxdz"
case userPhone = "yhsjhm"
}
}

这么做,在 Decode 之后就可以通过 username, userEmail, userPhoneuserAge 属性来访问这些诡异的命名的字段了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import Cocoa

enum MyError: Error {
case invlidUrl
case invlidServerResponse
}

struct User: Codable {
let username, userEmail, userPhone: String
let userAge: Int

private enum CodingKeys: String, CodingKey {
case username = "yhm"
case userAge = "yhnl"
case userEmail = "yhyxdz"
case userPhone = "yhsjhm"
}
}

func getUsers() async throws -> [User] {
guard let url = URL(string: "https://my.api.mockaroo.com/bad_api.json?key=8795e870") else { throw MyError.invlidUrl }

let (data, response) = try await URLSession.shared.data(from: url)

guard
let response = response as? HTTPURLResponse,
response.statusCode == 200
else {
throw MyError.invlidServerResponse
}

let decoded = try JSONDecoder().decode([User].self, from: data)

return decoded
}


Task {
let users = try await getUsers()

for user in users {
print("Name: \(user.username), Email: \(user.userEmail)")
}
}

1
2
3
4
5
6
7
8
9
10
Name: acawood0, Email: [email protected]
Name: obrade1, Email: [email protected]
Name: kfolliott2, Email: [email protected]
Name: cmclese3, Email: [email protected]
Name: apottberry4, Email: [email protected]
Name: beyers5, Email: [email protected]
Name: fcarlozzi6, Email: [email protected]
Name: thastin7, Email: [email protected]
Name: hbriers8, Email: [email protected]
Name: bgentry9, Email: [email protected]

snake_case

还有一些情况,一些开发者可能使用类似于 Python, PHP 等使用 snake_case 作为命名规范的语言

image.png

不过,Swift 采用的是小驼峰写法,在这里使用 snake_case 真的太奇怪了

可以使用上面提供的方法吗?是可以的,但是聪明的 Swift 还有另一个方法来处理 snake_case 的返回格式

现将上面的 User Struct 还原成

1
2
3
4
struct User: Codable {
let username, userEmail, userPhone: String
let userAge: Int
}

接下来我们指定 JSONDecoder 的 Decode 策略

为了指定 JSONDecoder 的策略,我们就可以考虑将一个 JSONDecoder 的实例化赋值给一个常量

例如

1
2
3
4
5
let decoder = JSONDecoder()

decoder.keyDecodingStrategy = .convertFromSnakeCase

let decoded = try decoder.decode([User].self, from: data)

最后 decoded 出来的数据还是跟上面的数据一致

1
2
3
4
5
6
7
8
9
10
Name: bgarm0, Email: [email protected]
Name: nblasl1, Email: [email protected]
Name: gsandom2, Email: [email protected]
Name: tgarlic3, Email: [email protected]
Name: amacmoyer4, Email: [email protected]
Name: bilyinski5, Email: [email protected]
Name: wclapston6, Email: [email protected]
Name: ekeynd7, Email: [email protected]
Name: sfarlam8, Email: [email protected]
Name: cwormell9, Email: [email protected]