[Day 2] SwiftUI 與 UiKit 之比較與選擇

前言 在 Day 1 時曾提到,這次要開發的 App,是讓使用者能直接輸入公路的里程數,並即時在地圖上定位。這次我選擇以 SwiftUI 來開發,除了想藉此機會嘗試這個框架外,更重要的是,我認為作為軟體工程師,理解各種技術的優勢與限制,並根據實際需求做出合適的選擇,是非常重要的能力。 技術本身只是工具,沒有絕對的對錯。關鍵在於能否根據專案目標、團隊狀況與未來維護等因素,做出最適合的技術選型,而不是單純因為新穎或流行。 而在 iOS 的原生開發中,主要有 UIKit 與 SwiftUI 兩個框架。SwiftUI 是蘋果在 WWDC 2019 發布的開發框架。以 2025 年的今天來看,它已不算最新技術,但相對於歷史悠久的 UIKit,仍是一個非常年輕的框架。 以下簡單對兩個框架做基本介紹。 SwiftUI 與 UIKit 簡介與比較 雖然本系列文章的重點並非深入比較這兩個框架,但這裡仍簡要說明其主要差異: 命令式語法 & 宣告式語法 命令式(Imperative)語法強調「如何做」:開發者需要明確描述每一步操作,手動管理 UI 狀態與流程。例如在 UIKit 中,必須指定元件的建立、佈局、狀態變更等細節。 宣告式(Declarative)語法則強調「要什麼」:開發者只需描述最終希望呈現的 UI 狀態,框架會自動處理狀態變化與畫面更新。例如 SwiftUI 中,直接描述畫面結構與資料綁定,讓 UI 隨資料自動更新。 我們用例子來說明可能會更清楚,以在畫面上放置一個按鈕,點擊後會增加數量紀錄的計數器為例,兩個框架的寫法相當不同: UIKit SwiftUI 可以看到程式碼的數量有明顯的差異。開發者只需將 count 變數與畫面元件綁定,當資料變動時,框架就會自動為我們處理 UI 更新。這不僅減少了手動操作 UI 的程式碼,也大幅降低了因狀態管理疏忽而出錯的風險。 除此之外,宣告式語法讓開發者的心力能更專注在「畫面應該長什麼樣子」,而不是「如何一步步把畫面變成想要的樣子」。對於我們的里程定位 App 來說,這代表當使用者輸入不同里程、或地圖狀態改變時,我們不必再手動更新標籤、按鈕或地圖標記的狀態,大幅提升了開發效率與程式碼的可維護性。 社群與資源 SwiftUI 看起來很棒對吧,而且每次修改完程式碼,右方會即時渲染。雖然後來 UIKit 也可以達到類似的效果,但我個人覺得還是 SwiftUI 比較方便。不過,SwiftUI 畢竟還年輕,當遇到較複雜或冷門的需求時,社群的資源與成熟的解決方案,仍不如歷史悠久的 UIKit 來得豐富,這點在查找資料時能明顯感受到差異。 性能與相容性 而 SwiftUI 許多新功能都需要較新版本的 iOS 才能使用。例如 NavigationStack(新版的導航結構)、Charts(繪製圖表)等元件必須 iOS 16 以上才支援。 因此若專案需支援較舊的 iOS 版本,SwiftUI 可能會受限,故需特別注意相容性問題。 ...

September 15, 2025 · 1 分鐘

[Day 1] 前言

