using-xtool
Scannednpx machina-cli add skill aiskillstore/marketplace/using-xtool --openclawUsing xtool
Overview
xtool is a cross-platform Xcode replacement for building iOS apps with SwiftPM on Linux, Windows, and macOS. It is NOT XcodeGen, Tuist, or Xcode project files.
Critical: xtool is NOT XcodeGen
| xtool Uses | NOT These |
|---|---|
xtool.yml | project.yml, Project.swift |
Package.swift (SwiftPM) | Xcode project files |
xtool dev | xtool build, xtool run, xtool generate |
Sources/ directory | Extensions/ directory |
Project Structure
MyApp/
├── Package.swift # SwiftPM package definition
├── xtool.yml # xtool configuration
├── Sources/
│ ├── MyApp/ # Main app target
│ │ ├── MyAppApp.swift
│ │ └── ContentView.swift
│ └── MyWidget/ # Extension target (if any)
│ └── Widget.swift
├── MyApp-Info.plist # Optional custom Info.plist
└── MyWidget-Info.plist # Required for extensions
Quick Reference: Commands
# Project lifecycle
xtool new MyApp # Create new project
xtool new MyApp --skip-setup # Create without running setup
xtool dev # Build + run (same as `xtool dev run`)
xtool dev build # Build only
xtool dev build --ipa # Build IPA file
xtool dev run -s # Run on iOS Simulator (--simulator)
xtool dev run -c release # Release build (--configuration)
xtool dev run -u <udid> # Target specific device (--udid)
xtool dev generate-xcode-project # Generate .xcodeproj for debugging
# Device management
xtool devices # List connected devices
xtool install app.ipa # Install IPA to device
xtool launch # Launch installed app
xtool uninstall # Uninstall app from device
# Authentication & setup
xtool setup # Full setup (auth + SDK)
xtool auth login # Authenticate with Apple
xtool auth status # Check auth status
xtool auth logout # Log out
xtool sdk # Manage Darwin Swift SDK
# Developer Services
xtool ds teams # List development teams
xtool ds certificates # Manage certificates
xtool ds profiles # Manage provisioning profiles
xtool.yml Format
Minimal:
version: 1
bundleID: com.example.MyApp
Full options:
version: 1
bundleID: com.example.MyApp
product: MyApp # Which SwiftPM product is main app
infoPath: MyApp-Info.plist # Custom Info.plist (merged)
iconPath: Resources/AppIcon.png # App icon (1024x1024 PNG)
entitlementsPath: App.entitlements
resources: # Files copied to app bundle root
- Resources/GoogleServices-Info.plist
extensions: # App extensions
- product: MyWidget
infoPath: MyWidget-Info.plist
Adding App Extensions (Widgets, Share, etc.)
Step 1: Update Package.swift
Add BOTH a product AND a target. Note: xtool uses .library (not .executable) - it bundles the library into an iOS app.
// swift-tools-version: 6.0
import PackageDescription
let package = Package(
name: "MyApp",
platforms: [.iOS(.v17)],
products: [
.library(name: "MyApp", targets: ["MyApp"]),
.library(name: "MyWidget", targets: ["MyWidget"]), // ADD
],
targets: [
.target(name: "MyApp"),
.target(name: "MyWidget"), // ADD
]
)
Step 2: Update xtool.yml
version: 1
bundleID: com.example.MyApp
product: MyApp
extensions:
- product: MyWidget
infoPath: MyWidget-Info.plist
Step 3: Create Extension Info.plist
Minimal required (just the extension type):
<?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">
<plist version="1.0">
<dict>
<key>NSExtension</key>
<dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.widgetkit-extension</string>
</dict>
</dict>
</plist>
Step 4: Create Extension Code
Sources/MyWidget/Widget.swift:
import WidgetKit
import SwiftUI
@main struct MyWidgetBundle: WidgetBundle {
var body: some Widget { MyWidget() }
}
struct MyWidget: Widget {
var body: some WidgetConfiguration {
StaticConfiguration(kind: "MyWidget", provider: Provider()) { entry in
Text(entry.date, style: .date)
.containerBackground(.fill.tertiary, for: .widget)
}
.configurationDisplayName("My 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 (Entry) -> Void) {
completion(Timeline(entries: [Entry()], policy: .after(.now + 3600)))
}
}
Step 5: Build and Run
xtool dev
Common Extension Types
| Extension | NSExtensionPointIdentifier |
|---|---|
| Widget (WidgetKit) | com.apple.widgetkit-extension |
| Share | com.apple.share-services |
| Action | com.apple.ui-services |
| Safari | com.apple.Safari.web-extension |
| Keyboard | com.apple.keyboard-service |
| Today (deprecated) | com.apple.widget-extension |
Troubleshooting
| Error | Solution |
|---|---|
| "Untrusted Developer" | Settings > General > VPN & Device Management > Trust |
| Device not found | Connect USB, run xtool devices, enable Developer Mode |
| Auth failed | Run xtool auth login |
| Build fails on first run | Normal - SDK modules building. Wait for completion. |
Resources Configuration
SwiftPM resources (in bundle subdirectory):
.target(name: "MyApp", resources: [.copy("Blob.png")])
// Access: Image("Blob", bundle: Bundle.module)
Top-level resources (in app bundle root):
# xtool.yml
resources:
- Resources/GoogleServices-Info.plist
Entitlements
# xtool.yml
entitlementsPath: App.entitlements
<!-- App.entitlements -->
<?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">
<plist version="1.0">
<dict>
<key>com.apple.developer.homekit</key>
<true/>
</dict>
</plist>
Common Mistakes
| Mistake | Fix |
|---|---|
Using xtool build | Use xtool dev build |
Using project.yml | Use xtool.yml |
Using Extensions/ dir | Use Sources/ (standard SwiftPM) |
| Forgetting Package.swift | Extensions need product + target in Package.swift |
| Complex extension Info.plist | Only NSExtension/NSExtensionPointIdentifier required |
Source
git clone https://github.com/aiskillstore/marketplace/blob/main/skills/2389-research/using-xtool/SKILL.mdView on GitHub Overview
xtool is a cross-platform Xcode replacement for building iOS apps with SwiftPM on Linux, Windows, and macOS. It uses xtool.yml and SwiftPM packages rather than Xcode project files. This matters when you need Xcode-free project setup, app extensions, and deployment workflows.
How This Skill Works
It relies on a SwiftPM package (Package.swift) and an xtool.yml config to define the main app and any extensions. The CLI handles creating the project, building, running on a device or simulator, and exporting an IPA, while ensuring Xcode project files are not used.
When to Use It
- Building iOS apps on Linux, Windows, or macOS without Xcode
- Adding a widget, share, or other extension to a SwiftPM-based iOS app
- Configuring xtool.yml with bundleID, resources, and extension entries
- Generating a debugging Xcode project with xtool for occasional IDE-based work
- Building, running, and exporting IPA for deployment or testing
Quick Start
- Step 1: Create a new project with xtool new MyApp
- Step 2: Add an extension by updating Package.swift and xtool.yml as shown
- Step 3: Build and run or export the IPA with xtool dev run or xtool dev build --ipa
Best Practices
- Add both a library product and a target for each extension in Package.swift
- Declare extensions in xtool.yml under extensions and point to the correct infoPath
- Keep extension Info.plist files (e.g., MyWidget-Info.plist) in place and referenced in xtool.yml
- Test builds locally with xtool dev run on a simulator before ipa export
- Use xtool dev generate-xcode-project only when you explicitly need an Xcode workflow
Example Use Cases
- Create a new app with xtool new MyApp, add a widget extension, and configure xtool.yml and Package.swift to include the extension
- Build an IPA for distribution with xtool dev build --ipa and install it to a device
- Run the app on iOS Simulator using xtool dev run -s
- Generate an Xcode project for debugging using xtool dev generate-xcode-project
- Authenticate with Apple and manage certificates and provisioning profiles via xtool setup and xtool ds profiles