在本單元第4課曾經提過網路程式的並行(Concurrent)特性,當使用 dataTask() 連續發出多個網路請求(Request)時,各個連線作業並非依序執行(一個連線作業完成再執行下一個),而是多個作業同時並行,先下載完的就先顯示。
改用 AsyncImage 之後,同樣會有並行特性,以下範例我們連續用8次AsyncImage抓8張網路貓狗圖片,觀察它們顯示的順序,就可看到程式背後的並行特性。
// 3-6b AsyncImage phase
// Created by Heman, 2021/12/18
import PlaygroundSupport
import SwiftUI
struct 抓圖: View {
var 網址: String
var body: some View {
AsyncImage(url: URL(string: 網址)) { 狀態 in
if let 下載圖片 = 狀態.image {
下載圖片
.resizable()
.scaledToFit()
} else if 狀態.error != nil {
Image(systemName: "xmark.icloud.fill")
.scaleEffect(2)
.foregroundColor(.red)
} else {
ProgressView()
}
}
.frame(width: 200, height: 200)
}
}
let 阿貓 = "<https://thecatapi.com/api/images/get?format=src&type=jpg>"
let 阿狗 = "<https://thedogapi.com/api/images/get?format=src&type=jpg>"
struct 網路上的阿貓阿狗: View {
var body: some View {
HStack {
VStack {
抓圖(網址: 阿貓)
抓圖(網址: 阿貓)
抓圖(網址: "<https://thecatapi.com/>")
抓圖(網址: 阿貓)
}
VStack {
抓圖(網址: 阿狗)
抓圖(網址: "<https://thedogapi.com/>")
抓圖(網址: 阿狗)
抓圖(網址: 阿狗)
}
}
}
}
PlaygroundPage.current.setLiveView(網路上的阿貓阿狗())
此範例顯示視圖「網路上的阿貓阿狗」,連續使用8次「抓圖」,分兩列4 x 2顯示, 第一列是連到提供貓貓圖片的API網站,另一列是狗狗網站。其中兩張圖片網址故意寫錯,會顯示紅色的系統圖示。
程式中「抓圖」的功能與上一節相同,但是寫法有兩個差異:一是增加一個物件屬性「網址」,讓我們可以傳不同網址進去;二是使用 AsyncImage 另外一種語法,詳述如下。
在上一課註解曾提過,Swift 同一個物件通常允許多種用法,用法之間以參數的差異來做區別。
本節 AsyncImage 用法與上一節不同的地方,在於少掉 placeholder 參數,這時候帶入匿名函式的參數類型,會與上一節有所不同。
範例3-6a
AsyncImage(url: URL(string: 網址)) { 下載圖片 in
下載圖片
.resizable()
.scaledToFit()
} placeholder: {
ProgressView()
}
在上一節的 AsyncImage 有 placeholder 參數,這時候傳入匿名函式的參數是Image類型的「下載圖片」,在取得圖片之前,則顯示 placeholder 裡面的 ProgressView()。
3-6a 這段程式碼有個缺失,就是當「網址」錯誤,或是網路斷線等原因,無法正確取得圖片時,畫面會一直顯示ProgressView(),也就是不停地轉圈,讓使用者空等。
過去我們用 URLSession.shared.dataTask() 抓圖時,對錯誤狀況必須在程式中一一判別加以應對,對程式設計師來說,「例外處理」是一件重要但很費心的工作。