與 iOS 開發的相遇 大家好,我是個從律師轉職的軟體工程師,踏入這個領域不知不覺也快兩年了。回想當初轉換跑道,一切從自學 Python 開始,接著摸索資料庫、API 等後端技術。後來有幸進入 AppWorks School (現已更名為 AiWorks) 接受 iOS App 開發訓練,最終進入金融業的 IT 部門。 進入公司後,我的工作日常相當「精彩」,除了本行的 iOS 專案開發與維護,還得兼顧後端維運、和 SQL 打交道,甚至管理 Splunk、Kong APIM 這些系統。雖然實際能投入在 iOS 開發的時間不如預期,但我對這塊領域的熱情從未減少。親手打造出一個能看、能用、能互動的應用程式,那種從無到有的創造感與成就感,是其他工作難以比擬的。也因此,剛進公司時,對於無法專注在 iOS 開發上,心裡難免有些失落。但現在回頭看,我相信廣泛接觸不同技術對一個軟體開發者來說絕對是不虧的事情。然而,心中那份對 iOS、特別是 SwiftUI 的渴望,依然強烈。 目前公司的專案仍以 Objective-C 與 UIKit 為主,雖然我曾用 SwiftUI 開發並上架過個人 App,但總覺得學得不夠扎實。這次鐵人賽,對我來說正是一個完美的機會,能強迫自己更多了解 SwiftUI 的世界,將那些零散的知識串連起來。 初入 DevOps 世界 在公司任職期間,我有幸參與了 Azure DevOps 的導入過程。坦白說,起初它對我而言,就只是個「新來的工具」:用 Wiki 寫文件、開 Work Items 追蹤任務、等 Pipeline 編譯出 .ipa 檔,然後透過 Pull Request (PR) 進行程式碼審查。每個環節都得遵循特定流程,像是 Pipeline 還需要主管審核才能部署,有時覺得有些繁瑣。 但隨著時間推移,這些經驗讓我逐漸體會到,DevOps 不僅僅是工具的堆砌,它更是一種文化,一種讓開發、測試與部署流程更順暢、更可靠的思維模式。當我看到一個小小的修改,能自動觸發測試、打包、甚至部署到測試環境,我才真正理解自動化所帶來的安心感。 即使現在是一人開發的個人專案,我也相信導入良好的 DevOps 實踐也是一個不錯的嘗試: 版本控制: 使用 Azure Repos 搭配 Git-flow 策略,讓每一次的程式碼異動都有所依循。 自動化測試: 設定 Pipeline 自動執行單元測試,確保程式碼品質。 持續整合: 每次提交程式碼後自動編譯並檢查。 持續部署: 自動將測試通過的版本部署到 App Store Connect。 這次的系列文章,我將結合 SwiftUI 開發與 DevOps 實踐,從建立基礎專案開始,一步步實現自動化的開發流程。 ...

September 14, 2025 · 1 分鐘

canOpenURL 與 openURL 的那些事

之前因為一直沒有機會去處理到不同 App 之間跳轉的問題,對這塊的東西一直不太熟悉。最近因為遇到 App 不能成功開啟其他 App 的問題,才有機會了解一下。 不能開啟的原因是 App 使用很久以前的 API: 可以看到在 iOS 10 以後就被蘋果棄用了,然而蘋果棄用通常並不表示馬上不能用,所以這支 API 還活了很久一段時間,直到 iOS 18 蘋果才真正不給用。這支 API 已經被 open(_:options:completionHandler:) 取代(Objective-C 為 openURL:options:completionHandler:): 雖然這個問題解決了,但我想藉此機會多了解一下跳轉這個機制。 要實現 App 能夠跳轉,例如 20250829_B 這個 App 要能夠讓被其他 App 開啟,就必須要去 target 設定裡的 Info 分頁,在 URL Type 裡面替自己定義 URL Schemes,這裡我們定義為 AppB。 接著,我們在另一個 App A 裡建立一個點擊會實現跳轉 AppB 的按鈕 程式碼如下: @IBAction func jumpToB(_ sender: Any) { let scheme = "AppB://" guard let url = URL(string: scheme) else { return } UIApplication.shared.open(url, options: [:]) { success in } } 我們僅實作 open(_:options:completionHandler:),而不實作 canOpenURL(_:) 這支很常被使用的 API。 ...

August 29, 2025 · 1 分鐘

Safari Web Inspector 流量攔截延遲現象

