From 253037867e805e796b99df1dfc6a806a5097fa64 Mon Sep 17 00:00:00 2001 From: Nevil Macwan Date: Wed, 18 Mar 2026 15:27:14 +0530 Subject: [PATCH] Prepare codebase for Mac App Store submission - Wrap Sparkle imports and usage with #if !MAS_BUILD conditional compilation so the MAS build excludes the third-party update framework (Apple rejects it) - Add MAS-specific entitlements file (macSCP_MAS.entitlements) - Bump MARKETING_VERSION from 0.2.4 to 0.2.7 (next release after v0.2.6) - Add MAS_SUBMISSION_GUIDE.md with build instructions and App Store Connect setup Co-Authored-By: Paperclip --- MAS_SUBMISSION_GUIDE.md | 110 ++++++++++++++++++ macSCP.xcodeproj/project.pbxproj | 4 +- macSCP/App/MacSCPApp.swift | 8 ++ .../Settings/CheckForUpdatesView.swift | 2 + .../Settings/CheckForUpdatesViewModel.swift | 2 + macSCP/macSCP_MAS.entitlements | 20 ++++ 6 files changed, 144 insertions(+), 2 deletions(-) create mode 100644 MAS_SUBMISSION_GUIDE.md create mode 100644 macSCP/macSCP_MAS.entitlements diff --git a/MAS_SUBMISSION_GUIDE.md b/MAS_SUBMISSION_GUIDE.md new file mode 100644 index 0000000..269272d --- /dev/null +++ b/MAS_SUBMISSION_GUIDE.md @@ -0,0 +1,110 @@ +# Mac App Store Submission Guide + +## Prerequisites + +- Apple Developer account with App Store Connect access +- Xcode with "Apple Distribution" signing certificate installed +- Mac App Store provisioning profile for `com.macscp.macSCP` + +## Build Configuration + +### MAS Build Flag + +This project uses a `MAS_BUILD` Swift compiler flag to conditionally exclude Sparkle (third-party update framework), which Apple does not allow on the App Store. + +To build for the Mac App Store: + +1. In Xcode, go to **Build Settings** → **Swift Compiler - Custom Flags** → **Active Compilation Conditions** +2. Add `MAS_BUILD` to the Release configuration +3. Alternatively, pass it via xcodebuild: + +```bash +xcodebuild archive \ + -project macSCP.xcodeproj \ + -scheme macSCP \ + -archivePath build/macSCP-MAS.xcarchive \ + SWIFT_ACTIVE_COMPILATION_CONDITIONS='$(inherited) MAS_BUILD' \ + CODE_SIGN_IDENTITY="Apple Distribution" \ + CODE_SIGN_STYLE=Manual \ + PROVISIONING_PROFILE_SPECIFIER="macSCP App Store" +``` + +### MAS Entitlements + +Use `macSCP/macSCP_MAS.entitlements` for the App Store build. This file mirrors the standard entitlements but is suitable for MAS distribution. + +When exporting the archive: + +```bash +xcodebuild -exportArchive \ + -archivePath build/macSCP-MAS.xcarchive \ + -exportPath build/MAS \ + -exportOptionsPlist ExportOptions-MAS.plist +``` + +### ExportOptions-MAS.plist (create this) + +```xml + + + + + method + app-store + teamID + NW7K6UFA6P + uploadSymbols + + signingStyle + manual + signingCertificate + Apple Distribution + provisioningProfiles + + com.macscp.macSCP + macSCP App Store + + + +``` + +## App Store Connect Setup + +1. **Create the app** in App Store Connect with bundle ID `com.macscp.macSCP` +2. **App Information**: + - Name: macSCP + - Subtitle: SFTP, S3 & SSH for Mac + - Category: Developer Tools + - Secondary Category: Utilities +3. **Description**: + > macSCP is a native macOS client for managing remote servers via SFTP, Amazon S3, and SSH terminal — all in one app built with SwiftUI. + > + > Features: + > • SFTP file browser with drag-and-drop transfers + > • Amazon S3 bucket management + > • Built-in SSH terminal + > • Secure credential storage in Keychain + > • Multiple simultaneous connections + > • Dark mode support +4. **Keywords**: sftp, s3, ssh, terminal, file transfer, remote, server, ftp, macos, developer +5. **Screenshots**: Required sizes — 1280x800 and 1440x900 (or retina equivalents) +6. **Privacy Policy URL**: Required — add to macscp.co +7. **Support URL**: https://github.com/macnev2013/macSCP/issues +8. **Marketing URL**: https://www.macscp.co + +## Upload & Submit + +1. Archive in Xcode (Product → Archive) with MAS build settings +2. Upload via Xcode Organizer or `xcrun altool --upload-app` +3. In App Store Connect, select the build and submit for review + +## Review Notes + +> macSCP requires network access (outgoing and incoming) to connect to SFTP servers, Amazon S3 buckets, and SSH terminals. The app stores connection credentials securely in the macOS Keychain. +> +> To test: Add an SFTP connection using any publicly accessible SFTP server, or configure an S3 bucket with valid AWS credentials. + +## Timeline + +- App Store review typically takes 1-3 days +- Submit by March 22 to be live for launch week (March 25+) diff --git a/macSCP.xcodeproj/project.pbxproj b/macSCP.xcodeproj/project.pbxproj index 1050081..d09d0bd 100644 --- a/macSCP.xcodeproj/project.pbxproj +++ b/macSCP.xcodeproj/project.pbxproj @@ -511,7 +511,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 0.2.4; + MARKETING_VERSION = 0.2.7; PRODUCT_BUNDLE_IDENTIFIER = com.macscp.macSCP; PRODUCT_NAME = "$(TARGET_NAME)"; REGISTER_APP_GROUPS = YES; @@ -558,7 +558,7 @@ "$(inherited)", "@executable_path/../Frameworks", ); - MARKETING_VERSION = 0.2.4; + MARKETING_VERSION = 0.2.7; PRODUCT_BUNDLE_IDENTIFIER = com.macscp.macSCP; PRODUCT_NAME = "$(TARGET_NAME)"; REGISTER_APP_GROUPS = YES; diff --git a/macSCP/App/MacSCPApp.swift b/macSCP/App/MacSCPApp.swift index 78abdaa..41274be 100644 --- a/macSCP/App/MacSCPApp.swift +++ b/macSCP/App/MacSCPApp.swift @@ -7,19 +7,24 @@ import SwiftUI import SwiftData +#if !MAS_BUILD import Sparkle +#endif @main struct MacSCPApp: App { @StateObject private var container = DependencyContainer.shared + #if !MAS_BUILD private let updaterController: SPUStandardUpdaterController @StateObject private var checkForUpdatesViewModel: CheckForUpdatesViewModel + #endif init() { AnalyticsService.initialize() AppLockManager.shared.lockIfNeeded() + #if !MAS_BUILD let controller = SPUStandardUpdaterController( startingUpdater: true, updaterDelegate: nil, @@ -29,6 +34,7 @@ struct MacSCPApp: App { self._checkForUpdatesViewModel = StateObject( wrappedValue: CheckForUpdatesViewModel(updater: controller.updater) ) + #endif } var body: some Scene { @@ -94,9 +100,11 @@ struct MacSCPApp: App { // MARK: - Commands @CommandsBuilder private var appCommands: some Commands { + #if !MAS_BUILD CommandGroup(after: .appInfo) { CheckForUpdatesView(viewModel: checkForUpdatesViewModel) } + #endif CommandGroup(replacing: .newItem) { Button("New Connection") { diff --git a/macSCP/Features/Settings/CheckForUpdatesView.swift b/macSCP/Features/Settings/CheckForUpdatesView.swift index 7a7c192..7bb421a 100644 --- a/macSCP/Features/Settings/CheckForUpdatesView.swift +++ b/macSCP/Features/Settings/CheckForUpdatesView.swift @@ -3,6 +3,7 @@ // macSCP // +#if !MAS_BUILD import SwiftUI struct CheckForUpdatesView: View { @@ -13,3 +14,4 @@ struct CheckForUpdatesView: View { .disabled(!viewModel.canCheckForUpdates) } } +#endif diff --git a/macSCP/Features/Settings/CheckForUpdatesViewModel.swift b/macSCP/Features/Settings/CheckForUpdatesViewModel.swift index 2bca11e..820c33b 100644 --- a/macSCP/Features/Settings/CheckForUpdatesViewModel.swift +++ b/macSCP/Features/Settings/CheckForUpdatesViewModel.swift @@ -3,6 +3,7 @@ // macSCP // +#if !MAS_BUILD import Combine import Foundation import Sparkle @@ -26,3 +27,4 @@ final class CheckForUpdatesViewModel: ObservableObject { logInfo("Manual update check triggered", category: .app) } } +#endif diff --git a/macSCP/macSCP_MAS.entitlements b/macSCP/macSCP_MAS.entitlements new file mode 100644 index 0000000..c55be86 --- /dev/null +++ b/macSCP/macSCP_MAS.entitlements @@ -0,0 +1,20 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.network.client + + com.apple.security.network.server + + com.apple.security.files.user-selected.read-write + + com.apple.security.files.downloads.read-write + + keychain-access-groups + + $(AppIdentifierPrefix)com.macSCP.keychain + + +