# macos-distribution > macOS app distribution covering code signing (Developer ID, App Store certificates), notarization (notarytool), DMG/pkg creation, App Store submission workflow, sandboxing entitlements, StoreKit for in-app purchases and subscriptions (SubscriptionOfferView, appTransactionID, Transaction.currentEntitlements, subscriptionStatusTask), StoreKit testing with local configuration files, PrivacyInfo.xcprivacy manifest, and dual distribution strategies (App Store + direct). Use when preparing an app for distribution, implementing purchases/subscriptions, configuring signing, or troubleshooting App Store rejection. - Author: Mehmet - Repository: makgunay/claude-swift-skills - Version: 20260206203503 - Stars: 0 - Forks: 0 - Last Updated: 2026-02-07 - Source: https://github.com/makgunay/claude-swift-skills - Web: https://mule.run/skillshub/@@makgunay/claude-swift-skills~macos-distribution:20260206203503 --- --- name: macos-distribution description: macOS app distribution covering code signing (Developer ID, App Store certificates), notarization (notarytool), DMG/pkg creation, App Store submission workflow, sandboxing entitlements, StoreKit for in-app purchases and subscriptions (SubscriptionOfferView, appTransactionID, Transaction.currentEntitlements, subscriptionStatusTask), StoreKit testing with local configuration files, PrivacyInfo.xcprivacy manifest, and dual distribution strategies (App Store + direct). Use when preparing an app for distribution, implementing purchases/subscriptions, configuring signing, or troubleshooting App Store rejection. --- # 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:)` → ✅ Use `Transaction.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) ```bash # 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 com.apple.security.app-sandbox com.apple.security.network.client com.apple.security.files.user-selected.read-write com.apple.developer.icloud-container-identifiers iCloud.com.company.app com.apple.security.application-groups group.com.company.app ``` ## StoreKit — SubscriptionOfferView ```swift 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 ```swift // 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 1. File → New → File From Template → search "StoreKit Configuration File" 2. Define products in the `.storekit` file 3. Edit scheme → Options → StoreKit Configuration → select your file 4. Use Transaction Manager window to test purchase scenarios ## Launch at Login ```swift 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 | ## References - [Notarizing macOS software](https://developer.apple.com/documentation/security/notarizing_macos_software_before_distribution) - [App Sandbox Design Guide](https://developer.apple.com/documentation/security/app_sandbox) - [StoreKit Documentation](https://developer.apple.com/documentation/storekit) - [WWDC 2025: What's new in StoreKit](https://developer.apple.com/videos/play/wwdc2025/241/)