iT邦幫忙

2026 iThome 鐵人賽

DAY 26
0
Vibe Coding

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

[Day 26] 雙十國慶日,簽章到期之後重新安裝:覆蓋安裝為什麼沒續期、Xcode 帳號掉了,以及 Google 授權畫面上的 OAuth

  • 分享至 

  • xImage
  •  

Day 26

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

雙十國慶日,簽章到期之後重新安裝:覆蓋安裝為什麼沒續期、Xcode 帳號掉了,以及 Google 授權畫面上的 OAuth

簽章到期之後,手機上的 App 還打得開嗎?重新安裝,為什麼還是失敗?

今天是雙十國慶日,中華民國生日快樂。昨天 Day 25 把匿名帳號綁定了 Google,今天遇到免費帳號必經的一關:簽章 7 天到期,要重新安裝。另外把昨天按下「綁定 Google」之後跳出來的授權畫面看懂,那一頁背後就是 OAuth。

先講結果:簽章是 10/9 下午 2:33 到期的,但之前四次重新安裝,用的都是 10/2 建立的同一份 profile,覆蓋安裝本來就不會延長期限。今天重新安裝時又遇到兩個錯誤:Xcode 裡沒有登入 Apple ID,以及 Mac 找不到太太的手機。在 Xcode 重新登入 Apple ID、兩支手機都接上 Mac 之後,重新安裝在兩支手機都成功,新的 profile 是 10/10 建立,10/17 到期。

1. 簽章到期,期限可以從檔案裡讀出來

免費的 Personal Team 簽章有效 7 天。要知道實際的期限,不用猜,可以直接讀 App 裡的 provisioning profile:

security cms -D -i Teammate.app/embedded.mobileprovision > profile.plist
plutil -extract ExpirationDate raw profile.plist

這個專案的 profile 內容:

欄位 值
名稱 iOS Team Provisioning Profile: com.gooliya.teammate
建立 2026-10-02 06:33:34Z
到期 2026-10-09 06:33:34Z(台灣時間 10/9 下午 2:33)
有效天數(TimeToLive) 7
允許安裝的裝置 2 支
entitlements application-identifier、get-task-allow、keychain-access-groups 等

到期時間是 profile 建立時就決定的,跟 App 有沒有被重新安裝無關。

2. 覆蓋安裝為什麼沒有續期

重新安裝腳本的紀錄裡,過去幾天的安裝時間是:

時間 動作
10/3 19:57 重新安裝
10/3 22:02 重新安裝
10/7 22:29 重新安裝
10/9 00:45 重新安裝

四次安裝,但 Xcode 存 profile 的資料夾裡,到 10/10 上午為止,這個專案只有兩份 profile(App 本體與分享擴充各一份),建立時間都是 10/2。也就是說,每一次安裝只是用同一份 profile 重新簽章,沒有產生新的。這個資料夾在今天重新安裝開始的同一分鐘被清空了,我沒有動它;看起來是 Xcode 發現 profile 已經過期,把它清掉。

所以「每隔幾天重新安裝一次」不會讓簽章保持有效,要等 profile 過期,Xcode 才會重新向 Apple 要一份新的。是否可以不等過期、提前換新,我還沒有驗證。

3. 自動排程為什麼沒有救到

專案裡有一個 launchd 排程範本:每週三、週日凌晨 3:30 自動執行重新安裝腳本,檔案開頭寫「間隔 < 7 天」。我去檢查 ~/Library/LaunchAgents/,裡面沒有這個檔案,launchctl list 也找不到,所以重新安裝一直是手動做的。

就算排程裝了,也有同一個問題:它每 3、4 天重新安裝一次,用的還是同一份 profile,到期那一刻照樣失效。所以排程要解決的其實是「過期後自動換新」,不是「定期重新安裝」。

4. 今天的重新安裝

先看 scripts/reinstall-devices.sh 對每支手機做的事:

  1. 用 xcrun devicectl list devices 把 Config/devices.txt 裡的識別碼換成硬體 UDID。
  2. 用 xcodebuild 建置 Release 版本,指定那支裝置為目的地,並帶上兩個旗標:-allowProvisioningUpdates 讓 Xcode 可以更新 profile,-allowProvisioningDeviceRegistration 讓 Xcode 可以把裝置註冊進 profile。
  3. 用 xcrun devicectl device install app 安裝到手機。
  4. 任何一支失敗,跳出系統通知並以失敗結束。

今天跑出兩個不同的錯誤。

你的手機:Xcode 沒有帳號

error: No Accounts: Add a new account in Accounts settings.
error: No profiles for 'com.gooliya.teammate' were found
** BUILD FAILED **

-allowProvisioningUpdates 要有帳號才能向 Apple 要新的 profile。Xcode 的 Settings → Accounts 裡沒有 Apple ID,所以建置失敗。這是 profile 過期後才會暴露的問題:過期之前,Xcode 用現成的 profile 就能簽章,用不到帳號。

太太的手機:Mac 找不到裝置

xcodebuild: error: Unable to find a destination matching the provided destination specifier
Available destinations for the "Teammate" scheme:

列出來的可用裝置裡只有我的手機,沒有她的。腳本的錯誤訊息提示三件事:同一個 Wi-Fi、已解鎖、開發者模式已開。

處理結果

在 Xcode 的 Settings → Accounts 重新登入 Apple ID,兩支手機都接上 Mac 之後重新執行腳本,兩支都安裝成功。新的 profile 是 Xcode 登入後,由 -allowProvisioningUpdates 向 Apple 要來的,有效期從今天重新算 7 天。

