iT邦幫忙

2026 iThome 鐵人賽

DAY 16
0
Vibe Coding

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

[Day 16] iOS 26 confirmationDialog 行為變更:從來源按鈕彈出,XCUITest 要跟著調整

  • 分享至 

  • xImage
  •  

Day 16

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

iOS 26 confirmationDialog 行為變更:從來源按鈕彈出,XCUITest 要跟著調整

SwiftUI 的 confirmationDialog 一行程式都沒改,到了 iOS 26,出現的位置卻變了。

Day 15 預告今天要裝到兩支 iPhone。裝機前,先把模擬器上用起來不順的地方調整好:卡片可以編輯和刪除,首頁多了一區「之後」,7 天以後的事也看得到。裝機改到 Day 18。

首頁新增的「之後」區:7 天以後的事也看得到,一樣標出對方知道了沒有

先講結果:兩份規格共 14 項任務全部完成,App 測試從 122 個增加到 142 個,verify.sh 通過。

今天的技術主軸在 Day 7 學習地圖的第二層 SwiftUI:iOS 26 的確認對話框換了呈現方式,畫面和 UI 測試都要跟著調整。

1. 規格先寫,AI 再動手

Day 7 定下「Spectra 寫規格、Claude Code 實作」的分工,Day 8 在第一版的 repo 初始化過 Spectra;這次在 v2 的 repo 也建立起來:

spectra init --tools claude          # openspec/ + Claude Code skills
spectra new change <change-name>     # one folder per change
spectra analyze <change-name>        # consistency check
spectra validate <change-name>       # format check

一個 change 由四份文件組成,照順序寫:

文件 寫什麼 不寫什麼
proposal 為什麼要改、改哪些、不做哪些 實作細節
spec 每一條需求和情境(WHEN/THEN) 怎麼寫程式
design 技術上怎麼做、為什麼選這個做法 行號、逐行步驟
tasks 可以打勾的任務,每項寫明怎麼驗證 「修改某檔案」這種沒有驗收的任務

每一條情境都給一個驗收編號,之後測試名稱直接帶這個編號,格式是 test_AC_<功能>_<編號>_<情境>。規格裡的情境和測試一對一,AI 做完有沒有漏,對編號就知道。

寫規格的過程中,Claude Code 會把需求沒講清楚的地方整理成選擇題問我。今天我總共回答了七題,都是「誰可以做」「做了之後另一半那邊會發生什麼事」這一類;這些 AI 不能自己假設,專案規則寫明有疑義就要停下來問。

2. 規格要對到程式碼,才寫得出情境

規格寫到一半,出現了一個問題。

Claude Code 一開始跟我說,從首頁「+」丟的事和從行事曆建立的行程是兩種資料,所以權限可以分開設定;我照這個說法先回答了權限相關的題目。等到要寫「對方可不可以改」的情境時,它得把每一條規則對到實際的資料欄位和安全規則,才發現兩個入口建立的其實是同一種資料,存在同一個地方,程式碼裡沒辦法分辨是從哪裡建立的。

前提錯了,前面的答案也就不成立。所以 Claude Code 把更正寫進問題紀錄,重新問我一次,這次我選了跟現有行為一致的做法:安全規則不需要修改,既有的測試也不用動。

照錯的前提寫出來的第一版規格裡,排了修改安全規則、改寫一條既有測試的任務;如果跳過規格直接實作,這些改動會直接進到程式碼。規格這一步的用處,就是讓 AI 在動手前先把需求對到程式碼,錯的前提在文件階段就會被發現。

3. 分析工具抓出「需求沒有對應的任務」

第一份規格的四份文件寫完,spectra validate 格式檢查通過;接著跑 spectra analyze,出現 8 個 Warning:

Requirement '…' has no matching task
Design topic '…' not referenced in tasks

前 4 個是 spec 裡的需求,在 tasks 裡找不到對應的任務;後 4 個是 design 裡的技術決定,tasks 沒有提到。任務其實都有寫,只是沒有標出它在實作哪一條需求。

於是在每一項任務後面補上對應的需求與設計名稱:

- [ ] 1.2 ...(Requirement: <name>;Design: <topic>)

