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:

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 dpInfo extracted from SINFs and the caller-provided hardwareID

Stage 4: Decrypt, Validate, and Publish

  • decryptMacPackage – transforms the encrypted PKG into a decrypted XAR archive
  • validateMacPackage – parses the XAR using github.com/blacktop/go-macho/pkg/xar and verifies each entry's checksum
  • publishMacPackage – moves the validated package to the final destination

Stage 5: Cleanup

  • cleanupMacPaths removes staging files
  • removeLegacyMacSidecars deletes metadata side-cars (.dpInfo and .hwInfo with suffixes defined by macDPInfoSuffix and macHWInfoSuffix)

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

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →