嗨,大家今天過得好嗎?歡迎來到鐵人賽 Day 8。
截至昨天為止,我們已經用 Vibe Coding 打造了乾淨的 MVC 架構跟高質感的 SwiftUI 介面。不過,現在點擊「產生推薦菜單」按鈕,Console 只會印出一行可憐的 print("準備發送 API 請求...")。
在真實的全端開發情境中,後端(我們這次是 n8n)通常沒那麼快把 API 寫好。如果我們傻傻地等後端完工才繼續寫前端,專案進度絕對會大 Delay。
所以今天,我們要來做前端開發非常重要的一個步驟:建立 API Service 層與 Mock Data(假資料)機制。
很多剛學 iOS 或是過度依賴 AI 寫 Code 的人,會把打 API 的程式碼(像是 URLSession、JSONDecoder)全部塞在 Controller 甚至 View 裡面。這絕對是個災難。
想像一下,未來如果 API 網址換了,或是我們要加上 Token 驗證,你總不希望去翻找每一個 Controller 來改吧?所以,我們要把「網路請求」獨立成一個專門的 Service 檔案。Controller 只需要問 Service:「嘿,給我資料」,至於資料是怎麼來的,Controller 不需要、也不應該知道。
在叫 AI 幫我們寫 Service 之前,我們必須先跟未來的後端(n8n)「喬好」資料長什麼樣子。這也是我們要新增的 Model。
請在 Models 資料夾下,新增一個 HardwareMenu.swift:
import Foundation
// 這是我們預期從 n8n 收到的 AI 推薦結果
struct HardwareMenu: Codable {
let cpu: String
let gpu: String
let totalPrice: Int
let reason: String // AI 給的推薦理由
}
現在,我們要請 AI 幫我們建立 API Service 層,並且使用 Mock Data (假資料) 來模擬網路連線。為什麼要模擬?因為這樣我們才能在前端實作「轉圈圈等待 (Loading)」的狀態。
你可以用這段結構化 Prompt 來引導 AI:
我正在開發一個遵循 MVC 架構的 SwiftUI App。
目前已經定義好回傳資料的 Model 如下:
(貼上上面的 HardwareMenu 程式碼)
請幫我新增一個 `APIService.swift`:
1. 請實作一個單例模式 (Singleton) 的 `APIService`。
2. 寫一個非同步函數 `fetchRecommendation(budget:purpose:)`。
3. 【重要】目前後端還沒寫好,請在該函數內實作 Mock Data (假資料)。使用 `Task.sleep` 模擬 2 秒的網路延遲,然後回傳一份寫死的 `HardwareMenu` 假資料。
AI 應該會產出類似這樣乾淨的 Service 檔案(請把它放在專案目錄裡):
// APIService.swift
import Foundation
class APIService {
// 單例模式,方便全域呼叫
static let shared = APIService()
private init() {}
// 非同步函數,回傳我們剛剛定義的 HardwareMenu
func fetchRecommendation(budget: Int, purpose: String) async throws -> HardwareMenu {
// 模擬真實世界的網路延遲 (等待 2 秒)
try await Task.sleep(nanoseconds: 2_000_000_000)
// 未來這裡會寫真實的 URLSession 網路請求
// 目前先回傳寫死的假資料
return HardwareMenu(
cpu: "AMD Ryzen 5 7500F",
gpu: "NVIDIA RTX 4060",
totalPrice: 28500,
reason: "針對 \(purpose) 需求,這套配置在預算 \(budget) 元內能提供最佳的 1080p 遊戲體驗,性價比極高。"
)
}
}
Service 寫好了,現在我們要讓 HomeController 去呼叫它。
打開你的 HomeController.swift,我們需要新增兩個狀態:一個是讓畫面知道「正在讀取中」的 isLoading,另一個是拿來裝「結果」的 resultMenu。
你可以自己改,或是叫 AI 幫你更新:
// HomeController.swift
import Foundation
import SwiftUI // 為了 MainActor
@MainActor // 確保 UI 更新都在主執行緒
class HomeController: ObservableObject {
@Published var requestData = HardwareRequest()
@Published var isLoading = false // 新增:讀取狀態
@Published var resultMenu: HardwareMenu? // 新增:裝載 API 回傳的結果
let purposeOptions = ["3A 遊戲", "影音剪輯", "文書處理"]
func submitRequest() {
guard let budgetInt = Int(requestData.budget), budgetInt > 0 else {
print("請輸入有效的預算金額")
return
}
// 1. 開啟讀取狀態
isLoading = true
resultMenu = nil
// 2. 開啟非同步 Task 呼叫 API
Task {
do {
let menu = try await APIService.shared.fetchRecommendation(
budget: budgetInt,
purpose: requestData.purpose
)
// 3. 拿到結果後,更新狀態、關閉讀取
self.resultMenu = menu
self.isLoading = false
} catch {
print("發生錯誤:\(error)")
self.isLoading = false
}
}
}
}
把這段寫完後,雖然你的畫面看起來跟昨天一樣,但骨子裡已經完全不同了。
現在只要你按下「產生推薦菜單」,背後就會透過非同步任務 (async/await) 模擬網路連線,等個兩秒鐘後,HomeController 就能成功拿到假資料。我們成功把前端架構的最後一塊拼圖——網路通訊層 (Network Layer) 給獨立出來了。
明天,我們要用 Vibe Coding 完成前端戰場的最後一役:打磨 Loading 狀態,並把這份假資料渲染成一張漂亮的「AI 推薦結果卡片」!
我們 Day 09 見!