再跑一次 spectra analyze,Warning 歸零。這個對照讓實作的 AI 看到任務時,知道要回頭讀哪一段規格。

4. iOS 26 的確認對話框,改從按鈕長出來

刪除一筆資料前要跳出確認,SwiftUI 用 confirmationDialog。iOS 18 以前,它在 iPhone 上是從畫面底部升起的 action sheet;iOS 26 改成從觸發它的元件長出來。Apple 的 Liquid Glass 採用指南寫得很清楚:

An action sheet originates from the element that initiates the action, instead of from the bottom edge of the display.

同一份指南也提醒要指定來源:

Specify the source of an action sheet. Position an action sheet's anchor next to the control it originates from.

在 SwiftUI 裡,「來源」就是 .confirmationDialog 掛在哪一個 view 上。Claude Code 第一次把它掛在整個詳情頁的內容上,對話框就從畫面中間長出來,箭頭指向內容正中央。改掛到工具列的「更多」按鈕之後,對話框就從按鈕旁邊出現:

Menu { /* edit, delete */ } label: { Image(systemName: "ellipsis") }
    .confirmationDialog("delete.title", isPresented: $isConfirming, titleVisibility: .visible) {
        Button("delete.confirm", role: .destructive, action: delete)
        Button("delete.keep", role: .cancel) {}
    }

左:詳情頁左上角的「更多」按鈕;右:iOS 26 的刪除確認從同一個按鈕長出來,不是從畫面底部升起

另外一個差異:在模擬器上實際看到的 popover 樣式沒有顯示取消按鈕,點對話框外面就是取消。

5. UI 測試也要跟著改

呈現方式一變,原本的 UI 測試寫法就找不到按鈕:

  • 確定按鈕在 XCUITest 的元素樹裡巢狀出現兩層,identifier 相同,直接 tap() 會因為找到多個符合的元素(Multiple matching elements)而失敗。
  • 沒有取消按鈕可以點,要點對話框外面的區域。

Claude Code 的改法:

// popover nests the button twice with the same identifier
app.buttons.matching(identifier: "deleteConfirm").firstMatch.tap()

// cancel = tap outside the popover
app.otherElements["PopoverDismissRegion"].firstMatch.tap()

PopoverDismissRegion 是 popover 外圍那塊區域在元素樹裡的名稱,UI 測試點它,效果跟使用者點外面一樣。

6. 先寫測試,會遇到的兩個狀況

這次照規格要求先寫測試、確認紅燈,再寫實作。Claude Code 回報了兩個實際遇到的狀況。

第一個:同一個 test target 裡,只要有一個測試檔編譯不過,整個 target 的測試都跑不起來。Swift 的測試 target 是一整個 module 一起編譯,所以「先寫測試」在 Swift 裡的紅燈,常常是編譯錯誤,不是測試失敗。兩個 change 的測試放在同一個 target,只好先把其中一個的實作補齊,另一個的 UI 測試才跑得起來。

第二個:刪除成功後,畫面會閃一下「找不到這件事」。原因是 Firestore 的即時監聽先把資料從串流移除,詳情頁還沒關,畫面就先重畫成找不到。解法是 ViewModel 在刪除前留一份快照,關閉前都顯示快照:

var displayedCard: Card? { card ?? cardBeingDeleted }

7. 目前的狀態

項目 結果
規格 2 份,共 14 項任務,全部完成
TeammateKit 測試 48 個
安全規則測試 79 個,沒有修改
App 測試 142 個(新增 20 個)
verify.sh VERIFY: PASS
截圖審查 Light、Dark、大字級都截圖自審,沒有 P0、P1

還沒做的部分:

  • 裝到兩支 iPhone(開發者模式、跟 Mac 配對、用腳本裝機),改到 Day 18

明天

Day 17 看 Apple 官方的 iPhone Duo 影片與工作坊,整理官方資源、設計原意,評估神隊友要怎麼適配。

參考資源

官方文件


上一篇
[Day 15] Security Rules 擋得住 App,擋不住管理者:建 Firebase 正式專案踩到的三件事
系列文
神隊友.swift:30 天 Vibe Coding 打造育兒 iOS App 共 16 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言