另一個分發渠道 — Mac App Store
前一系列介紹了用 Developer ID 直接分發 macOS 應用程式的一次性準備工作。配齊憑證、公證、Sparkle 自動更新和更新 Feed 託管後,使用者無需經過 App Store 即可直接下載 .dmg 檔案。
本系列介紹將同一應用程式上架 Mac App Store (MAS) 的一次性準備工作。兩種分發方式並非二選一,可以同時運營直接分發渠道和 App Store 渠道。App Store 由 Apple 代勞處理付款、退款和搜尋曝光,使用者信任度也更高,因此與直接分發並行運營的情況很常見。
本系列以應用程式已透過 Developer ID 直接分發、且已整合 Sparkle 自動更新為前提。該設定從 Developer ID 直接分發系列第 1 篇開始介紹。若尚未閱讀,建議先行查閱。
為何 MAS 需要「另一個目標」
直接分發的應用程式包含 Sparkle 自動更新——應用程式自行檢查更新 Feed,下載新版本並替換自身的功能。
然而,Mac App Store 的審核規定禁止此類行為。上架 App Store 的應用程式不得從外部下載程式碼更新自身,更新必須且只能透過 App Store 進行。也就是說,提交 MAS 的建置中不能包含 Sparkle。
但直接分發建置必須包含 Sparkle。同一建置產物無法同時滿足兩種要求。因此,解決方案是:
同一程式碼庫,兩個建置目標。
| 渠道 | 建置目標 | Sparkle | 分發途徑 |
|---|---|---|---|
| Developer ID | FocusTimer | 包含 | 直接分發(.dmg) |
| Mac App Store | FocusTimer MAS | 排除 | App Store |
共享同一份原始碼,將建置目標分為兩個,只在其中一個中連結 Sparkle。本系列介紹建立第二個目標(FocusTimer MAS)的一次性準備工作。
本系列將完成的內容
共三篇,逐步建置以下內容:
- (第 1 篇,本文) 註冊 MAS 專用 Bundle ID + 複製 Xcode 建置目標
- (第 2 篇) 區分兩個渠道的設定檔(entitlements 和 Info.plist)與程式碼分支
- (第 3 篇) 用於上傳的
ExportOptions-MAS.plist+ App Store Connect 註冊 + 建置驗證
系列結束時,你應該已經準備好以下內容:
- 在 Apple Developer Portal 註冊的 MAS 專用 Bundle ID
- 已移除 Sparkle 相依性的
FocusTimer MAS建置目標 - MAS 專用 entitlements 檔案和 Info.plist 檔案
- 用
#if canImport(Sparkle)分支的程式碼 - 用於 App Store 上傳的
ExportOptions-MAS.plist
範例應用程式 — FocusTimer
與前一系列相同,以虛構的 macOS 應用程式 FocusTimer(管理專注時間的簡單計時器應用程式)為例。FocusTimer、Bundle Identifier com.example.FocusTimer、Team ID ABCDE12345 等均為範例值,實際使用時請替換為你自己的應用程式和帳戶資訊。
本文基於 Xcode 26 編寫。
第 1 步 — 註冊 MAS 專用 Bundle ID
為何需要獨立的 Bundle ID
若直接分發渠道的 Bundle ID 為 com.example.FocusTimer,則 MAS 渠道使用不同的 Bundle ID。
| 渠道 | Bundle ID |
|---|---|
| Developer ID | com.example.FocusTimer |
| Mac App Store | com.example.FocusTimer.mas |
在原有 Bundle ID 後追加 .mas。為何要分開?
- 同一台 Mac 上兩個渠道可以共存 — Bundle ID 相同時,macOS 會將兩個應用程式識別為同一個並產生衝突。若不同,直接分發版和 App Store 版可以同時安裝在同一台機器上(便於開發和測試)。
UserDefaults網域隔離 — 設定儲存空間以 Bundle ID 為單位劃分,兩個渠道的設定不會混淆。- 避免 Launch Services 衝突 — 防止 macOS 在處理「開啟此應用程式」等請求時產生的混亂。
請保持直接分發渠道的 Bundle ID 不變。這是識別現有使用者的基準,不能更改。MAS 用的是額外註冊的一個新 ID。
註冊流程
- 登入 developer.apple.com/account(使用 Apple Developer Program 帳戶)
- Certificates, Identifiers & Profiles → 左側選單 Identifiers →
+按鈕 - 選擇 App IDs → Continue → 類型選擇 App → Continue
- Description:輸入易於識別的名稱(如
FocusTimer MAS) - Bundle ID:
- 選擇 Explicit(明確)
- 輸入:
com.example.FocusTimer.mas
- Capabilities:關閉應用程式不使用的所有功能。若不使用 iCloud、推播通知、App 內購買、Sign in with Apple 等,無需開啟任何項目。(App Sandbox 由 Xcode 端的 entitlements 處理,此處無需開啟。)
- Continue → Register
驗證
若 Identifiers 清單中顯示 FocusTimer MAS — com.example.FocusTimer.mas 條目,則註冊完成。
第 2 步 — 複製 Xcode 建置目標
現在在 Xcode 專案中建立 FocusTimer MAS 建置目標。最快的方法是複製現有目標。
2-1. 複製 (Duplicate) 目標
- 在 Xcode 中開啟
FocusTimer.xcodeproj - 點按 Project navigator(左側面板)最上方的專案圖示(藍色)
- 在中間編輯區域的 TARGETS 清單中按右鍵點按
FocusTimer→ Duplicate - 彈出對話框時選擇 Duplicate Only
- 新目標名稱為
FocusTimer copy,雙按名稱將其改為FocusTimer MAS - 若彈出「是否新增 Scheme?」提示,選擇 Activate(或稍後在 Manage Schemes 中新增)
2-2. 立即修改 Bundle ID
複製後最先要做的是替換 Bundle ID。若保持不變,兩個目標將擁有相同的 ID。
將 TARGETS → FocusTimer MAS → General → Identity → Bundle Identifier 改為第 1 步中註冊的值:
com.example.FocusTimer.mas
2-3. 關鍵陷阱 — Duplicate 留下的「清理負擔」
這是本篇中最棘手的部分。Xcode 的 Duplicate Target 以**「安全預設值」**運行,但這種安全反而留下了需要手動處理的清理工作。複製完成後需要檢查以下四點:
① 讓新目標包含原始碼檔案
為了安全,Duplicate 會自動將所有原始碼檔案從新目標中排除。若保持不變,FocusTimer MAS 目標就是一個空殼,建置時不會包含任何程式碼。需要重新整理目標成員關係,使新目標重新包含現有原始碼檔案。
② 從 MAS 目標中移除 Sparkle 相依性
Duplicate 會原樣複製原始目標的 Swift Package 相依性。因此 Sparkle 也會隨之進入 FocusTimer MAS 目標。本系列的出發點正是「MAS 建置中不能有 Sparkle」,所以需要從 MAS 目標的套件相依性清單和 Frameworks 建置階段中刪除 Sparkle。
③ 清理暫時性 Info.plist 檔案
Duplicate 會建立類似 FocusTimer copy-Info.plist 的暫時性 Info.plist 檔案。由於我們將在第 2 篇中單獨建立 MAS 專用的 Info.plist,這些暫時性檔案(包括專案參照和實際檔案)應全部刪除。
④ 更新 MAS 目標的建置設定
Bundle Identifier、Info.plist 路徑、entitlements 路徑等建置設定需要調整為 MAS 專用。此工作將在第 2 篇建立專用設定檔後統一處理。
①·② 大多可透過 Xcode UI 中的目標成員關係和套件相依性介面處理,但若複製過程留下的重複參照無法徹底清除,有時需要直接查看專案檔案(
.pbxproj)。清理完成後,分別建置兩個目標,確認 MAS 目標建置中import Sparkle是否報錯,是最可靠的驗證方式(第 2 篇中介紹此程式碼分支)。
第 1 篇小結
跟到這裡,你現在已經準備好以下內容:
- ✅ 在 Apple Developer Portal 註冊了 MAS 專用 Bundle ID(
com.example.FocusTimer.mas) - ✅ 透過複製建立了
FocusTimer MAS建置目標 - ✅ 了解了複製留下的清理負擔(原始碼成員關係、Sparkle 相依性、暫時性檔案)
目標這個「容器」已經建立好了。但這個目標目前本質上還只是 FocusTimer 的副本,並未真正成為「MAS 專用」。MAS 建置需要使用與直接分發建置不同的權限 (entitlements) 和不同的 Info.plist,程式碼也需要分支以便在沒有 Sparkle 時不報錯。
下一篇將介紹區分兩個渠道的設定檔與程式碼分支。