前言 最近遇到一個需求,我們的 mobile app 需要用 web view 開啟某個網頁。因為該網頁的認證方式最近有調整,app 端也得跟著修改。也因此這次 iOS 也要從原本的 SFSafariViewController 換成 MKWebView 來實作內嵌網頁,結果問題就這樣冒出來了。 過程 事情大概是這樣: 當我在頁面上點擊某個按鈕,會跳轉到另一個網頁,然後這個新頁面會自動彈出一個視窗到最前面。奇怪的是,首頁本身載入沒問題,如果在首頁觸發彈窗也都正常。但只要是跳轉後的頁面,彈窗就會出狀況。 一開始我懷疑是 cookie 沒有正確傳遞,或是 header 有缺漏,所以重點都放在這些地方。用 Safari Web Inspector 看 network,也沒看到什麼明顯的錯誤,只是覺得很怪,怎麼 request、response 的紀錄都不太完整,像是 header 竟然是空的。當下還以為這就是正常現象,只好很麻煩地回到 app 裡面用 NSLog 確認內容。 後來不知道哪根筋突然不對,想說試試看手動按一下重新整理。沒想到這一按,network 頁面所有的請求紀錄都跑出來了,header、response 什麼資訊都有。這時才發現,原來是某個 JS 檔在執行時出錯。仔細追查才知道,是網頁開發者寫的「取得使用者瀏覽器類型」的 function 沒有考慮到 MKWebView 發出的 User-Agent,導致相關 UI 無法正常顯示。 那段程式碼還註解「若非不得已,盡量少用此方法」,是一個拿 14 年前 stackoverflow 上的解答的函式XD User-Agent:WKWebView 預設的 User-Agent 與 Safari 或 SFSafariViewController 不同,若網頁端有依賴 User-Agent 進行判斷,需特別注意。 關於 Safari Web Inspector 的觀察 這次讓我發現,Safari Web Inspector 其實有個不太直覺的地方。你必須先在手機上打開那個網頁,Inspector 的 develop 頁籤才會出現該網頁的選項。也就是說,當你點進去看的時候,網頁內容已經載入完成了,Inspector 只會顯示最後的狀態。過程中的流量、請求紀錄根本沒被捕捉到,除非你手動重新整理一次,才會看到完整的 network 資訊。 ...

July 11, 2025 · 1 分鐘

Azure Pipeline 編譯 iOS App Groups 權限未簽入問題

前言 公司原本的上線流程最近要走 Azure DevOps 方式上線,我維護的 iOS App 不外乎也要走這個流程。原本測試都沒有太大問題,但上線之後發現 App Groups 功能沒有正確運作,就開始查找問題,最終找到問題所在並解決,記錄一下過程,並延伸相關知識。 Trouble Shooting 一開始覺得很奇怪,這次上線的內容並沒有異動到 App Group 的設定,所以應該跟 source code 沒有太大關係。後來先嘗試在本機 build 一版,結果發現透過這個方式運作正常,但透過 Azure pipeline build 出來的就會發生問題。因此轉向研究 pipeline yaml 的設定是否出現狀況。 先進一步確認透過 Azure pipeline 包出來的 artifacts 內容是否正確,一般而言,iOS App 打包後的成品如下: DistributionSummary.plist ExportOptions.plist YourAppName.ipa Packaging.log 查看 .ipa 我們可以查看 YourAppName.ipa 裡面到底簽入了哪些權限: 解壓縮 .ipa 檔案: unzip YourAppName.ipa -d AppContents 進入 Payload 目錄: cd AppContents/Payload 執行 codesign 指令: codesign -d --entitlements :- YourAppName.app 每個部分的含義是: codesign:呼叫程式碼簽署工具 d:表示 “display”,用於顯示簽署資訊 -entitlements :-:要求工具顯示應用程式中包含的所有權限(entitlements) 這裡的 :- 是一個特殊的語法,表示將輸出導向到標準輸出(stdout) YourAppName.app:要檢查的應用程式套件路徑 如果有正確簽入 App Groups 的權限,應該要有 <key>com.apple.security.application-groups</key> 的蹤跡,但發現透過 Azure pipeline 包出來 .ipa 沒有。 ...

July 3, 2025 · 2 分鐘