using-xtool

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Using xtool

使用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.
xtool是一款跨平台的Xcode替代工具,可在Linux、Windows和macOS系统上通过SwiftPM构建iOS应用。它并非XcodeGen、Tuist或Xcode项目文件。

Critical: xtool is NOT XcodeGen

重点说明:xtool 并非 XcodeGen

xtool UsesNOT 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
xtool 使用的内容不使用的内容
xtool.yml
project.yml
,
Project.swift
Package.swift
(SwiftPM)
Xcode项目文件
xtool dev
xtool build
,
xtool run
,
xtool generate
Sources/
目录
Extensions/
目录

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
MyApp/
├── Package.swift          # SwiftPM包定义文件
├── xtool.yml              # xtool配置文件
├── Sources/
│   ├── MyApp/             # 主应用目标
│   │   ├── MyAppApp.swift
│   │   └── ContentView.swift
│   └── MyWidget/          # 扩展目标(如有)
│       └── Widget.swift
├── MyApp-Info.plist       # 可选的自定义Info.plist
└── MyWidget-Info.plist    # 扩展必备文件

Quick Reference: Commands

快速参考:命令

bash
undefined
bash
undefined

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
xtool new MyApp # 创建新项目 xtool new MyApp --skip-setup # 创建项目但不执行初始化 xtool dev # 构建并运行(等同于
xtool dev run
) xtool dev build # 仅执行构建 xtool dev build --ipa # 构建IPA文件 xtool dev run -s # 在iOS模拟器运行(--simulator参数) xtool dev run -c release # 构建发布版本(--configuration参数) xtool dev run -u <udid> # 指定目标设备运行(--udid参数) xtool dev generate-xcode-project # 生成.xcodeproj文件用于调试

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
xtool devices # 列出已连接的设备 xtool install app.ipa # 将IPA安装到设备 xtool launch # 启动已安装的应用 xtool uninstall # 从设备卸载应用

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
xtool setup # 完整初始化(认证+SDK配置) xtool auth login # 登录Apple账号进行认证 xtool auth status # 检查认证状态 xtool auth logout # 登出账号 xtool sdk # 管理Darwin Swift SDK

Developer Services

开发者服务

xtool ds teams # List development teams xtool ds certificates # Manage certificates xtool ds profiles # Manage provisioning profiles
undefined
xtool ds teams # 列出开发团队 xtool ds certificates # 管理证书 xtool ds profiles # 管理描述文件
undefined

xtool.yml Format

xtool.yml 格式

Minimal:
yaml
version: 1
bundleID: com.example.MyApp
Full options:
yaml
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
最简配置:
yaml
version: 1
bundleID: com.example.MyApp
完整配置选项:
yaml
version: 1
bundleID: com.example.MyApp
product: MyApp                    # 指定哪个SwiftPM产品作为主应用
infoPath: MyApp-Info.plist        # 自定义Info.plist(会进行合并)
iconPath: Resources/AppIcon.png   # 应用图标(1024x1024 PNG格式)
entitlementsPath: App.entitlements
resources:                        # 复制到应用包根目录的文件
  - Resources/GoogleServices-Info.plist
extensions:                       # 应用扩展
  - product: MyWidget
    infoPath: MyWidget-Info.plist

Adding App Extensions (Widgets, Share, etc.)

添加应用扩展(Widget、Share等)

Step 1: Update Package.swift

步骤1:更新Package.swift

Add BOTH a product AND a target. Note: xtool uses
.library
(not
.executable
) - it bundles the library into an iOS app.
swift
// 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
    ]
)
同时添加产品(product)和目标(target)。注意:xtool使用
.library
(而非
.executable
)——它会将库打包到iOS应用中。
swift
// 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"]),  // 添加此行
    ],
    targets: [
        .target(name: "MyApp"),
        .target(name: "MyWidget"),  // 添加此行
    ]
)

Step 2: Update xtool.yml

步骤2:更新xtool.yml

yaml
version: 1
bundleID: com.example.MyApp
product: MyApp
extensions:
  - product: MyWidget
    infoPath: MyWidget-Info.plist
yaml
version: 1
bundleID: com.example.MyApp
product: MyApp
extensions:
  - product: MyWidget
    infoPath: MyWidget-Info.plist

Step 3: Create Extension Info.plist

步骤3:创建扩展的Info.plist

Minimal required (just the extension type):
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">
<plist version="1.0">
<dict>
    <key>NSExtension</key>
    <dict>
        <key>NSExtensionPointIdentifier</key>
        <string>com.apple.widgetkit-extension</string>
    </dict>
</dict>
</plist>
必备最简配置(仅需指定扩展类型):
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">
<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

步骤4:编写扩展代码

Sources/MyWidget/Widget.swift
:
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)))
    }
}
Sources/MyWidget/Widget.swift
:
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

步骤5:构建并运行

bash
xtool dev
bash
xtool dev

Common Extension Types

常见扩展类型

ExtensionNSExtensionPointIdentifier
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
扩展类型NSExtensionPointIdentifier
Widget(WidgetKit)
com.apple.widgetkit-extension
分享扩展
com.apple.share-services
动作扩展
com.apple.ui-services
Safari扩展
com.apple.Safari.web-extension
键盘扩展
com.apple.keyboard-service
Today扩展(已废弃)
com.apple.widget-extension

Troubleshooting

故障排查

ErrorSolution
"Untrusted Developer"Settings > General > VPN & Device Management > Trust
Device not foundConnect USB, run
xtool devices
, enable Developer Mode
Auth failedRun
xtool auth login
Build fails on first runNormal - SDK modules building. Wait for completion.
错误信息解决方法
"Untrusted Developer"前往设置 > 通用 > VPN与设备管理 > 信任开发者
设备未找到连接USB线,运行
xtool devices
,开启设备的开发者模式
认证失败运行
xtool auth login
重新认证
首次运行构建失败属于正常现象,SDK模块正在编译,等待完成即可

Resources Configuration

资源配置

SwiftPM resources (in bundle subdirectory):
swift
.target(name: "MyApp", resources: [.copy("Blob.png")])
// Access: Image("Blob", bundle: Bundle.module)
Top-level resources (in app bundle root):
yaml
undefined
SwiftPM资源(会放在应用包的子目录中):
swift
.target(name: "MyApp", resources: [.copy("Blob.png")])
// 访问方式:Image("Blob", bundle: Bundle.module)
顶层资源(会放在应用包的根目录中):
yaml
undefined

xtool.yml

xtool.yml

resources:
  • Resources/GoogleServices-Info.plist
undefined
resources:
  • Resources/GoogleServices-Info.plist
undefined

Entitlements

权限配置

yaml
undefined
yaml
undefined

xtool.yml

xtool.yml

entitlementsPath: App.entitlements

```xml
<!-- 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>
entitlementsPath: App.entitlements

```xml
<!-- 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

常见错误

MistakeFix
Using
xtool build
Use
xtool dev build
Using
project.yml
Use
xtool.yml
Using
Extensions/
dir
Use
Sources/
(standard SwiftPM)
Forgetting Package.swiftExtensions need product + target in Package.swift
Complex extension Info.plistOnly NSExtension/NSExtensionPointIdentifier required
错误操作修复方法
使用
xtool build
改用
xtool dev build
使用
project.yml
改用
xtool.yml
使用
Extensions/
目录
改用
Sources/
目录(标准SwiftPM结构)
忘记配置Package.swift扩展需要在Package.swift中添加对应的product和target
编写复杂的扩展Info.plist仅需保留NSExtension/NSExtensionPointIdentifier配置即可