Kênh Phân Phối Khác — Mac App Store
Series trước đã đề cập đến phần thiết lập một lần để phân phối trực tiếp ứng dụng macOS bằng Developer ID. Khi bạn đã có chứng chỉ, công chứng, cập nhật tự động Sparkle và update feed được lưu trữ, bạn có thể cho người dùng tải file .dmg trực tiếp mà không cần qua App Store.
Series này đề cập đến phần thiết lập một lần để đưa cùng ứng dụng đó lên Mac App Store (MAS). Hai phương thức phân phối không phải là sự lựa chọn một trong hai. Bạn có thể vận hành một ứng dụng qua cả kênh phân phối trực tiếp lẫn kênh App Store cùng một lúc. App Store có lợi thế là Apple xử lý thanh toán, hoàn tiền và khả năng hiển thị tìm kiếm thay bạn, và nó mang lại độ tin cậy cao hơn từ người dùng, vì vậy việc chạy song song với phân phối trực tiếp là lựa chọn phổ biến.
Series này giả sử ứng dụng của bạn đã được phân phối trực tiếp bằng Developer ID và đã tích hợp cập nhật tự động Sparkle. Phần thiết lập đó đã được đề cập bắt đầu từ Phần 1 của series “Tự Phân Phối Ứng Dụng macOS”. Nếu bạn chưa đọc, tôi khuyên bạn nên đọc trước.
Tại Sao MAS Cần “Target Khác”
Ứng dụng phân phối trực tiếp bao gồm cập nhật tự động Sparkle — tính năng cho phép ứng dụng tự kiểm tra update feed, tải phiên bản mới và thay thế chính nó.
Nhưng quy tắc duyệt Mac App Store cấm hành vi này. Ứng dụng trên App Store không được tải code từ bên ngoài để tự cập nhật; cập nhật phải chỉ diễn ra qua App Store. Nói cách khác, build dự định cho MAS không được chứa Sparkle.
Tuy nhiên, build phân phối trực tiếp phải có Sparkle. Một artifact build duy nhất không thể đáp ứng cả hai yêu cầu cùng một lúc. Vì vậy giải pháp là:
Một codebase, hai build target.
| Kênh | Build target | Sparkle | Phân phối qua |
|---|---|---|---|
| Developer ID | FocusTimer | Có | Phân phối trực tiếp (.dmg) |
| Mac App Store | FocusTimer MAS | Không | App Store |
Bạn chia sẻ một codebase nguồn duy nhất, nhưng chia thành hai build target, chỉ liên kết Sparkle trong một trong số đó. Series này đề cập đến phần thiết lập một lần để tạo target thứ hai đó (FocusTimer MAS).
Những Gì Series Này Xây Dựng
Qua ba phần, bạn sẽ chuẩn bị được những thứ sau.
- (Phần 1, bài này) Đăng ký Bundle ID chỉ dành cho MAS + nhân đôi build target Xcode
- (Phần 2) Các file cấu hình (entitlements và Info.plist) và phân nhánh code phân tách hai kênh
- (Phần 3)
ExportOptions-MAS.plistđể upload + đăng ký App Store Connect + xác minh build
Khi kết thúc series, bạn cần có:
- Bundle ID chỉ dành cho MAS đã đăng ký trong Apple Developer Portal
- Build target
FocusTimer MASđã xóa dependency Sparkle - File entitlements và Info.plist chỉ dành cho MAS
- Code được phân nhánh bằng
#if canImport(Sparkle) -
ExportOptions-MAS.plistđể upload lên App Store
Ứng Dụng Mẫu — FocusTimer
Như trong series trước, chúng ta sẽ dùng ứng dụng macOS giả tưởng FocusTimer (một ứng dụng đếm thời gian đơn giản để quản lý phiên tập trung) làm ví dụ. FocusTimer, bundle identifier com.example.FocusTimer, Team ID ABCDE12345, v.v. đều là giá trị mẫu. Trong thực tế, hãy thay thế bằng ứng dụng và thông tin tài khoản của bạn.
Bài viết này được viết dựa trên Xcode 26.
Bước 1 — Đăng Ký Bundle ID Chỉ Dành Cho MAS
Tại Sao Cần Bundle ID Riêng
Nếu Bundle ID của kênh phân phối trực tiếp là com.example.FocusTimer, kênh MAS sẽ dùng Bundle ID khác.
| Kênh | Bundle ID |
|---|---|
| Developer ID | com.example.FocusTimer |
| Mac App Store | com.example.FocusTimer.mas |
Bạn thêm .mas vào Bundle ID hiện có. Tại sao phải tách chúng?
- Cả hai kênh có thể cùng tồn tại trên một Mac — Nếu Bundle ID giống nhau, macOS coi hai ứng dụng là cùng một ứng dụng và chúng sẽ xung đột. Nếu khác nhau, bạn có thể cài đặt bản phân phối trực tiếp và bản App Store trên cùng một máy cùng lúc (tiện lợi cho phát triển và kiểm thử).
- Phân tách domain
UserDefaults— Bộ nhớ cài đặt được phân vùng theo Bundle ID, vì vậy cài đặt của hai kênh không bị trộn lẫn. - Tránh xung đột Launch Services — Ngăn nhầm lẫn khi macOS quyết định ứng dụng nào để gửi yêu cầu như “mở ứng dụng này”.
Giữ nguyên vẹn Bundle ID của kênh phân phối trực tiếp. Đó là định danh cho người dùng hiện tại của bạn, vì vậy không được thay đổi. Bạn đang đăng ký thêm một ID mới cho MAS.
Quy Trình Đăng Ký
- Đăng nhập tại developer.apple.com/account (bằng tài khoản Apple Developer Program)
- Certificates, Identifiers & Profiles → Identifiers trong menu bên trái → nút
+ - Chọn App IDs → Continue → chọn loại App → Continue
- Description: Nhập tên dễ nhận ra (ví dụ:
FocusTimer MAS) - Bundle ID:
- Chọn Explicit
- Nhập:
com.example.FocusTimer.mas
- Capabilities: Tắt mọi tính năng ứng dụng không dùng. Nếu bạn không dùng iCloud, push notification, in-app purchase, Sign in with Apple, v.v., bạn không cần bật gì cả. (App Sandbox được xử lý bởi entitlements phía Xcode, vì vậy không cần bật ở đây.)
- Continue → Register
Xác Minh
Nếu danh sách Identifiers hiển thị mục FocusTimer MAS — com.example.FocusTimer.mas, đăng ký đã hoàn tất.
Bước 2 — Nhân Đôi Build Target Xcode
Bây giờ bạn sẽ tạo build target FocusTimer MAS trong dự án Xcode. Cách nhanh nhất là nhân đôi target hiện có.
2-1. Nhân Đôi Target
- Mở
FocusTimer.xcodeprojtrong Xcode - Nhấp vào biểu tượng dự án (màu xanh) ở đầu Project navigator (bảng bên trái)
- Trong danh sách TARGETS ở vùng chỉnh sửa trung tâm, nhấp chuột phải vào
FocusTimer→ Duplicate - Khi hộp thoại xuất hiện, chọn Duplicate Only
- Target mới được tạo với tên
FocusTimer copy. Nhấp đúp vào tên và đổi thànhFocusTimer MAS - Nếu lời nhắc “Add a new Scheme?” xuất hiện, chọn Activate (hoặc thêm sau trong Manage Schemes)
2-2. Thay Đổi Bundle ID Ngay Lập Tức
Điều đầu tiên cần làm ngay sau khi nhân đôi là thay Bundle ID. Nếu để nguyên, cả hai target sẽ có cùng ID.
Thay đổi TARGETS → FocusTimer MAS → General → Identity → Bundle Identifier thành giá trị bạn đã đăng ký ở Bước 1.
com.example.FocusTimer.mas
2-3. Bẫy Quan Trọng — “Gánh Nặng Dọn Dẹp” Mà Duplicate Để Lại
Đây là phần phức tạp nhất của bài viết này. Duplicate Target của Xcode hoạt động với “giá trị mặc định an toàn”, nhưng chính sự an toàn đó để lại công việc dọn dẹp cần can thiệp thủ công. Ngay sau khi nhân đôi, bạn cần kiểm tra bốn điều sau.
① Làm cho target mới bao gồm các file nguồn
Vì lý do an toàn, Duplicate tự động loại trừ tất cả file nguồn khỏi target mới. Nếu để nguyên, target FocusTimer MAS là một vỏ rỗng — build nó sẽ tạo ra build không có code gì. Bạn cần sửa lại các target membership để target mới bao gồm các file nguồn hiện có.
② Xóa dependency Sparkle khỏi MAS target
Duplicate sao chép nguyên văn các Swift Package dependency của bản gốc. Kết quả là Sparkle cũng theo vào target FocusTimer MAS. Toàn bộ điểm khởi đầu của series này là “MAS build không được có Sparkle”, vì vậy hãy xóa Sparkle khỏi danh sách package dependency của MAS target và khỏi Frameworks build phase.
③ Dọn dẹp file Info.plist tạm thời
Duplicate tạo ra file Info.plist tạm thời như FocusTimer copy-Info.plist. Vì chúng ta sẽ tạo Info.plist dành riêng cho MAS ở Phần 2, hãy xóa các file tạm thời này — cả project reference lẫn file thực tế.
④ Cập nhật cài đặt build của MAS target
Bạn cần căn chỉnh các cài đặt build như bundle identifier, đường dẫn Info.plist và đường dẫn entitlements cho MAS. Chúng ta sẽ xử lý tất cả cùng một lúc ở Phần 2, sau khi tạo các file cấu hình chuyên dụng.
Mục ① và ② phần lớn có thể được xử lý từ các màn hình target membership và package dependency trong Xcode UI, nhưng nếu các tham chiếu trùng lặp còn lại từ quá trình nhân đôi không được dọn sạch gọn, đôi khi bạn cần nhìn trực tiếp vào file dự án (
.pbxproj). Khi bạn nghĩ việc dọn dẹp đã xong, cách đáng tin cậy nhất để xác minh là build cả hai target riêng biệt và kiểm tra xemimport Sparklecó bị lỗi trong MAS target build không (Phần 2 đề cập đến phân nhánh code này).
Tổng Kết Phần 1
Nếu bạn đã làm theo đến đây, bây giờ bạn có:
- ✅ Bundle ID chỉ dành cho MAS (
com.example.FocusTimer.mas) đã đăng ký trong Apple Developer Portal - ✅ Build target
FocusTimer MASđược tạo bằng cách nhân đôi - ✅ Hiểu về gánh nặng dọn dẹp mà việc nhân đôi để lại (source membership, Sparkle dependency, file tạm thời)
“Vỏ chứa” — target — đã được tạo. Nhưng target này về cơ bản vẫn chỉ là bản sao của FocusTimer; nó chưa thực sự trở thành “dành riêng cho MAS”. MAS build cần dùng entitlements khác và Info.plist khác so với build phân phối trực tiếp, và code cũng phải được phân nhánh để không bị lỗi khi thiếu Sparkle.
Ở phần tiếp theo, chúng ta sẽ đề cập đến các file cấu hình và phân nhánh code phân tách hai kênh.