-
Notifications
You must be signed in to change notification settings - Fork 0
Building and signing
Build from a local clone on the internal disk (e.g.
~/Documents/aMuleRemote-work), not from a cloud-synced folder: file providers such as Google Drive can blockxcodebuildinNSFileCoordinatorand make builds extremely slow.
Requires the Xcode Command Line Tools (Swift 5.9+).
cd aMuleRemote
swift build -c releaseTo refresh the aMule Remote.app bundle after a change:
swift build -c release
cp .build/release/AmuleRemote "aMule Remote.app/Contents/MacOS/aMule Remote"
xcrun xcstringstool compile Localizable.xcstrings --output-directory "aMule Remote.app/Contents/Resources"
codesign --force --deep --sign - "aMule Remote.app"The last line applies an ad-hoc signature, fine for local use. The builds published in the Releases (and via Homebrew) are instead signed with a Developer ID Application certificate, hardened runtime, and notarized by Apple — see below.
xcodebuild -exportArchive with signingStyle: automatic needs an Apple Account signed in to Xcode (Xcode → Settings → Apple Accounts): without it the export fails with No Accounts, and an App Store Connect API key only works for cloud signing if it has the Access to Cloud Managed Distribution Certificate permission. Alternative that needs neither: manual signing with a local Apple Distribution certificate and App Store provisioning profiles created in the developer portal (signingStyle: manual, provisioningProfiles per bundle id, installerSigningCertificate for the Mac package). The tvOS archive must be signed at archive time (manual signing with the App Store profile works without a registered Apple TV): an archive built with CODE_SIGNING_ALLOWED=NO and signed only at export loses the entitlements of SupportFiles/TV.entitlements (iCloud KVS, keychain groups) — this is what happened to the tvOS builds of 1.3 and 1.4. Verify every exported app with codesign -d --entitlements :- "Payload/aMule Remote.app" before uploading. On the Mac side, strip cloud-drive extended attributes with xattr -cr before codesign.
Requires the full Xcode (free from the Mac App Store). Release 1.4.3 (build 23) is built with Xcode 27.0 and the 27.0 SDKs; Xcode 26.x still builds the project (deployment targets are unchanged: iOS/tvOS 17, macOS 14, watchOS 10).
The Xcode project is generated from project.yml with XcodeGen:
xcodegen generateThen open AmuleRemoteiOS.xcodeproj, set your Team in Signing & Capabilities and build. Targets:
| Target | What it builds |
|---|---|
AmuleRemoteiOS |
iPhone / iPad app (embeds the Watch app) |
AmuleRemoteWatch |
Apple Watch companion |
AmuleRemoteVision |
Apple Vision Pro app |
AmuleRemoteTV |
Apple TV app (tvOS 17+, folder TV/, layered icon and Top Shelf assets in TV/Assets.xcassets) |
AmuleRemoteMac |
sandboxed Mac App Store build (same bundle id as iOS, universal purchase) |
All App Store targets declare the iCloud Key-Value Storage entitlement (com.apple.developer.ubiquity-kvstore-identifier with the fixed shared value TEAMID.com.sdimambro.amule-remote-ios) and a shared keychain access group, generated by XcodeGen in SupportFiles/*.entitlements, for the optional profile sync; both App IDs have the iCloud capability enabled in the developer portal. The SPM/DMG build gets the same entitlements at signing time through SupportFiles/MacDirect.entitlements plus a Developer ID provisioning profile (type "Mac App Direct", created in the developer portal with the Developer ID Application certificate) copied to Contents/embedded.provisionprofile before codesign. Without them (a plain swift build run) CloudSync.isAvailable is false and the switch is greyed out.
See Installing on iPhone and iPad.
ℹ️ The shared sources in
Sources/AmuleRemoteare listed one by one inproject.yml: a new shared file is not compiled into the iOS/visionOS/tvOS apps until you add it there. The SPM macOS target picks up the whole folder automatically.
Marketing version and build number are kept aligned across all platforms (MARKETING_VERSION / CURRENT_PROJECT_VERSION in every target of project.yml, and CFBundleShortVersionString / CFBundleVersion in the Mac bundle's Info.plist). Current: 1.4.2 (build 22). Release tags: v1.4.2-build22.
# 1) bundle
swift build -c release
cp .build/release/AmuleRemote "aMule Remote.app/Contents/MacOS/aMule Remote"
xcrun xcstringstool compile Localizable.xcstrings --output-directory "aMule Remote.app/Contents/Resources"
# 2) Developer ID signature with hardened runtime + iCloud entitlements
xattr -cr "aMule Remote.app"
cp YourDeveloperID.provisionprofile "aMule Remote.app/Contents/embedded.provisionprofile"
codesign --force --deep --options runtime --timestamp \
--entitlements SupportFiles/MacDirect.entitlements \
--sign "Developer ID Application: YOUR NAME (TEAMID)" "aMule Remote.app"
# 3) notarize and staple
ditto -c -k --keepParent "aMule Remote.app" notarize.zip
xcrun notarytool submit notarize.zip --key AuthKey.p8 --key-id KEYID --issuer ISSUER --wait
xcrun stapler staple "aMule Remote.app"
# 4) DMG: app + symlink to /Applications, Finder icon layout, compress,
# then sign + notarize + staple the DMG itself
hdiutil create -volname "aMule Remote" -srcfolder dmgroot -fs HFS+ -format UDRW -o rw.dmg
hdiutil convert rw.dmg -format UDZO -imagekey zlib-level=9 -o aMuleRemote-macOS.dmg
codesign --force --timestamp --sign "Developer ID Application: YOUR NAME (TEAMID)" aMuleRemote-macOS.dmg
xcrun notarytool submit aMuleRemote-macOS.dmg --key AuthKey.p8 --key-id KEYID --issuer ISSUER --wait
xcrun stapler staple aMuleRemote-macOS.dmgThe DMG is attached to the GitHub release as aMuleRemote-macOS.dmg (always the same name, so the download buttons and the Homebrew cask point to releases/latest).
The cask lives in the sidimam/homebrew-tap repository (Casks/amule-remote.rb). For every release: update version and sha256 (shasum -a 256 aMuleRemote-macOS.dmg), then brew audit --cask --online amule-remote.
aMuleRemote/
├── Package.swift # macOS target (SPM)
├── project.yml # XcodeGen spec: iOS, Watch, Vision, TV and Mac App Store targets
├── Localizable.xcstrings # string catalog, 7 languages (keys are the Italian strings)
├── Sources/AmuleRemote/ # SHARED CODE (macOS + iOS + visionOS)
│ ├── ECCodes.swift # EC protocol constants
│ ├── ECPacket.swift # tag/packet serialization
│ ├── ECClient.swift # TCP client + auth handshake
│ ├── Models.swift # data models (downloads, servers, …)
│ ├── RemotePrefs.swift # remote amuled preferences
│ ├── AppState.swift # application logic
│ ├── Notifier.swift # local notifications
│ ├── BackgroundMonitor.swift # checks while disconnected
│ ├── IconColor.swift # app icon colors
│ ├── PasswordField.swift # password field with show/hide
│ ├── Keychain.swift # credential storage (local or iCloud-synchronizable)
│ ├── OfflineCache.swift # JSON snapshot per server for the Offline mode
│ ├── OfflineBanner.swift # "Offline · data updated at…" banner
│ ├── CloudSync.swift # iCloud Key-Value Storage sync of the profiles
│ ├── QuickActions.swift # quick-action router (icon menu, Dock, menus)
│ ├── AboutView.swift # About section, license text, links
│ ├── MacQuickActions.swift # Dock menu, activity monitor, About panel (macOS)
│ ├── App.swift / *View.swift # macOS interface (SwiftUI)
├── iOS/ # iPhone/iPad/visionOS interface (SwiftUI)
├── TV/ # Apple TV interface and brand assets
├── Watch/ # Apple Watch app
├── Vision/ # visionOS assets (layered icons)
├── Mac/ # Mac App Store assets and Dock icons
├── SupportFiles/ # extra Info.plist keys per target
└── docs/ # privacy page (GitHub Pages)
The EC engine (ECCodes, ECPacket, ECClient, Models, RemotePrefs, AppState, Keychain) is shared by every platform; only the interface layer changes.
The source code is released under the MIT License.
aMule Remote · a project by Simone Di Mambro · repository