iOS vs macOS Download Adapters in ipatool: Platform-Specific Differences Explained
The iOS download adapter in ipatool streams IPA files directly without post-processing, while the macOS adapter performs multi-stage decryption, XAR validation, and cleanup for encrypted PKG files.
The ipatool repository (majd/ipatool) provides a Go-based CLI for downloading apps from the App Store. Because iOS and macOS use fundamentally different package formats—unencrypted IPAs versus encrypted PKG files—the tool implements two distinct download adapters that share the same public interface but diverge significantly in their internal pipelines.
Core Architectural Split
Both adapters implement the downloadPackage contract, but their implementations live in separate source files:
- iOS adapter:
pkg/appstore/appstore_download.go - macOS adapter:
pkg/appstore/appstore_download_macos_adapter.go
This separation allows each platform to receive exactly the processing it requires—no more, no less.
iOS Adapter: Direct Streaming
The iOS download adapter follows a minimal path. Since IPA files arrive from Apple's servers already decrypted, the adapter simply streams bytes to disk.
In appstore_download.go, the downloadPackage method delegates directly to downloadFile:
func (t *appstore) downloadPackage(
ctx context.Context,
item downloadItemResult,
destination string,
progress *progressbar.ProgressBar,
) (DownloadOutput, error) {
// Simply stream the file to the destination
if err := t.downloadFile(ctx, item.URL, destination, progress); err != nil {
return DownloadOutput{}, fmt.Errorf("failed to download file: %w", err)
}
return DownloadOutput{DestinationPath: destination}, nil
}
Key characteristics:
- No staging files – writes directly to the final destination path
- No decryption – IPAs are delivered unencrypted
- No validation – assumes HTTP integrity is sufficient
- No cleanup – only the final IPA remains on disk
The metadata request only needs software-platform set to "ios", as verified in appstore_download_package_test.go.
macOS Adapter: Decryption and Validation Pipeline
The macOS download adapter operates a multi-stage pipeline because PKG files are encrypted and require additional processing before they become usable.
In appstore_download_macos_adapter.go, the downloadMacPackage method orchestrates five distinct phases:
func (t *appstore) downloadMacPackage(
ctx context.Context,
item downloadItemResult,
destination string,
hardwareID []byte,
progress *progressbar.ProgressBar,
) (DownloadOutput, error) {
// 1️⃣ Prepare staging paths
encryptedPath := destination + macEncryptedStageSuffix
decryptedPath := destination + macDecryptedStageSuffix
t.cleanupMacPaths(encryptedPath, decryptedPath)
// 2️⃣ Download the encrypted file
if err := t.downloadFile(ctx, item.URL, encryptedPath, progress); err != nil {
return DownloadOutput{}, fmt.Errorf("failed to download file: %w", err)
}
// 3️⃣ Initialise decrypter (loads SAP assets & StoreAgent)
decrypter, err := defaultMacPackageDecrypterFactory(ctx, hardwareID, dpInfo)
if err != nil { /* handle error */ }
// 4️⃣ Decrypt → validate → publish
if err := t.decryptMacPackage(ctx, decrypter, encryptedPath, decryptedPath); err != nil { /* handle error */ }
if err := t.validateMacPackage(decryptedPath); err != nil { /* handle error */ }
if err := t.publishMacPackage(decryptedPath, destination); err != nil { /* handle error */ }
// 5️⃣ Cleanup side‑cars
_ = t.removeLegacyMacSidecars(destination)
return DownloadOutput{DestinationPath: destination}, nil
}
Stage 1: Staging File Preparation
The adapter creates two temporary paths using platform-specific suffixes:
<dest>.ipatool-encrypted– holds the raw encrypted download<dest>.ipatool-decrypted– holds the decrypted package before validation
cleanupMacPaths ensures these are clean before starting.
Stage 2: Encrypted Download
The encrypted PKG streams to the staging location, identical to the iOS HTTP path but with a temporary destination.
Stage 3: Decrypter Initialization
The adapter creates a macPackageDecrypter via defaultMacPackageDecrypterFactory, which:
- Loads SAP assets (StoreAssetPack)
- Opens a StoreAgent connection
- Consumes
dpInfoextracted from SINFs and the caller-providedhardwareID
Stage 4: Decrypt, Validate, and Publish
decryptMacPackage– transforms the encrypted PKG into a decrypted XAR archivevalidateMacPackage– parses the XAR usinggithub.com/blacktop/go-macho/pkg/xarand verifies each entry's checksumpublishMacPackage– moves the validated package to the final destination
Stage 5: Cleanup
cleanupMacPathsremoves staging filesremoveLegacyMacSidecarsdeletes metadata side-cars (.dpInfoand.hwInfowith suffixes defined bymacDPInfoSuffixandmacHWInfoSuffix)
Comparison Summary
| Aspect | iOS Adapter | macOS Adapter |
|---|---|---|
| Primary file | appstore_download.go |
appstore_download_macos_adapter.go |
| Package format | IPA (unencrypted) | PKG (encrypted) |
| Staging files | None | Two temporary paths |
| Decryption | Not required | Required via macPackageDecrypter |
| Validation | None | XAR checksum validation |
| Cleanup | None | Staging files + side-car removal |
| Error sources | HTTP only | Decryption, validation, cleanup stages |
| Platform metadata | software-platform: "ios" |
software-platform: "macos" |
Supporting Files and Test Coverage
appstore_download_macos.go– Core macOS download logic (staging, decryption, XAR validation) called by the adapterappstore_download_package_test.go– Verifies metadata differentiation between platformsappstore_download_macos_adapter_test.go– Validates macOS-specific decryption and cleanup workflows
Summary
- iOS downloads are lightweight – stream directly to disk with no post-processing overhead
- macOS downloads are secure-by-design – require decryption, cryptographic validation, and careful cleanup of temporary artifacts
- Both adapters share the same interface – enabling unified CLI commands while hiding platform complexity
- The architectural split prevents unnecessary overhead for iOS while ensuring macOS packages are properly decrypted and validated
Frequently Asked Questions
Why does macOS require decryption but iOS doesn't?
macOS apps are distributed as encrypted PKG files that must be decrypted using Apple's StoreAgent infrastructure with valid SAP assets and hardware identifiers. iOS IPAs are delivered unencrypted from Apple's servers, so no additional cryptographic processing is needed before installation.
What happens if macOS decryption fails partway through?
The adapter returns a wrapped error with context (e.g., "failed to decrypt macOS package") and leaves staging files in place for debugging. The cleanupMacPaths call at the start of each run ensures fresh temporary files, preventing corruption from interrupted downloads.
Can the iOS adapter handle macOS packages or vice versa?
No—the adapters are strictly separated. The iOS adapter lacks decryption and validation capabilities required for PKG files, while the macOS adapter's staging and cleanup logic would add unnecessary overhead for IPAs. Platform detection uses the software-platform field to route to the correct implementation.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →