# iOS vs macOS Download Adapters in ipatool: Platform-Specific Differences Explained

> Explore the differences between iOS and macOS download adapters in ipatool. Understand platform-specific features like direct streaming vs. multi-stage decryption and validation.

- Repository: [Majd/ipatool](https://github.com/majd/ipatool)
- Tags: deep-dive
- Published: 2026-09-06

---

**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`](https://github.com/majd/ipatool/blob/main/pkg/appstore/appstore_download.go)
- **macOS adapter**: [`pkg/appstore/appstore_download_macos_adapter.go`](https://github.com/majd/ipatool/blob/main/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`](https://github.com/majd/ipatool/blob/main/appstore_download.go), the `downloadPackage` method delegates directly to `downloadFile`:

```go
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`](https://github.com/majd/ipatool/blob/main/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`](https://github.com/majd/ipatool/blob/main/appstore_download_macos_adapter.go), the `downloadMacPackage` method orchestrates five distinct phases:

```go
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`](https://github.com/majd/ipatool/blob/main/appstore_download.go) | [`appstore_download_macos_adapter.go`](https://github.com/majd/ipatool/blob/main/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`](https://github.com/majd/ipatool/blob/main/appstore_download_macos.go)** – Core macOS download logic (staging, decryption, XAR validation) called by the adapter
- **[`appstore_download_package_test.go`](https://github.com/majd/ipatool/blob/main/appstore_download_package_test.go)** – Verifies metadata differentiation between platforms
- **[`appstore_download_macos_adapter_test.go`](https://github.com/majd/ipatool/blob/main/appstore_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.