## File: README.md

# xtool

Cross-platform Xcode replacement. Build and deploy iOS apps with SwiftPM on Linux, Windows, and macOS.

## Overview

xtool is a cross-platform (Linux/WSL/macOS) tool that replicates Xcode functionality with open standards.

✅ Build a SwiftPM package into an iOS app

✅ Sign and install iOS apps

✅ Interact with Apple Developer Services programmatically

## Getting Started

1. Follow the guide to install `xtool`
    - [Installation (Linux/Windows)](https://xtool.sh/documentation/xtooldocs/installation-linux)
    - [Installation (macOS)](https://xtool.sh/documentation/xtooldocs/installation-macos)
2. Create and run your first xtool-powered app by following the [tutorial](https://xtool.sh/tutorials/xtooldocs/first-app)!

## Examples

### Screenshot



### Command line interface

```bash
$ xtool --help
OVERVIEW: Cross-platform Xcode replacement

USAGE: xtool <subcommand>

OPTIONS:
  -h, --help              Show help information.

CONFIGURATION SUBCOMMANDS:
  setup                   Set up xtool for iOS development
  auth                    Manage Apple Developer Services authentication
  sdk                     Manage the Darwin Swift SDK

DEVELOPMENT SUBCOMMANDS:
  new                     Create a new xtool SwiftPM project
  dev                     Build and run an xtool SwiftPM project
  ds                      Interact with Apple Developer Services

DEVICE SUBCOMMANDS:
  devices                 List devices
  install                 Install an ipa file to your device
  uninstall               Uninstall an installed app
  launch                  Launch an installed app

  See 'xtool help <subcommand>' for detailed help.
```

### Library

xtool includes a library that you can use to interact with Apple Developer Services, iOS devices, and more from your own app. You can use this by adding `XKit` as a SwiftPM dependency.

```swift
// package dependency:
.package(url: "https://github.com/xtool-org/xtool", .upToNextMinor(from: "1.2.0"))
// target dependency:
.product(name: "XKit", package: "xtool")
```

---

## File: Documentation/xtool.docc/Appex.md

# Create App Extensions

Add app extensions to your iOS app.

## Overview

iOS includes several [App Extension Points](https://developer.apple.com/documentation/technologyoverviews/app-extensions) to deeply integrate with system functionality: such as Widgets, Share Extensions, and Safari Extensions. With a little setup, you can create App Extensions with xtool.

This guide will show you how to add a **Widget Extension** to your app. The steps are similar for other extensions as well.

> Note: This guide assumes you already have an xtool-based application up and running. If you don't, create one first with <doc:First-app>.

## Step 1: Add a new product

Start by adding a new product declaration to your `Package.swift`.

```diff
  // swift-tools-version: 6.0
  
  import PackageDescription
  
  let package = Package(
      name: "Hello",
      platforms: [.iOS(.v17)],
      products: [
          .library(
              name: "Hello",
              targets: ["Hello"]
          ),
+         .library(
+             name: "HelloWidget",
+             targets: ["HelloWidget"]
+         ),
      ],
      targets: [
          .target(name: "Hello"),
+         .target(name: "HelloWidget"),
      ]
  )
```

## Step 2: Update xtool.yml

Now that we have two products, we'll need to tell xtool which one corresponds to the _application_ and which one corresponds to the _extension_.

You should already have a bare-bones `xtool.yml` file that describes metadata about your package (the bundle ID, at the bare minimum.) We'll add the new information to this file.

```diff
  version: 1
  bundleID: com.example.Hello
+ product: Hello
+ extensions:
+   - product: HelloWidget
+     infoPath: HelloWidget-Info.plist
```

## Step 3: Add an Info.plist

App extensions require an `Info.plist` file that tells the system what kind of extension they are, amongst other things. In the previous step, we promised xtool that this file will be located at `./HelloWidget-Info.plist` in your project. We'll now create this file.

```xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">

<dict>
    <key>NSExtension</key>
    <dict>
        <key>NSExtensionPointIdentifier</key>
        <string>com.apple.widgetkit-extension</string>
    </dict>
</dict>

```

> Note:
>
> The value `com.apple.widgetkit-extension` tells the system that this is a Widget Extension. If you're creating a different type of Extension, your `Info.plist` might look different.
>
> For a list of possible `NSExtensionPointIdentifier` values, see [Apple's documentation](https://developer.apple.com/documentation/bundleresources/information-property-list/nsextension/nsextensionpointidentifier) on the subject. You may also need other keys like `NSExtensionPrincipalClass`: refer to Apple's ~better~ legacy [documentation library](https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/AppExtensionKeys.html) for details.
>
> Finally, note that the above refers to the old paradigm of **Foundation Extensions**. Apple has started moving towards a newer framework called **ExtensionKit** for recent extension types. The main difference for extension consumers is that you replace `NSExtension -> EXAppExtensionAttributes`, and `NSExtensionPointIdentifier -> EXExtensionPointIdentifier`. xtool doesn't support ExtensionKit yet, but it's [planned](https://github.com/xtool-org/xtool/issues/138).

## Step 4: Code your widget 

We can finally write the code for the widget. Create a new file at `Sources/HelloWidget/Widget.swift` with these contents:

```swift
import WidgetKit
import SwiftUI

@main struct Bundle: WidgetBundle {
    var body: some Widget {
        HelloWidget()
    }
}

struct HelloWidget: Widget {
    var body: some WidgetConfiguration {
        StaticConfiguration(
            kind: "HelloWidget",
            provider: Provider()
        ) { entry in
            VStack {
                Text(entry.date, style: .date)
            }
            .containerBackground(.fill.tertiary, for: .widget)
        }
        .configurationDisplayName("HelloWidget")
        .description("This is an example widget.")
    }

    struct Entry: TimelineEntry {
        var date = Date()
    }

    struct Provider: TimelineProvider {
        func placeholder(in context: Context) -> Entry {
            Entry()
        }

        func getSnapshot(
            in context: Context,
            completion: @escaping (Entry) -> Void
        ) {
            completion(Entry())
        }

        func getTimeline(
            in context: Context,
            completion: @escaping (Timeline<Entry>) -> Void
        ) {
            completion(Timeline(
                entries: [Entry()],
                policy: .after(.now + 3600)
            ))
        }
    }
}
```

> Note: Blindly refreshing once an hour isn't a great strategy in practice but it makes for a short snippet. I never said this was a tutorial on writing _good_ WidgetKit code; there's plenty of other resources online if that's your goal, including Apple's own [documentation](https://developer.apple.com/documentation/widgetkit).

Finally, build and run with `xtool dev`. You should be able to locate the widget in your widget library and add it to your home screen!

---

## File: Documentation/xtool.docc/Control.md

# Control your app's structure

Add resources, modify your `Info.plist`, and more. 

## Overview

When building your app, xtool provides many knobs to add resources and modify files in its `.app` bundle. This article explains how to perform some common tasks along these lines.

Many of the tasks here involve modifying the `xtool.yml` file. The default `xtool new` template should generate this file for you. If you don't have it, make sure you create one first.

```bash
cat > xtool.yml << EOF
version: 1
bundleID: com.example.Hello
EOF
```

## Change your bundle ID

You can update your app's bundle ID by editing the `bundleID` field in `xtool.yml`.

> Note:
>
> xtool adds a prefix to your bundle ID when signing and installing the app on a real device. For example, it may update the above bundle id to `XTL-1234.com.example.Hello`. This is because, at least with free accounts (those not enrolled in the Apple Developer Program), two accounts cannot use the same bundle ID. Prefixing ensures that if you share your app with someone else, they won't run into a bundle ID conflict when installing it.
>
> With additional engineering effort, this limitation could be lifted for paid Developer Program users by leveraging wildcard provisioning profiles. If you have a concrete use case for this and/or wish to contribute, please [file an issue](https://github.com/xtool-org/xtool/issues/new).

## Customize Info.plist

xtool automatically generates a bare-bones `Info.plist` file for you. However, you may need to add new `Info.plist` keys or edit existing ones.

To do this, create an `Info.plist` file in your project containing just the keys you want to add/update, and tell xtool about it by adding the `infoPath` key:

```yaml
infoPath: path/to/Info.plist
```

> Tip:
>
> After running `xtool dev [build]`, you can inspect the built app bundle at `./xtool/YourApp.app` to view its `Info.plist` and other resources. 

For example, you might create an `Info.plist` file that looks something like

```plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">

<dict>
    <key>UISupportedInterfaceOrientations</key>
    <array>
        <string>UIInterfaceOrientationLandscapeLeft</string>
        <string>UIInterfaceOrientationLandscapeRight</string>
    </array>
    <key>CFBundleDisplayName</key>
    <string>My App</string>
</dict>

```

- The default `Info.plist` has a `UISupportedInterfaceOrientations` value of portrait. This will replace it with landscape-only.
- We don't include a `CFBundleDisplayName` by default, so this will add the key and specify a display name of "My App".

## Copy resources (SwiftPM)

If you need to include non-code resource files, like images, the easiest way to do so is to use [SwiftPM Resources](https://developer.apple.com/documentation/xcode/bundling-resources-with-a-swift-package). These "just work" with xtool.

For example, if you have `Sources/Hello/Blob.png`, you can include it in your app by updating the `targets` in your `Package.swift` to

```swift
targets: [
    .target(
        name: "Hello",
        resources: [.copy("Blob.png")]
    ),
]
```

Then, to draw it on screen, you can use

```swift
Image("Blob", bundle: Bundle.module)
```

## Copy resources (top level)

SwiftPM resources are copied into `.bundle` directories nested inside your `.app`. Sometimes, you need to place a file in a specific location in the app directory instead.

To achieve this, you can include a `resources` array in your `xtool.yml` containing a list of files that should be directly included at the root level of the app bundle.

For example, if you use Firebase, you usually need to include a [`GoogleServices-Info.plist`](https://firebase.google.com/docs/ios/setup#add-config-file) file in your root bundle. If you saved this file in your project directory at `./Resources/GoogleServices-Info.plist`, you can update your `xtool.yml` with

```yaml
resources:
- Resources/GoogleServices-Info.plist
```

The file will be copied to `Hello.app/GoogleServices-Info.plist`.

## Add an app icon

Say your app icon is saved at `Resources/AppIcon.png`. You can make this your app icon by setting the `iconPath` key in `xtool.yml`:

```yaml
iconPath: Resources/AppIcon.png
```

> Tip:
>
> Your app icon must be a png, and it should ideally have a resolution of 1024x1024px.

## Add entitlements

You may need to add special [entitlements](https://developer.apple.com/documentation/bundleresources/entitlements) to your app, for example you need the `com.apple.developer.homekit` entitlement in order to connect to HomeKit devices.

First create an `App.entitlements` file with the necessary entitlements:

```plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">

<dict>
    <key>com.apple.developer.homekit</key>
    
</dict>

```

Then, tell xtool about it via `xtool.yml`:

```yaml
entitlementsPath: App.entitlements
```

> Troubleshooting:
>
> There are a number of situations in which xtool may not be able to successfully apply an entitlement to your app.
>
> - Some entitlements, such as [Network Extension](https://developer.apple.com/documentation/bundleresources/entitlements/com.apple.developer.networking.networkextension), don't work with free developer accounts. You might need to enrol in one of the Apple Developer Programs to use these.
> - Other entitlements, like [User Notifications Filtering](https://developer.apple.com/documentation/bundleresources/entitlements/com.apple.developer.usernotifications.filtering), require special permission from Apple. If you do have the appropriate permission, these may or may not work with xtool.
> - Many entitlements require specific "Capabilities" to be enabled on your app in the Developer Services portal. xtool handles this for you, but we need to add code to xtool for each Entitlement-Capability mapping. If you run into a Capability that we don't handle yet, please let us know.
>
> Entitlements are a large surface area. If you find any bugs or missing functionality, please [create a GitHub Issue](https://github.com/xtool-org/xtool/issues/new/choose).

---

## File: Documentation/xtool.docc/Installation-Linux.md

# Installation (Linux/Windows)

Set up xtool for iOS app development on Linux or Windows.

## Overview

This article outlines the steps to install `xtool` and begin developing iOS apps on Linux (or Windows via WSL).

## Prerequisites

### WSL (for Windows users)

If you're on Windows, you can use xtool via [Windows Subsystem for Linux](https://learn.microsoft.com/en-us/windows/wsl/install) (WSL).

Once you install WSL, you'll also need to set up USB passthrough. See Microsoft's instructions on [installing USBIPD](https://learn.microsoft.com/en-us/windows/wsl/connect-usb). Make sure you're able to bind your iOS device to WSL via USB.

### Swift

Install the Swift 6.3 toolchain for your Linux distribution from <https://swift.org/install/linux>.

After following the steps there, confirm that Swift is installed correctly:

```bash
swift --version
# should say something like:
# Swift version 6.3 (swift-6.3-RELEASE)
```

### usbmuxd

xtool relies on [usbmuxd](https://github.com/libimobiledevice/usbmuxd) to talk to your iOS device from Linux.

Your Linux distro probably offers this package, and it may be preinstalled. To check if it is, run

```bash
usbmuxd --help
# Usage: usbmuxd [OPTIONS]
# ...
```

If instead you get "command not found", you need to install `usbmuxd` yourself. On Ubuntu/Debian, for example, you can do this with

```bash
sudo apt-get install usbmuxd
```

> Other useful tools:
>
> `usbmuxd` is part of the [libimobiledevice](https://libimobiledevice.org) project. You may want to install other libimobiledevice tools, such as `ideviceinfo`, that offer many ways to interact with your iOS device from the command line. On Ubuntu/Debian, you can run
>
> ```bash
> sudo apt-get install libimobiledevice-utils
> # The following NEW packages will be installed:
> #   libimobiledevice-utils
> # 0 upgraded, 1 newly installed
> ideviceinfo
> # DeviceName: Kabir's iPhone
> # SerialNumber: ...
> # UniqueDeviceID: ...
> # ...
> ```

### Xcode.xip

Download **Xcode 26** from <https://developer.apple.com/download/all/?q=Xcode>. Note the path where `Xcode.xip` is saved.

> Note:
>
> The URL above requires authentication, so make sure to visit it in your browser rather than running `curl`. You'll be asked to log in with your Apple ID and accept the license agreement to download Xcode.

## Installation

### 1. Download xtool

Once you have the prerequisites, download the [latest GitHub Release](https://github.com/xtool-org/xtool/releases/latest) of `xtool.AppImage` for your architecture. Rename it to `xtool` and add it to a location in your `PATH`.

```bash
curl -fL \
  "https://github.com/xtool-org/xtool/releases/latest/download/xtool-$(uname -m).AppImage" \
  -o xtool
chmod +x xtool
sudo mv xtool /usr/local/bin/
```

Confirm that xtool is installed correctly:

```bash
xtool --help
# OVERVIEW: Cross-platform Xcode replacement
# ...
```

### 2. Configure xtool: log in

Perform one-time setup with

```bash
xtool setup
```

You'll be asked to log in:

```
Select login mode
0: API Key (requires paid Apple Developer Program membership)
1: Password (works with any Apple ID but uses private APIs)
Choice (0-1):
```

> Choosing a login mode:
>
> **API Key:** If you have a paid [Apple Developer Program](https://developer.apple.com/programs/enroll/) membership, this is the recommended option. It relies on the public App Store Connect API. You'll want to follow the [instructions](https://developer.apple.com/documentation/appstoreconnectapi/creating-api-keys-for-app-store-connect-api) to generate a **Team Key** with the **App Manager** role.
>
> **Password:** If you aren't enrolled in the paid developer program, you'll want to use password-based authentication. This relies on private Apple APIs to authenticate, so you may want to create a throwaway Apple ID to be extra cautious.

Once you select a login mode, you'll be asked to provide the corresponding credentials (API key or email+password+2FA). Needless to say, *your credentials are only sent to Apple* and nobody else (feel free to build xtool from source and check!)

### 3. Configure xtool: SDK

After you're logged in, you'll be asked to provide the path to the `Xcode.xip` file you downloaded earlier.

```
Choice (0-1): 0
...
Path to Xcode.xip:
```

Enter the path (for example `~/Downloads/Xcode_16.3.xip`) and hit enter. xtool will extract the Xcode XIP to generate and install an iOS Swift SDK for you.

Confirm that it worked:

```bash
swift sdk list
# darwin
```

## Next steps

You're now ready to use xtool! See <doc:First-app>.

---

## File: Documentation/xtool.docc/Installation-macOS.md

# Installation (macOS)

Set up xtool for declarative, Xcode-free iOS app development on macOS.

## Overview

This article outlines the steps to install `xtool` for declarative, SwiftPM-driven iOS app development on macOS.

## Prerequisites

### Xcode

Though we don't rely on the Xcode build system, you still need to install Xcode for the iOS SDK and toolchain. You'll want to [install Xcode](https://developer.apple.com/xcode/) and launch it once, completing any installation prompts.

Confirm that Xcode is set up with the iOS SDK:

```bash
xcrun -sdk iphoneos -show-sdk-path
# /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS18.4.sdk
```

As well as Swift:

```bash
swift --version
# swift-driver version: 1.120.5 Apple Swift version 6.1 (swiftlang-6.1.0.110.21 clang-1700.0.13.3)
# Target: arm64-apple-macosx15.0
```

## Installation

### 1. Download xtool

If you have [Homebrew](https://brew.sh), you can install xtool with

```bash
brew install xtool-org/tap/xtool
```

Otherwise, you can download `xtool.app` from the [latest GitHub release](https://github.com/xtool-org/xtool/releases/latest). After moving it into `/Applications`, launch it and run the suggested script to add the `xtool` command to your `PATH`.

> Note:
>
> If you install xtool manually, you'll also need to check for new GitHub releases and update it yourself. If you're interested in contributing support for auto-updates through Sparkle, pull requests are appreciated. See [issue #34 on GitHub](https://github.com/xtool-org/xtool/issues/34).

Confirm that xtool is installed correctly:

```bash
xtool --help
# OVERVIEW: Cross-platform Xcode replacement
# ...
```

### 2. Configure xtool: log in

Perform one-time setup with

```bash
xtool setup
```

You'll be asked to log in:

```
Select login mode
0: API Key (requires paid Apple Developer Program membership)
1: Password (works with any Apple ID but uses private APIs)
Choice (0-1):
```

> Choosing a login mode:
>
> **API Key:** If you have a paid [Apple Developer Program](https://developer.apple.com/programs/enroll/) membership, this is the recommended option. It relies on the public App Store Connect API. You'll want to follow the [instructions](https://developer.apple.com/documentation/appstoreconnectapi/creating-api-keys-for-app-store-connect-api) to generate a **Team Key** with the **App Manager** role.
>
> **Password:** If you aren't enrolled in the paid developer program, you'll want to use password-based authentication. This relies on private Apple APIs to authenticate, so you may want to create a throwaway Apple ID to be extra cautious.

Once you select a login mode, you'll be asked to provide the corresponding credentials (API key or email+password+2FA). Needless to say, *your credentials are only sent to Apple* and nobody else (feel free to build xtool from source and check!)

## Next steps

You're now ready to use xtool! See <doc:First-app>. (The tutorial is tailored to Linux, but it works the same on macOS.)

---

## File: Documentation/xtool.docc/xtool.md

# xtool

@Metadata {
    @TechnologyRoot
    @TitleHeading("Command-line Tool")
    @CallToAction(url: "https://github.com/xtool-org/xtool", label: "View on GitHub")
}

Cross-platform Xcode replacement.

## Overview

xtool is a cross-platform (Linux/WSL/macOS) tool that replicates Xcode functionality with open standards.

✅ Build a SwiftPM package into an iOS app

✅ Sign and install iOS apps

✅ Interact with Apple Developer Services programmatically



## Topics

### Getting started

- <doc:Installation-Linux>
- <doc:Installation-macOS>
- <doc:First-app>

### Common tasks

- <doc:Control>
- <doc:Appex>

---

## File: Linux/README.md

# xtool + Linux = <3

xtool currently uses AppImage to build its Linux CLI. It is possible to build and run the AppImage on a native Linux host, as well as through Docker (natively or through Docker Desktop on macOS).

## Setup

Make sure `usbmuxd` is installed on your host machine. On macOS, `usbmuxd` is preinstalled.

### Using Docker

You can build and run a container with standard Docker Compose commands, for example:
```
docker compose run --rm xtool
``` 
This will spawn a shell inside the container. The xtool root directory will be bind-mounted at `/xtool`.

To actually access iOS devices from inside Docker, you'll also need to forward the host's `usbmuxd` to a port which the Docker container's libusbmuxd will connect to. Keep this command running on your host machine:
```
socat -dd TCP-LISTEN:27015,range=127.0.0.1/32,reuseaddr,fork UNIX-CLIENT:/var/run/usbmuxd
```

## Building

Simply run `./build.sh` in this directory. This will output an AppImage to `packages/xtool.AppImage`, which can directly be invoked.

---

## File: macOS/README.md

# macOS Packaging

xtool is packaged as a .app bundle on macOS, so that we can add entitlements and use the keychain.

Ideally we'd dogfood xtool to package xtool, but it only supports iOS apps right now. Someday, hopefully.

## Building

1. Install [XcodeGen](https://github.com/yonaskolb/XcodeGen)
2. Run `xcodegen` inside this (`./macOS`) directory
3. Open the Xcode project and build the `XToolMac` scheme