網路基礎-定時篇
-
本單元介紹一個新物件「定時器 Timer」,這是 Foundation 裡的物件,此物件跟 URLSession 類似,都是由作業系統所控制,如果應用程式需要的話,必須跟作業系統登記,由作業系統統一分發。
登記使用定時器物件的方法如下:
let 定時器 = Timer.publish(every: 5.0, on: .main, in: .common).autoconnect()
Timer.publish() 是定時器物件類型的一個「類型方法」 "type method",就像上一課的 URLSession.shared 是個類型屬性 "type property",類型方法是整個類型適用的函式,publish() 這個函式會產出一個定時器的物件實例。
為什麼稱為 publish() 呢?publish 是發行、發布的意思,因為在此處,作業系統與App的溝通模式跟網路連線很類似,術語稱為發布-訂閱模式 "Publisher-Subscriber",圖解如下:
在範例中,let 定時器 = Timer.publish()是發布者,而視圖修飾語 .onReceive(定時器)是訂閱者。
Timer.publish() 的三個參數:
every: 5.0 on: .main in: .common
只有第一個參數 every 我們需要了解,是設定「每隔多久」發布一次通知,範例中為5.0秒。另外兩個 on: .main, in: .common 牽涉到作業系統的運作原理,我們暫不討論,但也不可省略。
這樣就會產出一個定時器物件實例,不過物件還沒有真的啟用,必須在最後一步使用物件方法 autoconnect() 來啟用,才會每隔5.0秒鐘發出一個「通知」:
let 定時器 = Timer.publish(every: 5.0, on: .main, in: .common).autoconnect()
這樣的過程是不是跟 URLSession.shared.dataTask().resume() 很類似?沒有錯,因為 Timer 和URLSession 一樣,都是非同步(asynchronous)的行為模式,我們看過範例程式後會更了解。
取得定時器的物件實例之後,如何使用呢?我們會放在顯示圖片的視圖中:
Image(uiImage: 下載圖片!) .onReceive(定時器) { 時間 in 下載圖片 = nil; 淡入 = false }
與 URLSession 類似,程式收到定時器的通知後,會帶一個參數「時間」進入匿名函式,這個參數「時間」其實就是發布通知當下的時間 Date(),不過我們在範例中並未用到這個參數值。
匿名函式中,我們只寫了兩個指定句,將「下載圖片」與「淡入」兩個狀態變數還原,這樣就會重新執行視圖的主體(body),再一次下載圖片並且產生淡入的效果。兩個短句可以寫在同一行,中間用分號 ; 隔開即可。
操作步驟如下:
- 選取畫面左方的「+」號新增電子書頁面。
- 將新增的電子書面頁命名為「(18)網路程式基礎-2定時篇」。
3-2d 定時反覆讀取網路圖片(URLSession + Timer)
- 在「Main」模組中撰寫程式:
// 3-2d 定時反覆讀取網路圖片(URLSession + Timer) // Created by Philip, Heman, Jean 2021/09/12 // Revised by Jean 2024/11/24 import PlaygroundSupport import SwiftUI let 網址 = "https://picsum.photos/720/1280" struct 抓圖: View { @State var 下載圖片: UIImage? @State var 淡入 = false let 定時器 = Timer.publish(every: 5.0, on: .main, in: .common).autoconnect() func 下載() { guard let myURL = URL(string: 網址) else { return } URLSession.shared.dataTask(with: myURL) { 回傳資料, 回應碼, 錯誤碼 in if let 圖檔 = UIImage(data: 回傳資料!) { print(回應碼 ?? "No response") 下載圖片 = 圖檔 } else { print(錯誤碼 ?? "No error") } }.resume() } var body: some View { if 下載圖片 == nil { ProgressView() .onAppear { 下載() } } else { Image(uiImage: 下載圖片!) .resizable() .scaledToFit() .opacity(淡入 ? 1.2 : 0.0) .animation(.easeInOut(duration: 5.0)) .onAppear { 淡入 = true } .onReceive(定時器) { 時間 in 下載圖片 = nil; 淡入 = false } } } } PlaygroundPage.current.setLiveView(抓圖())
- 程式執行結果,如下圖。