最近「Vibe Coding」這個詞在開發者社群裡吵得沸沸揚揚——與其一行一行敲鍵盤,不如用自然語言描述你想要的功能,讓 AI 幫你實現。聽起來很美好,可是這招真的能拿來開發生產級別的應用嗎?還是只是寫寫玩具專案用的呢?
我想與其空想,不如自己動手驗證一下。於是我用 Claude Code 加上 Claude 4.5 Opus,從零開始打造了一款完整的 iOS 應用程式:AlphaGPS——一個透過藍牙低功耗(BLE)把 iPhone 的 GPS 資料同步到 Sony 相機的小工具。
值得一提的是:我從來沒寫過任何 Swift 程式。雖然我以前有 Objective-C 的 iOS 開發經驗,但 Swift 和現代 iOS 那一整套框架對我來說根本是全新的領域——SwiftUI、Combine、async/await、Live Activity,這些我全都是第一次碰。
而且這實在不是個簡單的 Hello World 專案。它牽涉到 BLE 硬體協議、iOS 背景執行限制、頗複雜的狀態管理,還有 Live Activity 整合。最後的成果呢?大概 4,100 行 Swift 程式碼、30 個檔案、90 個 commits,以及一個真的能用的產品。
為什麼選擇這個專案?
身為一個攝影愛好者,我一直很希望照片能自動記下拍攝的地點。Sony 雖然有官方的 Creators App,可是它的背景同步體驗實在頗遜——三不五時就要手動重連,而且還會把我其他的藍牙連線給踢掉。
我真正想要的其實很單純:打開相機就自動連上、App 縮到背景也能持續同步、整個過程完全不用我動手。
這個需求剛好涵蓋了一堆有趣的技術挑戰,拿來測試 AI 輔助開發的極限,真是再適合不過了。
Vibe Coding 的實際體驗
第一天(1/11):從協議規格到基礎架構
第一個 commit 是 Initial commit with Sony GPS protocol spec——我先把逆向工程得到的 Sony 藍牙 GPS 協議文件丟進去,然後就直接跟 Claude 說:「根據這份協議規格,幫我設計一個 iOS App 吧。」
接下來的兩個 commits 分別是:
feat(phase1): implement iOS app foundation for Sony camera GPS syncfeat(phase2): implement BLE foundation and GPS encoding
Claude 建議使用 Multi-Manager Singleton 模式搭配 Combine 響應式框架:
「你的 App 需要協調多個系統:藍牙掃描、GPS 定位、相機連線狀態。
建議使用獨立的 Manager 單例,各自管理 @Published 狀態,
透過 Combine 讓 UI 自動響應變化。」
這可不是 Claude 隨便唬爛的建議喔——它是真的考慮到 CoreBluetooth 的 Delegate 模式和 SwiftUI 的響應式特性,照著 iOS 開發的最佳實踐給出來的。
第二到第三天(1/12-1/13):攻克背景執行
iOS 對背景執行的限制嚴格得不得了,大多數 App 一縮到背景沒多久就被系統砍掉了。可是我要的偏偏是:就算 App 整個被殺掉,相機重新開機的時候也得乖乖自動重連。
這部分有多硬,看 commit 歷史就知道了:
feat(phase4): implement background lifecycle and persistenceImplement iOS background BLE state preservation and cleanup codeStart scanning in background when cameras exist but none connectedStart scanning when last connected camera disconnects in backgroundAbort connecting cameras when app enters backgroundStop scanning when camera connects in background
這時候就輪到 iOS Core Bluetooth State Preservation 登場了。我把需求跟 Claude 講清楚:
「我需要相機關機後再開機時自動重連,
即使使用者已經完全關閉 App。」
Claude 不只丟程式碼給我,還順便把整個機制講解了一遍:
- 用
CBCentralManagerOptionRestoreIdentifierKey初始化藍牙管理器 - iOS 會在背景記住連線意圖
- 當藍牙事件發生時,iOS 會喚醒 App 並呼叫
willRestoreState - App 需要在這個回調中重建狀態並恢復連線
最難搞的還是那些邊界情況:藍牙 UUID 可能會變、相機可能同時連好幾台、狀態恢復還可能搶在位置權限授權之前發生。這些 Claude 都幫我一個一個想過,最後才真的做出那種「完全不用碰它」的體驗。
Sony 的 BLE 協議
Sony 從來沒公開過他們的藍牙 GPS 同步協議。還好我在網路上找到了其他開發者的逆向工程成果(特別是 camera-gps-link 這個專案),人家早就把協議細節摸得一清二楚。我把整理過後的協議規格放在 docs/sony-gps-protocol-spec.md。
協議的關鍵在這:要寫入 GPS 資料,得先跑一個三步驟的「解鎖」序列:
- 寫入解鎖指令到 DD30 characteristic
- 寫入鎖定指令到 DD31 characteristic
- 寫入 GPS 封包到 DD11 characteristic(91 或 95 bytes)
只要漏掉任何一步,相機就會默默把資料吃掉當沒這回事——連個錯誤都不會回給你,超難 debug。
我把這個發現告訴 Claude 之後,它馬上就抓到了非同步寫入的眉角:
// Claude 建議的解決方案
private var unlockWriteComplete = false
private var lockWriteComplete = false
// 只有在兩個階段都完成後才發送 GPS
func checkAndWriteLocation() {
guard unlockWriteComplete, lockWriteComplete else { return }
// 發送 GPS 封包...
}這種狀態追蹤的寫法,剛好解決了 BLE 寫入回調可能亂序、甚至直接漏掉的問題。
印象深刻的地方
1. 理解上下文的能力
1/13 那天,我花了不少時間在搞 Live Activity。從 commit 歷史就看得出這段路走得不太順:
Implement Live Activity for camera connection statusFix iOS availability checks for Live Activity APIFix Widget Extension bundle configurationFix ActivityViewContext build error by inlining Live Activity view
後來到了 1/16,我才發現「這個 Live Activity 斷線的時候沒有正確收掉」。Claude 沒有只是傻傻地搜關鍵字,而是把整個程式碼流程追了一遍,最後發現問題其實出在狀態轉換的時序上:
didConnect → 狀態設為 .connected → 啟動 Live Activity
↓
handleConnection → 狀態改為 .connecting(進行服務發現)
↓
didDisconnect → wasConnected 檢查回傳 false → Live Activity 沒有結束!
修好的結果就是 fix(ble): always end Live Activity on disconnect 這個 commit。AI 能理解到這種程度,除錯速度真的快了不少。
2. 主動考慮邊界情況
在實作電池優化時,Claude 主動提出:
「你需要考慮這些情況:
- App 在背景但沒有相機連線 → 應該暫停 GPS
- 相機在背景連線 → 立即恢復 GPS
- 使用者關閉背景同步設定 → 停止所有更新」
這最後變成 feat(location): stop GPS updates when no cameras connected in background 這個 commit。說真的,這些情況我根本沒明講過,可是它對產品品質實在是很關鍵呢。
3. 架構一致性
專案一路長到 30 個 Swift 檔案,Claude 卻始終記得我們一開始定下的那套模式:Manager 單例、原子性狀態更新、Combine 訂閱管理。它不會突然冒出一個風格完全不一樣的實作來嚇你。
看看下面這幾個 refactor commits,Claude 一邊幫我持續改進架構,一邊又沒把一致性給搞砸:
Refactor to single camera list and state-based architectureConsolidate Camera discovering and connecting statesRefactor autoConnect to separate field from connection stateConsolidate connected and syncing states into single connected staterefactor: simplify connecting state by removing retryCount
不完美的地方
不過老實講,Vibe Coding 也不是萬能的啦。從那 90 個 commits 裡,你會看到一堆 fix commit:
Bug fixes 佔的比例真的不小:
fix(ble): clear peripheral when aborting connectionfix(ble): cancel connection timeout on disconnectfix(ble): fix critical disconnect handling issuesfix(ble): clear stale peripheral on background disconnect
這些清一色都是實機測試才抓到的。Claude 寫得出結構漂亮的程式碼,可是 BLE 跟 iOS 背景執行的邊界情況實在太多了,不真的跑一遍裝置根本發現不了。
得把話講白:Claude 偶爾會想太多、過度工程化。我得明白地跟它說「保持簡單就好,別自己加料」,它才會收斂。
測試還是很麻煩:BLE 跟 Live Activity 在模擬器裡根本測不了,每個功能我還是得乖乖在實機上一個一個手動試過。
領域知識少不了:程式碼是 Claude 幫我寫的沒錯,但要搞懂 Sony 協議、iOS 背景限制、BLE 的各種特性——這些終究得我自己去查、去讀。AI 是個超強的副駕駛,可是方向盤往哪轉,還是得你自己心裡有底才行。
成果總覽
| 指標 | 數值 |
|---|---|
| 程式碼行數 | ~4,100 行 |
| Swift 檔案數量 | 30 |
| Git commits | 90 |
| 開發時間 | 6 天(兼職,1/11 - 1/16) |
| 手動寫的程式碼 | < 5% |
功能清單:
- 自動掃描和連接 Sony 相機
- 背景 GPS 同步(App 在背景時持續運作)
- Live Activity 即時顯示同步狀態
- 相機斷線自動重連
- 電池優化的智慧掃描
- 支援 iOS 15.0+,完整功能需 iOS 16.1+
我學到的事
-
Vibe Coding 是玩真的:對有經驗的開發者來說,AI 真的能把生產力拉高個 5 到 10 倍。不過關鍵在於——你得有能力驗證、有能力引導它的輸出,不然一切都是空談。
-
你怎麼問,它就怎麼答:指示含糊,拿到的就是含糊的程式碼。把需求、限制、邊界情況講得越具體,Claude 就越能端出專業級的解法。
-
Opus 4.5 的差別:跟之前的模型比起來,Opus 4.5 在理解複雜系統、維持上下文一致性、主動想到邊界情況這幾點上,進步真的很明顯。
-
它不是拿來取代學習的:AI 讓你更快把想法做出來沒錯,可是底層技術你還是得懂。不然 AI 給的建議到底對不對,你連判斷的底氣都沒有。
寫在最後
AlphaGPS 現在已經是我天天在用的工具了。每次出門拍照,相機一開機就自動連上、照片自動標好位置,全程連手機都不用碰一下,真是頗爽。
回頭看這整個專案,我覺得最有意思的其實不是程式碼本身,而是這種全新的開發方式:我只要專心想「我要什麼」跟「為什麼要」,「怎麼做」那些細節就交給 Claude 去傷腦筋。
我想這大概就是 Vibe Coding 的精髓吧——它不是要把你取代掉,而是讓你能把力氣花在真正重要的事情上。
如果你也想試試,我的建議是:先找一個你是真心想解決的問題,然後就去跟 Claude 聊聊看。我猜你大概會被它能帶你走多遠給嚇到喔。
這篇文章本身也是用 Claude 協助撰寫的——用 AI 寫一篇關於 AI 開發的文章,似乎也是剛好而已。