name: using-xtool description: 此技能应在使用xtool(无需Xcode的iOS开发)构建iOS应用、创建xtool项目、添加应用扩展或配置xtool.yml时使用。触发词包括“xtool”、“SwiftPM iOS”、“Linux上开发iOS”、“Windows上开发iOS”、“无需Xcode”、“应用扩展”、“小组件扩展”、“分享扩展”。涵盖项目设置、应用扩展和部署。
使用xtool
概述
xtool是一个跨平台的Xcode替代工具,用于在Linux、Windows和macOS上使用SwiftPM构建iOS应用。它不是XcodeGen、Tuist或Xcode项目文件。
关键点:xtool不是XcodeGen
| xtool使用 | 不是这些 |
|---|---|
xtool.yml |
project.yml、Project.swift |
Package.swift (SwiftPM) |
Xcode项目文件 |
xtool dev |
xtool build、xtool run、xtool generate |
Sources/目录 |
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 # 扩展必需的Info.plist
快速参考:命令
# 项目生命周期
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用于调试
# 设备管理
xtool devices # 列出已连接的设备
xtool install app.ipa # 安装IPA到设备
xtool launch # 启动已安装的应用
xtool uninstall # 从设备卸载应用
# 认证与设置
xtool setup # 完整设置(认证+SDK)
xtool auth login # 使用Apple认证登录
xtool auth status # 检查认证状态
xtool auth logout # 登出
xtool sdk # 管理Darwin Swift SDK
# 开发者服务
xtool ds teams # 列出开发团队
xtool ds certificates # 管理证书
xtool ds profiles # 管理配置文件
xtool.yml格式
最小配置:
version: 1
bundleID: com.example.MyApp
完整选项:
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
添加应用扩展(小组件、分享等)
步骤1:更新Package.swift
添加产品和目标。注意:xtool使用.library(不是.executable)——它将库打包到iOS应用中。
// 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"), // 添加
]
)
步骤2:更新xtool.yml
version: 1
bundleID: com.example.MyApp
product: MyApp
extensions:
- product: MyWidget
infoPath: MyWidget-Info.plist
步骤3:创建扩展Info.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">
<plist version="1.0">
<dict>
<key>NSExtension</key>
<dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.widgetkit-extension</string>
</dict>
</dict>
</plist>
步骤4:创建扩展代码
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)))
}
}
步骤5:构建并运行
xtool dev
常见扩展类型
| 扩展 | NSExtensionPointIdentifier |
|---|---|
| 小组件 (WidgetKit) | com.apple.widgetkit-extension |
| 分享 | com.apple.share-services |
| 操作 | com.apple.ui-services |
| Safari | com.apple.Safari.web-extension |
| 键盘 | com.apple.keyboard-service |
| 今日(已弃用) | com.apple.widget-extension |
故障排除
| 错误 | 解决方案 |
|---|---|
| “不受信任的开发者” | 设置 > 通用 > VPN与设备管理 > 信任 |
| 设备未找到 | 连接USB,运行xtool devices,启用开发者模式 |
| 认证失败 | 运行xtool auth login |
| 首次运行构建失败 | 正常 - SDK模块正在构建。等待完成。 |
资源配置
SwiftPM资源(在包子目录中):
.target(name: "MyApp", resources: [.copy("Blob.png")])
// 访问:Image("Blob", bundle: Bundle.module)
顶级资源(在应用包根目录中):
# xtool.yml
resources:
- Resources/GoogleServices-Info.plist
权限
# 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>
常见错误
| 错误 | 修复 |
|---|---|
使用xtool build |
使用xtool dev build |
使用project.yml |
使用xtool.yml |
使用Extensions/目录 |
使用Sources/(标准SwiftPM) |
| 忘记Package.swift | 扩展需要在Package.swift中有产品+目标 |
| 复杂的扩展Info.plist | 仅需要NSExtension/NSExtensionPointIdentifier |