將網路抓下來的資料放在app中
先將它變成 array
解析 JSON 資料的兩個方法
- 利用 JSONDecoder
- 利用 JSONSerialization (舊的方法)
方法一、利用 JSONDecoder & Codable 解析 JSON
遵從 protocol Codable 的型別 資料可以解碼(Decodable)和編碼(Encodable)
- Decodable
- 可以解碼(從 Data 型別轉換)
- 可以讀檔
- Encodable
- 可以編碼(轉換成 Data 型別)
- 可以存檔
基本型別:
- Int
- String
- Bool
- Double, Float
- Data
- Date
- URL
- Array
- Dictionary
遵從 protocol Codable 的型別:
struct Lover: Codable {
let name: String
let star: String
let innerBeauty: Bool
}
只要屬性都有遵從 protocol Codable,Swift 可以自動幫我們 寫出解碼編碼的程式,因此我們不用定義 Codable 的 function
如果只要解碼,可以只遵從 Decodable protocol
如果只要編碼,可以只遵從 Encodable protocol
也可以用 class 或 enum 遵從 protocol Codable
沒遵從 protocol Codable 的屬性,我們必須自己定義 Codable 的 function,如:UIButton
如何從 JSON 資料定義遵從 Codable protocol 的型別
範例實作 - 隨機的 meme 圖片
API 的網址如下
https://some-random-api.ml/meme
格式如下:
{
"id": 12,
"image": "https://i.some-random-api.ml/07jjUxQKNf.png",
"caption": "Ooof",
"category": "random"
}
定義型別:mene
測試程式時,可以先用playground測試
利用 JSONDecoder,我們可以將網路上抓下來的 JSON 資料從 Data 型別變成自訂型別 Meme。
將 JSON 裡以 { } 描述的 object 變成自訂型別,可以用 class,也可以用 struct 定義,型別名字可自取。
struct mame: Codable {
let id: Int
let image: URL
let caption: String
}
遵從 protocol Codable 後,Meme 將有解碼跟編碼的功能。
JSON 裡 object 的 key 將成為自訂型別的 property 名字, 而 property 的型別則由 key 對應的 value 型別決定。
property 可宣告為變數,也可宣告為常數。
以下幾點規則決定 property 的型別:
- 網址 ➪ URL。(如果網址包含 ASCII 以外的文字,請將型別宣告為 String,之後再另外轉成 URL)
- 時間 ➪ Date。
- 整數 ➪ Int
- 浮點數 ➪ Float 或 Double
- 字串 ➪ String
- true 或 false ➪ Bool
- 陣列 ➪ Array
- 若value 是 object ➪ 遵從 protocol Decodable 或 Codable 的自訂型別。
- key 不一定會出現或 key 的 value 可能為 null 時 ➪ 型別要宣告為 optional。
習慣用 extension 遵從 protocol 的朋友,也可以改用以下寫法:
struct Meme {
let id: Int
let image: URL
let caption: String
let category: String
}
extension Meme: Codable { }
值得注意的,只要型別不對就會失敗,就算加問號也一樣,因此 let id: String? 也會轉換失敗。
以下兩種情況 property 要宣告為 optional:
JSON object 不一定有的 key 。
JSON object 某個 key 對應的 value 有可能是 null。
測試 JSON 解碼是否成功
方法 1: 使用 async & await
struct Meme: Codable {
let id: Int
let image: URL
let caption: String
let category: String
}
let urlString = "https://some-random-api.ml/meme"
if let url = URL(string: urlString) {
Task {
do {
let (data, _) = try await URLSession.shared.data(from: url)
let decoder = JSONDecoder()
let meme = try decoder.decode(Meme.self, from: data)
print(meme)
} catch {
print(error)
}
}
}
......
Meme(id: 7, image: https://i.some-random-api.ml/9sBO7cjaDe.png, caption: "Google it please...", category: "random")
方法 2: 使用 completion handler
struct Meme: Codable {
let id: Int
let image: URL
let caption: String
let category: String
}
let urlString = "https://some-random-api.ml/meme"
if let url = URL(string: urlString) {
URLSession.shared.dataTask(with: url) { data, response , error in
if let data {
let decoder = JSONDecoder()
do {
let meme = try decoder.decode(Meme.self, from: data)
print(meme)
} catch {
print(error)
}
}
}.resume()
}
//或是
struct Meme: Codable {
let id: Int
let image: URL
let caption: String
let category: String
}
let url = URL(string: "https://some-random-api.ml/meme")!
URLSession.shared.dataTask(with: url){
data, response, error
in
if let data {
let decoder = JSONDecoder()
do {
let meme = try decoder.decode(Meme.self, from: data)
print(meme)
} catch {
print(error)
}
}
}.resume()
Meme 資料放到 cell 上
新增一個檔案
新增二個元件,一個圖片一個文字,並拉線命名。
安裝Package - kingfisher
方便抓取網路上的圖片
完成
因為要引用套件
記得要 import Kingfisher
新增檔案來放新增的型別
檔名:File
並把剛剛的型別命名貼上
把程式放到 ViewController.swift
主程式:
/
// ViewController.swift
// 0927jsonDemo
//
// Created by Huang on 2022/9/27.
//
import UIKit
import Kingfisher
class ViewController: UIViewController {
@IBOutlet weak var imageView: UIImageView!
@IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "https://some-random-api.ml/meme")!
URLSession.shared.dataTask(with: url){
data, response, error
in
if let data {
let decoder = JSONDecoder()
do {
let meme = try decoder.decode(Meme.self, from: data)
} catch {
print(error)
}
}
}.resume()
}
}
在閉包裡面,如果要讀到外面的型別,就要加 self
把文字放到label:
self.label.text = meme.caption
把圖片放到 image
self.imageView.kf.setImage(with: meme.image)
上面兩行放在 do 裡面
do {
let meme = try decoder.decode(Meme.self, from: data)
print(meme.caption)
//文字
self.label.text = meme.caption
//圖片
self.imageView.kf.setImage(with: meme.image)
} catch {
print(error)
}
啟動模擬手機
打開後圖片跟文字都會顯示出來,但是可能會比較慢,並且,會出現紫色
的提示訊息。
再改一下程式碼:
do {
let meme = try decoder.decode(Meme.self, from: data)
print(meme.caption)
DispatchQueue.main.sync {
//文字
self.label.text = meme.caption
//圖片
self.imageView.kf.setImage(with: meme.image)
}
} catch {
print(error)
}
程式改完後,下載的速度快了些,紫色訊息也不見了。