尝试过无数次学习 Swift ,然后无数次被虐。
又要重新学一遍,因为 Swift 1 发布了😪
又要重新学一遍,因为 Swift 2 发布了😪
又要重新学一遍,因为 Swift 3 发布了😪
又要重新学一遍,因为 Swift 4 发布了😪
这次我找了个切入点,从解析 JSON 开始。也算蹭个热点,Swift 4 终于添加了对 JSON 的原生支持。好吧,以下基本都是从这篇文章抄来的啦😅
var beer = [
"name": "Endeavor😋",
"brewery": "Saint Arnold",
"style": "ipa",
"abv": "8.9"
]
let encoder = JSONEncoder()
let data = try! encoder.encode(beer)
print(String(data: data, encoding: .utf8)!)
// {"style":"ipa","brewery":"Saint Arnold","name":"Endeavor😋","abv":"8.9"}
显而易见,用Dictionary
来表示的缺点是值都得一个类型[1]。比如:abv
本该是数值,但这里也得用String
。
改进 1 :用Struct
来接收 JSON
enum BeerStyle : String, Codable {
case ipa
case stout
case kolsch
// ...
}
struct Beer : Codable {
let name: String
let brewery: String
let style: BeerStyle
let abv: Double
}
var beer = Beer(name: "Endeavor😋", brewery: "Saint Arnold", style: .ipa, abv: 8.9)
其余代码不用变。注意:这里需要将Beer
类型标记为Codable
。
接着问题又来了,JSON 数据一般用 snake-case 的命名风格,与 Swift 不符。
改进 2 :自定义键值名
struct Beer : Codable {
let name: String
let brewery: String
let style: BeerStyle
let abv: Double
enum CodingKeys : String, CodingKey {
case name
case abv = "alcohol_by_volume"
case brewery = "brewery_name"
case style
}
}
// {"style":"ipa","name":"Endeavor😋","brewery_name":"Saint Arnold","alcohol_by_volume":8.9000000000000004}
但你不觉得这输出有点丑吗?
改进 3 :改进阅读体验
添加一行:
encoder.outputFormatting = .prettyPrinted
输出好看多了:
{
"style" : "ipa",
"name" : "Endeavor😋",
"brewery_name" : "Saint Arnold",
"alcohol_by_volume" : 8.9000000000000004
}
现在可以考虑怎么从 JSON 文件📃中读取出结构了。
var jsonString = """
{
"name": "Endeavor",
"alcohol_by_volume": 8.9,
"brewery_name": "Saint Arnold",
"style": "ipa"
}
"""
let jsonData = jsonString.data(using: .utf8)!
let decoder = JSONDecoder()
let beer = try! decoder.decode(Beer.self, from: jsonData)
beer.name
beer.brewery
beer.style
beer.abv
接下来本该是,讨论如何用URLSession
从网络上抓 JSON 数据,可惜我还没从坑里爬出来😓
现在终于可以从饭否上扒数据了👻[2]
import Foundation
struct Statuses: Codable {
var createdAt: String
var id: String
var text: String
private enum CodingKeys: String, CodingKey {
case createdAt = "created_at"
case id
case text
}
}
if let url = URL(string: "http://api.fanfou.com/statuses/user_timeline.json?id=Sedgewick") {
do {
let contents = try String(contentsOf: url)
let decoder = JSONDecoder()
let statuses = try decoder.decode([Statuses].self, from: contents.data(using: .utf8)!)
for s in statuses {
print("\(s.createdAt)\n\(s.text)\n\n")
}
} catch {
// contents could not be loaded
}
} else {
// the URL was bad!
}
总而言之,Swift 处理起 JSON 来,比 Ruby 麻烦多了😒 Ruby 可不用事先写一个结构来接收 JSON 数据。
好吧,知足吧~~如果你在 Swift 4 发布之前处理过 JSON ,你就知道现在有多幸福了😪
参考资料:
-
⚠️就算指定为
[String : Any]
,JSONEncoder
也解析不了,不知道为什么😓 ↩ -
参见这篇教程《怎样从饭否抓数据?》和饭否 API 的文档 ↩