大家好,鐵人賽堂堂邁入第二十二日!
昨天,我們用 Codable
教會了 App 如何「聽懂」API 的語言。今天,我們的任務是建立真正的「通訊管道」,讓 App 能夠將請求發送出去,並接收後端的回應。
我們將打造一個專業、可重複使用的 NetworkManager
,它將成為我們 App 中所有網路請求的中樞。這篇文章將會聚焦在 Swift 的現代化網路開發實踐上,包括泛型 (Generics) 與 async/await
。
NetworkConfiguration.swift
)一個常見的壞習慣,是將伺服器網址、API 路徑等設定,散落在程式碼的各個角落。這樣做的後果就是,一旦伺服器 IP 變更,你就得像尋寶一樣找出所有需要修改的地方。
一個更好的做法是,將所有網路設定集中管理。我們在 Model/Network
資料夾下建立 NetworkConfiguration.swift
:
// NetworkConfiguration.swift
struct NetworkConfiguration {
enum Server {
case adk
var host: String {
// 請將此 IP 修改為您 adk api_server 的實際 IP 位址
return "Your adk api_server IP"
}
}
// 將所有 API 路徑用 enum 定義出來
enum APIPath: String {
case session = "/apps/multi_tool_agent/users/u_123/sessions/s_123"
case run = "/run"
}
// 定義清晰的網路錯誤類型
enum NetworkError: Error {
case badURLFormat
case badResponse
// ... 其他錯誤類型
}
}
重點提示: 你的 iPhone 和運行
adk api_server
的電腦,必須連接到同一個 Wi-Fi 網路,並且你需要將host
中的 IP 位址,設定為你電腦的區域網路 IP。
NetworkManager.swift
)接下來是今天的核心。我們將建立一個 NetworkManager
,並採用單例模式 (Singleton),確保整個 App 共用同一個實例來管理所有網路請求。
// NetworkManager.swift
import Foundation
final class NetworkManager {
static let shared = NetworkManager() // 單例實例
// 這是我們唯一的、萬用的網路請求函式
func requestData<E, D>(
method: HTTP.Method,
server: NetworkConfiguration.Server,
path: APIPath,
parameters: E
) async throws -> D where E: Encodable, D: Decodable {
let urlRequest = try buildURLRequest(...) // 組合 URLRequest
// 使用 Swift 現代的 async/await 語法發送請求
let (data, response) = try await URLSession.shared.data(for: urlRequest)
// ... 錯誤處理與 JSON 解碼 ...
let result = try JSONDecoder().decode(D.self, from: data)
return result
}
// ... 其他輔助函式 ...
}
async throws
: 這個函式簽名告訴我們兩件事:1. 這是一個非同步函式,我們可以用 await
來呼叫它,不會卡住 App 的主畫面。2. 它可能會拋出錯誤 (throws
),我們需要用 try
來處理。<E, D>
: 這就是讓 requestData
變得「萬用」的魔法!
E: Encodable
: 表示 parameters
可以是任何我們昨天定義的請求 struct
。D: Decodable
: 表示這個函式可以回傳任何我們昨天定義的回應 struct
。NetworkManager
在之後的 ViewController
中,呼叫這個強大的 NetworkManager
會變得異常簡單和直觀:
// MainViewController.swift (明日內容預覽)
func runAgent(for text: String) async {
do {
let requestBody = SendMessageRequest(...) // 建立請求物件
// 一行程式碼,優雅地發送請求並等待回應
let response: SendMessageResponse = try await NetworkManager.shared.requestData(
method: .post,
server: .adk,
path: .run,
parameters: requestBody
)
// 成功!可以開始處理 response 了
} catch {
// 優雅地處理可能發生的網路錯誤
print("網路請求失敗: \(error)")
}
}
今天,我們為 App 打造了強健的「引擎」。我們學會了:
async/await
,打造一個萬用且現代化的 NetworkManager
。
我們的 App 內部已經萬事俱備。明天,我們將正式進入 UI 開發,我們會先從最小的元件——「聊天氣泡」(TableViewCell
) 開始,將資料與畫面進行第一次的結合!