5. 簽章跟登入有什麼關係

簽章過期,除了 App 打不開,還會影響登入狀態嗎?兩個地方要看:

  • Firebase 的登入狀態存在 Keychain(ADR-002 寫的)。
  • Google SDK 的文件寫明:App 必須用 Apple 核發的憑證簽章,才能把憑證存進 Keychain。

profile 的 entitlements 裡有 keychain-access-groups,這就是簽章和 Keychain 的連結。覆蓋安裝同一個 App ID,Keychain 的資料理論上會保留,所以重新安裝之後逐項檢查:

檢查 為什麼
App 能不能開啟 簽章過期時 iOS 不會執行這個 App
設定頁的帳號區是不是「已綁定 Google」 登入狀態存在 Keychain
家庭與卡片還在不在 資料在 Firebase,不在手機
登出再用 Google 登入 確認 userHouseholds 對照還在,找得回家庭

重新安裝之後的實測結果:

手機 App 能開啟 登入狀態
我的手機 正常 這次沒有逐項檢查
太太的手機 正常 帳號區仍是已綁定 Google

覆蓋安裝同一個 App ID,Keychain 裡的登入狀態有保留,這一點在太太的手機上確認。我的手機只確認 App 能開啟,「登出再用 Google 登入」這項今天沒有測。

6. 授權畫面上的 OAuth

昨天按下「綁定 Google」之後,跳出來的是這個畫面(email 已遮蓋):

Google 授權畫面

先把從按下按鈕到回到 App 的過程拆開:

步驟 誰做 對應 Day 25 的哪一個設定或程式
1. 準備設定 App GIDConfiguration(clientID:),client id 從 Firebase 的設定讀
2. 打開登入畫面 SDK 系統的網頁驗證視窗(ASWebAuthenticationSession)開啟 accounts.google.com,就是畫面上方的網址
3. 授權 使用者 選帳號、看權限、按「繼續」
4. 回到 App 系統 Google 導向 redirect_uri,系統依 URL scheme 把網址交給 App,onOpenURL 再交給 GIDSignIn.handle
5. 換 token SDK 依官方文件的流程,用 authorization code 換 access token 與 ID token
6. 交給 Firebase App GoogleAuthProvider.credential 組好憑證,link 或 signIn

畫面上有三件事,各自對應 OAuth 的一個概念。

權限(scope)。 畫面只要求兩項:名稱與個人資料相片、電子郵件地址,對應 openid、email、profile 三個基本身份權限。官方文件說,請求包含這類身份權限時,回應會帶一個 ID token,是用 JWT 表達、有數位簽章的使用者身份資訊。

token。 SDK 拿到的有三種,這個 App 用到的只有前兩種:

token 用途 這個 App
idToken OpenID Connect 的 ID token,證明「這是誰」 交給 Firebase 當憑證
accessToken 呼叫 Google API 用的短效憑證 一併交給 Firebase,沒有用它呼叫任何 Google API
refreshToken 換新 access token SDK 內部保管

redirect_uri。 這是授權完成後,Google 把使用者送回 App 的位置。官方文件的 native app 流程,iOS 的格式是 com.googleusercontent.apps.<數字>:/<路徑>,也就是 client id 的網域反過來寫。Day 25 設定的 URL scheme 就是這個,沒設的話,登入完成後回不到 App。文件同時說明,自訂網址在 Android 與 Chrome 應用程式已經不支援。文件還建議在授權請求帶 code_challenge(PKCE);GoogleSignIn-iOS 相依 AppAuth 與 GTMAppAuth,但我沒有逐行驗證 SDK 實際送出的請求,所以這裡只寫文件的建議。

7. 畫面上的名稱是預設值

授權畫面標題顯示的是 project-<專案編號>,這是 Google Cloud 專案的預設名稱。要改成「神隊友」,到 Cloud Console 的 Branding 頁設定,官方文件的流程是:

  1. 在 Branding 頁填 app 名稱、logo、聯絡資訊,修改先存成「草稿」。
  2. 按 Verify Branding,自動審核通常幾分鐘完成。
  3. 審核通過後按 Publish branding,才會出現在授權畫面;通過後 7 天內沒發佈,要重新驗證。

發佈狀態也會影響使用者看到的畫面:

發佈狀態 誰能登入 畫面
測試中 名單內的測試使用者,上限 100 人;只要求基本身份權限時,任何人都可以 顯示測試中的警告
已發佈、未驗證 任何 Google 帳號 不顯示 app 名稱與 logo
已發佈、已驗證 任何 Google 帳號 顯示 app 名稱與 logo

這個專案實際是哪一種狀態,我還沒有到 Cloud Console 確認。

8. 目前的狀態

項目 結果
簽章期限 10/9 到期;10/10 重新登入後換新,再 7 天有效
重新安裝 兩支手機都成功
自動排程 範本在專案裡,沒有安裝;而且沒有解決「過期才換新」的問題
提前換新 profile 還沒驗證
授權畫面的應用程式名稱 還沒設定
發佈狀態 還沒確認

明天預告

Day 27 加入英文語系:String Catalog 為 288 個字串補上英文與 comment、日期與時間格式改成跟隨語言、既有測試先固定語言、英文版在 ax3 字級下的版面檢查。

參考資源

官方文件


上一篇
[Day 25] 匿名帳號怎麼升級成 Google 登入:Firebase 與 Xcode 設定、用 link 保留 uid,再補一份對照讓換手機找得回家庭
系列文
神隊友.swift:30 天 Vibe Coding 打造育兒 iOS App 共 26 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言