Writing

Runs as Current User 並不是一個可以隨便勾選的 Checkbox

Syncnext 1.188 上線後,開始有人回報原本已購買的終身版突然失效,既有的 StoreKit 1 驗證流程也失去工作能力。

我最後在 1.190 移除了 runs-as-current-user。同一位用戶更新後,不只購買恢復,原本消失的播放紀錄也一起回來了。

這次事故讓我確認:Runs as Current User 並不是一個可以根據 Xcode 裡幾行說明就安全開啟的選項。它實際造成的影響,遠大於 Apple 文件描述的範圍。對我來說,這不是一個好的 API。

當初為什麼會開啟它

事情起於一個真實的使用需求。

一位用戶家裡有兩台 Apple TV,希望兩邊保留各自的觀看紀錄。他想保留原來的使用者,再新增另一位 Apple TV 使用者:新使用者登入另一個 iCloud Apple ID,讓 CloudKit 資料分開;「商店」仍使用原本購買 Syncnext 的 Apple Account,以沿用 Pro 權益。

Apple TV 允許 iCloud 與商店帳號分開設定。但建立多個系統使用者之後,App 是否跟隨目前使用者,還取決於 User Management entitlement。

我當時查到的有效公開資料,只有 Apple 的 User Management entitlement 文件。文件說明:加入 com.apple.developer.user-management 並指定 runs-as-current-user 後,系統會以目前選取的使用者執行 App,本機資料、UserDefaults、iCloud 等資源也會跟隨目前使用者。

這段說明正好對應觀看紀錄隔離的需求。10 月 4 日,我把這個 entitlement 加入 Syncnext。

1.188 之後的事故

在 1.188 版本加入了 runs-as-current-user。

10 月 9 日,一位已購買終身版的用戶回報:前一天仍能使用,當天卻失效;App 無法取得原有購買。兩台 Apple TV 使用相同 Apple ID 與網路,其中一台正常,另一台無法辨識購買。

這不是一套剛寫好的 StoreKit 程式。Syncnext 的 StoreKit 程式碼至少從 tvOS 15 開始就在實際使用,近期也沒有修改這條購買驗證流程。

同一時間,原本持續工作數年的 StoreKit 1 跨區憑證驗證開始大量失敗。永久買斷與訂閱驗證都受到影響。1.187 沒有相同現象;第一個出現問題的版本,正是加入 runs-as-current-user 的 1.188。

Apple 文件只說明 App 會改以目前使用者執行,以及本機與 iCloud 資料會跟隨該使用者。文件沒有提到 StoreKit 1、App Store receipt、既有購買,或升級後的交易狀態會受到什麼影響。我也找不到其他有效的公開資料,可以解釋或預告這種行為。

我能依靠的只有版本時間線:StoreKit 程式沒有變,新增的 runs-as-current-user 卻同時改變了播放紀錄與購買驗證的行為。

我決定移除它。

移除後的驗證

runs-as-current-user 在 1.190 被移除。tvOS Simulator 與裝置 build 通過,簽署產物也確認不再包含 com.apple.developer.user-management。

10 月 10 日,用戶更新到 1.190 後回報:購買恢復正常,原有播放紀錄也重新出現。

播放紀錄回來,表示 App 再次讀到原本的資料範圍;購買同時恢復,則確認問題不只存在於文件有說明的本機與 iCloud 資料隔離。這個 entitlement 還影響了既有的 StoreKit 1 工作流程。

Apple 把 Runs as Current User 呈現為一項 User Management capability,但它改變的是整個 App 的執行身分,而且影響會穿過文件沒有說明的系統邊界。當一個 API 的實際作用範圍無法從文件判斷,甚至會讓長期正常運作的購買流程失效,我不認為它是一個好的 API。