iT邦幫忙

2026 iThome 鐵人賽

DAY 11
0
Vibe Coding

神隊友.swift:30 天 Vibe Coding 打造育兒 iOS App 系列 第 11 篇

[Day 11] 一句話存成一筆紀錄:三條規則的解析,和 migration 要寫幾段

  • 分享至 

  • xImage
  •  

Day 11

文章同步發表在我的個人 Blog

一句話存成一筆紀錄:三條規則的解析,和 migration 要寫幾段

中秋節快樂!白天在家裡,能坐下來的時間比平常少,這篇的密度也比前幾天低。

昨天寫完架構文件,今天照規格做第一個功能:一句話進來,存成一筆事件。第一個做記錄不做同步,理由是 Day 9 排順序時寫過的,記錄這件事如果做不順,後面全部都沒有意義。

先講結果:這條路通了,build 成功,44 個單元測試全過。但解析只有三條關鍵字規則,第一次測試就判錯。我打「尿布快沒了」,它分到「紀錄」,實際上那是待辦。

1. 做出來長什麼樣

輸入畫面

首頁只有一份清單和底部的輸入框。輸入「尿布快沒了」按解析:

確認卡

卡片上有三個地方是照架構文件做的:

  • 原文保留在最上面,不管解析對不對,你講的那句話都留著
  • 候選分類老實標示推測,規則猜的是「紀錄」,我改成「待辦」,畫面上仍然寫著「候選分類:紀錄(推測)」,它不假裝自己猜對了
  • 時間不自動換算,寫「明天下午」就存「明天下午」,不會變成某月某日下午三點,下面標著「時間未解析,可自行填寫」

確認儲存之後:

存好的清單

一張卡,待辦標籤、內容、時間,下面小字寫著原文。關掉 App 再打開,還在。

2. 為什麼它會猜錯

解析的全部內容就是這三條:

規則 結果
結尾是「嗎」「嗎?」「?」「?」 請求
「買」或「記得」開頭 待辦
其他全部 紀錄(標示為推測)

沒有 AI、沒有模型、沒有時間解析。內容就是整句話,時間留空讓人自己填。

所以「尿布快沒了」歸類到第三項,因為它不是「買」開頭、結尾也沒有「嗎」。

這是 Day 9 定的「規則為主、模型為輔」的第一步:先做一個每支 iPhone 都能跑的版本,不綁裝置端模型。

代價是準確度就會不好,每記一筆都可能要停下來改一次分類。確認卡本來是為了避免存錯東西,可是如果每次都要改,記一件事反而變慢,而讓記錄變快本來是這個 App 要解決的第一件事。這個問題今天還沒解。

3. 讀 AI 寫的程式碼,要先看懂什麼

我平常寫 Laravel,SwiftData 的資料模型是這次第一次操作。對照起來大概是這樣:

Laravel SwiftData 差別
Eloquent Model @Model 標記的 class 欄位直接寫成屬性,不用 $fillable
migration 檔 VersionedSchema + SchemaMigrationPlan 不是資料夾裡的檔案,是程式碼裡宣告的版本
DB::table() 或 Model::query() @Query 寫在畫面上,資料變了畫面自己更新
Eloquent 的連線設定 ModelContainer App 啟動時建立,決定資料存在哪
交易與 save() ModelContext 新增、刪除、儲存都走它

這次做出來的事件模型:

@Model
nonisolated final class Event {
    @Attribute(.unique) var id: UUID
    var rawText: String
    var kindRawValue: String
    var content: String
    var timeText: String?
    var createdAt: Date
    var child: Child?   // nil is a valid answer, not a missing value
}

有三個地方跟 Laravel 的習慣不一樣。

種類存成字串,不直接存 enum。 kindRawValue 是 String,讀出來才轉回 enum。這樣之後同步從另一支手機拉到這台還不認得的種類,只會讓那一欄轉不出來,整筆資料還讀得到。

nonisolated 和 final。 Swift 6 的嚴格並行下,資料模型不能隨便跨執行緒傳遞,這兩個關鍵字是在標明它的界線。這是 Laravel 不會遇到的問題。

migration 要寫幾段,看你改什麼。 Laravel 改欄位是在資料夾裡新增一個帶時間戳的 migration 檔;SwiftData 是直接改 class,再看情況補上版本說明:

改什麼 要寫幾段
加一個 optional 欄位 不用寫,框架自動處理
加有預設值的欄位 不用寫
改名、拆欄位、語意變了 三段:舊版 schema、新版 schema、轉換的 plan

這次加「關於哪個小孩」那個欄位是 optional,所以一段都沒寫。真正要寫三段的場合不多,例如之後把 timeText 從字串改成日期才會用到。

還有一個跟 Laravel 不同的地方:Laravel 的 migration 有 up() 和 down() 可以回滾,SwiftData 只能往前,版本順序一旦發布就不能改。

這裡設立一條規則:改欄位不可以靠刪掉 App 重來。

現在比較沒關係,都是假資料。等家人手機上有兩週的紀錄,刪下去就回不來。這條特別容易犯,因為免費帳號每七天本來就要重新安裝一次,續簽跟刪掉重來在手上的動作很像。

4. 驗收看了什麼

項目 結果
build 成功
單元測試 44 個,全過
UI 測試 1 個,過
靜態掃描 Persistence 以外沒有 import SwiftData、@Query、modelContext
log 檢查 沒有 print、Logger,照護內容不寫進 log
網路檢查 沒有 Speech、URLSession、Network 呼叫

模擬器上實際跑過:不輸入直接按解析會擋下來、取消不會存、內容改成空白會出欄位錯誤、確認之後清單出現、關掉重開還在。

有四種情況在模擬器上做不出來:

沒測到的 為什麼做不出來
儲存失敗 要磁碟滿了才會發生,沒辦法把模擬器的磁碟塞滿
清單讀不出來 要資料庫損毀
資料庫開不起來 同上
儲存中連點兩下 按鈕在儲存期間已經鎖住,點不到第二下

這四種改成在單元測試裡用一個假的儲存元件,叫它固定回傳失敗,檢查畫面有沒有正確顯示錯誤、有沒有把使用者剛打的內容留著。

這張表是驗收紀錄裡它自己補上的,我沒要求。標題叫「未在模擬器上操作的項目」。

值得記下來的是這個做法:測不到的東西要講清楚為什麼測不到,而不是假裝測過。

明天

Day 12 做 M2 的交接卡:把累積的紀錄整理成一段文字,補充之後分享出去,對方不裝 App 也讀得懂。


上一篇
[Day 10] 架構文件:免費 Apple ID 能做到哪裡
系列文
神隊友.swift:30 天 Vibe Coding 打造育兒 iOS App 共 11 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言