iT邦幫忙

2026 iThome 鐵人賽

DAY 7
0
Vibe Coding

從零打造 AI 全端應用:Vibe Coding 結合 n8n 視覺化工作流實戰系列 第 7 篇

Day 07:狀態管理不崩潰:引導 AI 建立正確的資料綁定與元件化

  • 分享至 

  • xImage
  •  

Day 07:狀態管理不崩潰:引導 AI 建立正確的資料綁定與元件化

嗨,大家今天過得好嗎?歡迎來到鐵人賽 Day 7。

昨天我們成功用 Vibe Coding 把首頁打磨得很有科技感。不過,如果你仔細看昨天的 ContentView.swift,你會發現一個隱憂:所有的 UI 程式碼都塞在同一個檔案裡。

現在還只是一個輸入框和一個按鈕而已。想像一下,等我們之後串接了 n8n,還需要加入「等待 AI 產出結果的轉圈圈動畫 (Loading State)」、「錯誤提示訊息 (Error Alert)」,以及「最終菜單展示卡片 (Result Card)」。

如果讓 AI 繼續把這些東西全塞進 ContentView,不用到下禮拜,你的檔案就會飆破 500 行。到那時候,AI 每次閱讀跟修改都會開始「幻覺」,不小心刪掉你的按鈕或是搞壞你的版面。

拆解的藝術:元件化 (Componentization)

為了不讓 AI 暴走,身為工程師的我們必須主動進行「元件化」。我們要把它拆成一個一個獨立的小積木 (Subviews),再回到主畫面組合起來。

但在 SwiftUI 裡,只要一拆分 View,馬上就會面臨一個死穴:資料狀態綁定 (State Binding)。

很多新手直接叫 AI:「幫我把輸入框拆成另一個檔案」,結果 AI 拆是拆了,但原本存在 Controller 裡的 requestData 狀態卻斷線了,導致你在子畫面輸入的數字,主畫面根本收不到。

避坑指南:精準指定 Property Wrapper

要讓 AI 乖乖幫你拆元件又不斷線,你的 Prompt 不能只說「幫我拆開」,你必須明確告訴它**「用什麼方式傳遞資料」**。在我們目前的 MVC 架構下,最適合傳遞 HardwareRequest 型別資料的方式是 @Binding。

請對 AI 下這段明確的重構指令:

這是我目前的 ContentView.swift (附上昨天的程式碼)。
為了維持良好的架構,請幫我將「卡片區塊 (包含標題、預算輸入框、用途選擇器)」獨立抽離成一個名為 `InputCardView` 的 SwiftUI 元件。

【拆分要求】
1. 請在 `InputCardView` 中使用 `@Binding` 來接收 `HardwareRequest` 資料。
2. `ContentView` 必須保持乾淨,透過傳遞 `$controller.requestData` 給 `InputCardView` 來維持狀態綁定。
3. 原本的設計風格與漸層按鈕請保留在 `ContentView`,不要更動 `HomeController` 邏輯。

拆解後的清爽架構

拿到指令後,AI 就不會亂猜你的意圖,而是精準地產出以下兩個乾淨的結構。

1. 獨立出來的 InputCardView.swift (放在 Views 資料夾)

你看,這裡使用了 @Binding,代表這個 View 只是個「接收器」,它不負責保存資料,資料的源頭依然在 Controller 身上。

import SwiftUI

struct InputCardView: View {
    // 透過 @Binding 與外部的 Controller 狀態保持同步
    @Binding var requestData: HardwareRequest
    let purposeOptions: [String]
    
    var body: some View {
        VStack(alignment: .leading, spacing: 20) {
            Text("請輸入您的需求")
                .font(.headline)
                .foregroundColor(.gray)
            
            TextField("輸入預算 (如: 30000)", text: $requestData.budget)
                .keyboardType(.numberPad)
                .padding()
                .background(Color(.systemGray6))
                .cornerRadius(10)
            
            Picker("選擇用途", selection: $requestData.purpose) {
                ForEach(purposeOptions, id: \.self) { option in
                    Text(option).tag(option)
                }
            }
            .pickerStyle(SegmentedPickerStyle())
        }
        .padding()
        .background(Color.white)
        .cornerRadius(15)
        .shadow(color: Color.black.opacity(0.05), radius: 10, x: 0, y: 5)
    }
}

2. 瘦身成功的 ContentView.swift

回到主畫面,原本落落長的排版不見了,取而代之的是一行乾淨俐落的 InputCardView 呼叫。

import SwiftUI

struct ContentView: View {
    @StateObject private var controller = HomeController()
    
    var body: some View {
        NavigationView {
            ZStack {
                Color(.systemGray6).edgesIgnoringSafeArea(.all)
                
                VStack(spacing: 25) {
                    // 呼叫拆分出去的子元件,並將狀態綁定進去
                    InputCardView(
                        requestData: $controller.requestData,
                        purposeOptions: controller.purposeOptions
                    )
                    
                    Spacer()
                    
                    Button(action: {
                        controller.submitRequest()
                    }) {
                        Text("產生推薦菜單")
                            .font(.headline)
                            .foregroundColor(.white)
                            .frame(maxWidth: .infinity)
                            .padding()
                            .background(
                                LinearGradient(gradient: Gradient(colors: [Color.blue, Color.purple]), startPoint: .leading, endPoint: .trailing)
                            )
                            .cornerRadius(12)
                            .shadow(color: Color.purple.opacity(0.3), radius: 10, x: 0, y: 5)
                    }
                    .padding(.bottom, 20)
                }
                .padding()
            }
            .navigationTitle("AI 硬體配對")
        }
    }
}

小結

把畫面元件化,是我們對抗「AI 亂改一通」最強大的防禦機制。未來當我們要新增功能時,只要叫 AI 去修改特定的 View 檔案,主畫面跟邏輯層就能安然無恙。

到今天為止,我們前端的「輸入介面」已經完全準備就緒了。但現在我們的 HomeController 按下送出後,只會印出一行 print("準備發送 API 請求..."),這可不行。

明天,我們要在前端把「API Service 層」建立起來,並用 Mock Data (假資料) 先讓畫面動起來,為下週正式串接 n8n 引擎做好暖身!

我們 Day 08 見!


上一篇
Day 06:動嘴寫 Code:用直覺提示詞極速打磨首頁 UI 介面
下一篇
Day 08:串接前夕:設計前端 API Service 層與 Mock Data 測試
系列文
從零打造 AI 全端應用:Vibe Coding 結合 n8n 視覺化工作流實戰 共 17 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言