iT邦幫忙

2025 iThome 鐵人賽

DAY 22
0
生成式 AI

三十天解鎖上下文超能力:MCP 實戰系列 第 22

Day 22 - 跨平台實戰 II:建立通訊管道,打造通用的 NetworkManager

  • 分享至 

  • xImage
  •  

大家好,鐵人賽堂堂邁入第二十二日!

昨天,我們用 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。

二、打造萬用的 API 呼叫神器 (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
    • 這意味著,我們只需要寫這一個函式,就能處理未來所有的 API 呼叫,無論是建立 Session 還是發送訊息!

三、如何使用 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 打造了強健的「引擎」。我們學會了:

  1. 如何集中管理網路設定,讓程式碼更易於維護。
  2. 如何利用泛型和 async/await,打造一個萬用且現代化的 NetworkManager

我們的 App 內部已經萬事俱備。明天,我們將正式進入 UI 開發,我們會先從最小的元件——「聊天氣泡」(TableViewCell) 開始,將資料與畫面進行第一次的結合!


上一篇
Day 21 - 跨平台實戰 I 架構先行:搭建 iOS 專案與資料模型
下一篇
Day 23 - 跨平台實戰 III:UI 的最小單位,設計聊天氣泡 TableViewCell
系列文
三十天解鎖上下文超能力:MCP 實戰24
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言