本單元第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網站,另一列是狗狗網站。其中兩張圖片網址故意寫錯,會顯示紅色的系統圖示。

image.png

程式中「抓圖」的功能與上一節相同,但是寫法有兩個差異:一是增加一個物件屬性「網址」,讓我們可以傳不同網址進去;二是使用 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() 抓圖時,對錯誤狀況必須在程式中一一判別加以應對,對程式設計師來說,「例外處理」是一件重要但很費心的工作。