macos-distribution
4
总安装量
4
周安装量
#52927
全站排名
安装命令
npx skills add https://github.com/makgunay/claude-swift-skills --skill macos-distribution
Agent 安装分布
opencode
4
claude-code
4
codex
4
cursor
4
gemini-cli
3
github-copilot
3
Skill 文档
macOS Distribution & StoreKit
Critical Constraints
- â DO NOT distribute without code signing â â Always sign with Developer ID (direct) or Apple Distribution (App Store)
- â DO NOT skip notarization for direct distribution â â Required since macOS 10.15 for Gatekeeper
- â DO NOT use
Transaction.currentEntitlement(for:)â â UseTransaction.currentEntitlements(for:)(plural, returns sequence) - â DO NOT forget PrivacyInfo.xcprivacy â â Required for App Store submission
Distribution Decision Tree
App Store distribution?
âââ YES â Apple Distribution certificate + sandbox + review
â âââ Basic version (sandbox-safe features)
â âââ Pro features via IAP/subscription
âââ Direct distribution?
âââ Developer ID certificate + notarization
âââ DMG or pkg installer
âââ Full system access (no sandbox required)
Dual strategy?
âââ App Store: Basic/free version, sandbox-safe
âââ Direct: Pro version, full Accessibility/CGEvent access
Code Signing & Notarization (Direct)
# Sign with Developer ID
codesign --deep --force --verify --verbose \
--sign "Developer ID Application: Your Name (TEAMID)" \
--options runtime \
MyApp.app
# Create DMG
hdiutil create -volname "MyApp" -srcfolder MyApp.app \
-ov -format UDZO MyApp.dmg
# Notarize
xcrun notarytool submit MyApp.dmg \
--apple-id "you@email.com" \
--team-id "TEAMID" \
--password "app-specific-password" \
--wait
# Staple (attach notarization ticket)
xcrun stapler staple MyApp.dmg
Sandboxing Entitlements (App Store)
<?xml version="1.0" encoding="UTF-8"?>
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key><true/>
<key>com.apple.security.network.client</key><true/>
<key>com.apple.security.files.user-selected.read-write</key><true/>
<key>com.apple.developer.icloud-container-identifiers</key>
<array><string>iCloud.com.company.app</string></array>
<key>com.apple.security.application-groups</key>
<array><string>group.com.company.app</string></array>
</dict>
</plist>
StoreKit â SubscriptionOfferView
import StoreKit
// Basic subscription offer
SubscriptionOfferView(productID: "com.app.pro.monthly")
.prefersPromotionalIcon(true)
// With custom icon
SubscriptionOfferView(productID: "com.app.pro.monthly") {
Image("pro_icon").resizable().frame(width: 40, height: 40)
}
// Show upgrade/downgrade based on customer status
SubscriptionOfferView(groupID: "com.app.subscriptions", visibleRelationship: .upgrade)
// Detail action
SubscriptionOfferView(productID: "com.app.pro.monthly")
.subscriptionOfferViewDetailAction { isShowingStore = true }
StoreKit â Transaction Handling
// Check entitlements (new plural API)
for await result in Transaction.currentEntitlements(for: "com.app.pro") {
if case .verified(let transaction) = result {
let appTxID = transaction.appTransactionID
if let offerPeriod = transaction.offerPeriod {
// Handle offer period
}
}
}
// Track subscription status
.subscriptionStatusTask(for: "com.app.subscriptions") { statuses in
if statuses.contains(where: { $0.state == .subscribed }) {
customerStatus = .subscribed
} else {
customerStatus = .notSubscribed
}
}
// AppTransaction â unique download identifier
let appTransaction = try await AppTransaction.shared
let transactionID = appTransaction.appTransactionID
let platform = appTransaction.originalPlatform // .iOS, .macOS, .tvOS, .visionOS
StoreKit Testing in Xcode
- File â New â File From Template â search “StoreKit Configuration File”
- Define products in the
.storekitfile - Edit scheme â Options â StoreKit Configuration â select your file
- Use Transaction Manager window to test purchase scenarios
Launch at Login
import ServiceManagement
func setLaunchAtLogin(_ enabled: Bool) {
do {
if enabled { try SMAppService.mainApp.register() }
else { try SMAppService.mainApp.unregister() }
} catch { print("Launch at login error: \(error)") }
}
func isLaunchAtLoginEnabled() -> Bool {
SMAppService.mainApp.status == .enabled
}
Common Mistakes & Fixes
| Mistake | Fix |
|---|---|
| “App is damaged” error | Notarize and staple before distribution |
| App Store rejection for entitlements | Justify each entitlement in review notes |
currentEntitlement(for:) deprecated |
Use currentEntitlements(for:) (plural) |
| Missing privacy manifest | Add PrivacyInfo.xcprivacy to app target |
| StoreKit products not loading in dev | Set StoreKit Configuration in scheme options |