项目结构
标准 Skip 项目
Section titled “标准 Skip 项目”Skip 应用项目是通过 skip init 或 skip create 创建的项目。本质上,它是一个标准的 Swift Package Manager 项目 ↗,包含顶层的 Package.swift 以及 Sources/ 和 Tests/ 目录。
Skip 在此基础上增加了共享的 Skip.env 元数据,以及 Darwin/ 和 Android/ 子文件夹,分别容纳各自平台的构建系统以及平台特定的元数据和部署信息。
Skip 项目可以使用 swift build 本地构建,使用 swift test 测试。此外,Skip 特有的命令如 skip export 可用于构建平台特定的二进制文件(iOS 的 .ipa 和 Android 的 .apk),以便通过各种应用商店分发。
有四个标准示例可以参考,它们展示了标准 Skip 项目的结构:
- HelloSkip:转译的 Skip Lite 应用项目基础示例。
- HowdySkip:编译的 Skip Fuse 应用项目基础示例。
- AhoySkipper:多模块 Skip Fuse 应用项目。
- HiyaSkip:使用编译(Skip Fuse)UI 模块桥接到转译(Skip Lite)模型层模块的 Skip 应用项目示例。
文件系统布局
Section titled “文件系统布局”以一个包含两个模块和测试用例的“Ahoy Skipper”Skip Fuse 应用为例,可以通过以下 skip init 命令(或等效的交互式 skip create 命令)生成:
skip init --native-app --bridged --module-tests --show-tree --appid=ahoy.skipper ahoy-skipper AhoySkipper SkipperModel应用的精简文件系统布局如下所示:
- README.md
- Skip.env 应用元数据(名称、版本、appid 等)
- Package.swift 标准 SwiftPM 构建文件
- Project.xcworkspace Darwin 项目的 Xcode 工作区容器
文件夹Sources
文件夹AhoySkipper 应用模块,包含 SwiftUI 源码
- AhoySkipperApp.swift 应用入口
- ContentView.swift 顶层 SwiftUI 视图
文件夹Resources
- Localizable.xcstrings 本地化 UI 字符串
- Module.xcassets 图片及其他资源
文件夹Skip
- skip.yml Skip 模块配置
文件夹SkipperModel
AhoySkipper使用的模型代码- ViewModel.swift Observable 模型代码
文件夹Resources
- Localizable.xcstrings 本地化模型字符串
文件夹Skip
- skip.yml Skip 模块配置
文件夹Tests
文件夹SkipperModelTests
- SkipperModelTests.swift XCUnit 测试用例
文件夹Resources
- TestData.json 测试数据
文件夹Skip
- skip.yml Skip 模块配置
文件夹Android
- gradle.properties
- settings.gradle.kts 顶层 Gradle 配置
文件夹app
- build.gradle.kts Gradle 构建文件
- proguard-rules.pro Proguard/R8 优化配置
文件夹src
文件夹main
- AndroidManifest.xml Android 应用元数据
文件夹kotlin
- Main.kt Android 应用入口
文件夹res
文件夹mipmap-hdpi Android 应用图标
- …
文件夹fastlane App Store 配置
- Appfile
- Fastfile
文件夹metadata
文件夹android
文件夹en-US
- title.txt
- short_description.txt
- full_description.txt
- …
文件夹Darwin
- AhoySkipper.xcconfig Xcode 构建配置
- AhoySkipper.xcodeproj Xcode 项目
- Info.plist iOS 应用元数据
- Entitlements.plist iOS 权限
文件夹Assets.xcassets iOS 应用资源
文件夹AppIcon.appiconset iOS 应用图标
文件夹Sources
- Main.swift Darwin 应用入口
文件夹fastlane Play Store 配置
- Appfile
- Deliverfile
- Fastfile
文件夹metadata
- app_privacy_details.json
- rating.json
文件夹en-US
- title.txt
- subtitle.txt
- description.txt
- …
Package.swift
Section titled “Package.swift”此文件是 Swift Package Manager (SwiftPM) 的清单文件。它定义了项目名称、产品(库或可执行文件)、依赖和目标。在 Skip 项目中,它还声明了 skipstone 插件,该插件负责将 iOS 项目转换为等效的 Android 项目。
- 示例:https://github.com/skiptools/skipapp-ahoy/blob/main/Package.swift ↗
- 参考:Swift Package Manager — Package.swift 清单文件 ↗
Project.xcworkspace
Section titled “Project.xcworkspace”此文件是 Xcode 工作区,用于将多个 Xcode 项目和 Swift 包组织在一起的容器。在 Skip 中,它允许你在单个界面中管理主要的 Darwin 应用项目和共享 Swift 模块,确保共享代码的更改立即反映在应用构建中。
生成的 Project.xcworkspace 仅包含 Darwin/ProjectName.xcodeproj,但可以按贡献指南中的描述添加额外的项目和框架。
- 示例:https://github.com/skiptools/skipapp-ahoy/blob/main/Project.xcworkspace/contents.xcworkspacedata ↗
- 参考:Xcode 帮助 — 工作区概念 ↗
Skip.env
Section titled “Skip.env”这是 Skip 项目的共享配置环境文件。它包含项目范围的元数据键值对,如 PRODUCT_NAME、MARKETING_VERSION 和 PRODUCT_BUNDLE_IDENTIFIER。这些变量被 Xcode 构建过程和 Gradle 构建过程共同使用,以确保 iOS 和 Android 二进制文件之间的一致性。它被 Darwin/ProjectName.xcconfig 和 Android/settings.gradle.kts 文件共同引用。
Sources/<Module>/<Module>App.swift
Section titled “Sources/<Module>/<Module>App.swift”共享应用入口点,负责启动和配置应用,持有主要的 ContentView.swift 并包含应用生命周期回调。这将是 Darwin Main.swift 和 Android Main.kt 共同调用的通用应用逻辑。
Sources/<Module>/ContentView.swift
Section titled “Sources/<Module>/ContentView.swift”此文件是一个 SwiftUI View 文件,表示应用的主要用户界面。在 Skip 项目中,此文件中的 SwiftUI 代码将被转换为 Jetpack Compose 用于 Android,允许单个声明式 UI 定义驱动两个平台。
- 示例:https://github.com/skiptools/skipapp-ahoy/blob/main/Sources/AhoySkipper/ContentView.swift ↗
- 参考:SwiftUI 视图基础 ↗
Sources/<Module>/Skip/skip.yml
Section titled “Sources/<Module>/Skip/skip.yml”这是 Skip 特有的 YAML 配置文件。它控制模块级设置,如 Skip 模式、桥接配置和 Android 特定依赖。
Sources/<Module>/Resources/Module.xcassets
Section titled “Sources/<Module>/Resources/Module.xcassets”这是位于特定 Swift 模块中的资源目录。它存放该模块范围内的图片、数据和本地化字符串等资源。Skip 确保这些资源被处理并提供给 iOS 包和 Android res 目录。
- 示例:https://github.com/skiptools/skipapp-ahoy/tree/main/Sources/AhoySkipper/Resources/Module.xcassets ↗
- 参考:Apple 开发者文档 — 管理资源目录 ↗
Sources/<Module>/Resources/Localizable.xcstrings
Section titled “Sources/<Module>/Resources/Localizable.xcstrings”这是用于管理翻译的字符串目录文件。它是 Darwin 本地化的现代格式。Skip 在构建过程中将这些字符串转换为 Android 资源,从而在两个平台上提供翻译一致性。
- 示例:https://github.com/skiptools/skipapp-ahoy/blob/main/Sources/AhoySkipper/Resources/Localizable.xcstrings ↗
- 参考:Apple 开发者文档 — 本地化界面文本 ↗
Sources/<Module>/ViewModel.swift
Section titled “Sources/<Module>/ViewModel.swift”此文件包含应用的业务逻辑和状态,通常使用 @Observable 宏。
Tests/<Module>/SkipperModelTests.swift
Section titled “Tests/<Module>/SkipperModelTests.swift”此文件包含用于验证模块逻辑的测试用例。对于 Skip Fuse 项目,这些测试可以直接使用 skip android test 运行;对于 Skip Lite 项目,测试将被转译为 Kotlin,可以使用 skip test 运行。
Darwin/ProjectName.xcconfig
Section titled “Darwin/ProjectName.xcconfig”这是 Xcode 构建配置文件。它提供了一种以纯文本格式定义构建设置(如编译器标志或搜索路径)的方式,而不是在不透明的 .xcodeproj 文件中管理,从而更容易管理环境特定的设置。
此配置文件会自动包含根级别的 Skip.env 文件,因此 Darwin 和 Android 项目可以共享通用属性。
- 示例:https://github.com/skiptools/skipapp-ahoy/blob/main/Darwin/AhoySkipper.xcconfig ↗
- 参考:Apple 开发者文档 — 配置设置文件(xcconfig)格式 ↗
Darwin/ProjectName.xcodeproj
Section titled “Darwin/ProjectName.xcodeproj”这是 Xcode 项目文件,一个包含 Darwin(iOS/macOS)版本应用的配置、构建目标和文件引用的包。它是面向苹果生态系统的开发者的主要入口点。
此项目将自动被生成的 Project.xcworkspace 包含。
- 示例:https://github.com/skiptools/skipapp-ahoy/tree/main/Darwin/AhoySkipper.xcodeproj ↗
- 参考:Apple 开发者文档 — Xcode 项目结构 ↗
Darwin/Assets.xcassets
Section titled “Darwin/Assets.xcassets”这是 Darwin 应用的主要资源目录。它通常包含 iOS 目标所需的应用图标集、启动图片和全局颜色资源。
Darwin/Assets.xcassets/AppIcon.appiconset/AppIcon.png
Section titled “Darwin/Assets.xcassets/AppIcon.appiconset/AppIcon.png”这是 Darwin 应用图标的源图片文件。
- 示例:https://github.com/skiptools/skipapp-ahoy/tree/main/Darwin/Assets.xcassets/AppIcon.appiconset ↗
- 参考:Apple 开发者文档 — 应用图标 ↗
Darwin/Entitlements.plist
Section titled “Darwin/Entitlements.plist”这是一个 XML 属性列表,定义了 iOS 应用的能力和权限,如推送通知或应用权限。
- 示例:https://github.com/skiptools/skipapp-ahoy/blob/main/Darwin/Entitlements.plist ↗
- 参考:Apple 开发者文档 — 权限配置 ↗
Darwin/Info.plist
Section titled “Darwin/Info.plist”此文件是 iOS 应用的信息属性列表。它包含重要的配置数据,如包标识符、支持的方向和所需的硬件功能。
- 示例:https://github.com/skiptools/skipapp-ahoy/blob/main/Darwin/Info.plist ↗
- 参考:Apple 开发者文档 — Info.plist 键 ↗
Darwin/Sources/Main.swift
Section titled “Darwin/Sources/Main.swift”此文件作为 Darwin 入口点。它包含 @main 属性,用于初始化 iOS/macOS 版本的 SwiftUI App 生命周期。
此文件的内容会非常简洁。与其 Kotlin Main.kt 对应文件一样,它主要调用共享的 <Module>App.swift。
Darwin/fastlane/
Section titled “Darwin/fastlane/”此目录包含 Fastlane 配置文件(Deliverfile、Fastfile、Appfile),用于自动化 iOS 应用的部署流程,特别是上传到 Apple App Store 和 TestFlight。
- 示例:https://github.com/skiptools/skipapp-ahoy/tree/main/Darwin/fastlane ↗
- 参考:Fastlane — 将元数据和二进制文件上传到 App Store Connect ↗
Android/app/src/main/kotlin/Main.kt
Section titled “Android/app/src/main/kotlin/Main.kt”这是 Android 入口点。它包含 MainActivity 类,作为 Android 应用的主要 Activity。此文件的内容会非常简洁。与其 Swift Main.swift 对应文件一样,它主要调用共享的 <Module>App.swift。
- 示例:https://github.com/skiptools/skipapp-ahoy/blob/main/Android/app/src/main/kotlin/Main.kt ↗
- 参考:Android 开发者文档 — Activity ↗
Android/settings.gradle.kts
Section titled “Android/settings.gradle.kts”这是一个用 Kotlin DSL 编写的 Gradle Settings 文件。它定义了项目级设置并包含构成 Android 构建的子模块(如 :app)。
- 示例:https://github.com/skiptools/skipapp-ahoy/blob/main/Android/settings.gradle.kts ↗
- 参考:Gradle — 设置文件 ↗
Android/app/build.gradle.kts
Section titled “Android/app/build.gradle.kts”这是模块级 Gradle 构建文件。它定义了 Android 特定的构建配置,包括 SDK 版本、签名配置和依赖(包括转译的 Swift 模块)。
- 示例:https://github.com/skiptools/skipapp-ahoy/blob/main/Android/app/build.gradle.kts ↗
- 参考:Android 开发者文档 — 配置构建 ↗
Android/app/proguard-rules.pro
Section titled “Android/app/proguard-rules.pro”此文件包含 R8/ProGuard 规则,用于代码缩减和优化。它确保 Kotlin 代码及其依赖在生产环境中被正确压缩。
Android/src/main/AndroidManifest.xml
Section titled “Android/src/main/AndroidManifest.xml”这是 Android Manifest 文件。它向 Android 构建工具、Android OS 和 Google Play 描述应用的基本信息,包括权限和入口 Activity。
- 示例:https://github.com/skiptools/skipapp-ahoy/blob/main/Android/app/src/main/AndroidManifest.xml ↗
- 参考:Android 开发者文档 — 应用清单概览 ↗
Android/src/main/kotlin/res/
Section titled “Android/src/main/kotlin/res/”此文件夹包含各种密度 Android 屏幕的应用图标。
- 示例:https://github.com/skiptools/skipapp-ahoy/tree/main/Android/app/src/main/res ↗
- 参考:Android 开发者文档 — 应用图标 ↗
Android/fastlane/
Section titled “Android/fastlane/”此目录包含 Fastlane 配置文件(Appfile、Fastfile),用于自动化 Android 应用的部署流程,特别是上传到 Google Play Store。
- 示例:https://github.com/skiptools/skipapp-ahoy/tree/main/Android/fastlane ↗
- 参考:Fastlane — 将元数据、截图和二进制文件上传到 Google Play ↗
Skip 的快速入门指南演示了使用 skip create 交互式生成基础应用项目,但还有多种其他项目类型。本文档讨论了可以用 Skip 创建和构建的各种不同项目类型。
使用 skip init 创建应用
Section titled “使用 skip init 创建应用”skip init 命令是交互式 skip create 命令的替代方案,可以通过单个非交互命令初始化应用。运行 skip init --help 或查看命令行参考获取 skip init 选项的完整列表。
创建标准应用
Section titled “创建标准应用”使用以下命令创建新的双平台应用项目:
skip init --native-app --appid=bundle.id project-name AppName传入 --open-xcode 参数可以立即在 Xcode 中打开项目。例如:
skip init --open-xcode --native-app --appid=bundle.id.HelloSkip hello-skip HelloSkip这将创建一个 hello-skip/ 文件夹,包含一个新的 SwiftPM 包,其中有一个名为 HelloSkip 的模块,以及名为 Darwin 和 Android 的文件夹和共享的 Skip.env 应用配置文件。Darwin 文件夹将包含一个带有 HelloSkip 目标的 HelloSkip.xcodeproj 项目,你可以在 Xcode 中打开它。
创建多模块应用
Section titled “创建多模块应用”Skip 的设计支持并鼓励使用多模块项目。你可以在 skip init 命令链末尾指定额外的模块名来创建模块化项目。例如:
skip init --native-app --appid=bundle.id.HelloSkip multi-project HelloSkip HelloModel HelloCore此命令将创建一个包含三个模块的 SwiftPM 项目:HelloSkip、HelloModel 和 HelloCore。这种模块创建的启发式规则是,所有模块都依赖于其后续的兄弟模块,第一个模块(HelloSkip)初始依赖 SkipFuseUI,第二个模块依赖 SkipFuse 和 SkipModel。Package.swift 文件可以手动编辑以调整依赖,或添加对新框架的依赖。
创建独立的 iOS 和 Android 应用
Section titled “创建独立的 iOS 和 Android 应用”你可以选择使用双平台框架共享功能,但创建独立的 iOS 和 Android 应用。例如,一些开发团队希望共享通用的模型和业务逻辑层,但为每个平台单独编写 UI。
Travel Posters 示例应用提供了这种模式的例子。你可以在这篇博客文章中了解更多信息。它具有以下顶层条目:
travel-posters-model:此 SwiftPM 包构建一个双平台框架,包含 iOS 和 Android 应用的通用模型层。Skip 确保你用 Swift 编写的@Observable类型不仅能驱动 SwiftUI 界面,还能驱动 Compose 界面。详见开发文档。iOS:包含 TravelPosters iOS 应用和 Xcode 项目的目录,已将travel-posters-model作为包依赖。Android:包含应用 Android 版本的目录。Android/lib目录包含travel-posters-model及其依赖的各种 Skip 框架的导出归档。TravelPostersNative.xcworkspace:包含 iOS 应用和travel-posters-model包的工作区。
使用 TravelPostersNative.xcworkspace 来迭代 iOS 应用和/或共享模型层。要将最新的 travel-posters-model 代码提供给 Android 应用:
skip export --project travel-posters-model -d Android/lib/debug/ --debugskip export --project travel-posters-model -d Android/lib/release/ --release有多种方法可以自动化此过程,从简单脚本到 git 子模块,再到将 Skip 生成的 Android travel-posters-model 输出发布到本地 Maven 仓库。使用最适合你团队工作流的系统。
其他注意事项:
- 更新导出的库后,你可能需要在 Android Studio 中执行“Sync Project with Gradle Files”。
- 使用具有对额外 Android 库的传递依赖的导出库函数可能导致运行时错误。你必须确保所有传递依赖都在你自己的应用的
build.gradle.kts中。
创建双平台框架
Section titled “创建双平台框架”SkipFuse 应用可以依赖纯 SwiftPM 包以及纯 Kotlin/Java Android 库。详见依赖章节。本节描述如何创建一个 Swift 框架,使其 API 可以同时从 Swift 和 Kotlin 中使用。最常见的用例是为 SkipLite 转译应用或独立的 iOS 和 Android 应用提供支持。
双平台 Skip 框架是封装通用功能的纯 SwiftPM 包。框架比应用项目更简单,因为它们不需要 Darwin/ 和 Android/ 文件夹。
可以使用以下命令生成新的框架项目:
skip init --native-model lib-name ModuleName这将创建一个新的 lib-name 文件夹,包含一个 Package.swift,其中有 ModuleName 和 ModuleNameTests 目标。
生成的包可以在 Xcode 中打开,你可以用它来构建和运行单元测试。或者在持续集成过程中使用终端的 swift build 和 swift test 进行无头测试。
传递给 skip init 的 --native-model 选项将配置 Skip 自动将我们模型的 public API 从编译的 Swift 桥接到 Android 的 ART ↗ Java 运行时。这是通过每个 Skip 模块中包含的 skip.yml 配置文件完成的。详见桥接和 skip.yml 的文档。
Skip 框架结构
Section titled “Skip 框架结构”Skip 框架的结构与其他 SwiftPM 包完全相同:
文件夹lib-name
- Package.swift
- README.md
文件夹Sources
文件夹ModuleName
- ModuleName.swift
文件夹Resources
- Localizable.xcstrings
文件夹Skip
- skip.yml
文件夹Tests
文件夹ModuleNameTests
- ModuleNameTests.swift
- XCSkipTests.swift
文件夹Resources
- TestData.json
文件夹Skip
- skip.yml
Skip 框架使用标准的 Package.swift 文件,区别在于添加了对 Skip 库的依赖以及使用 skipstone 插件进行转译:
// swift-tools-version: 5.9import PackageDescription
let package = Package( name: "lib-name", defaultLocalization: "en", platforms: [.iOS(.v16), .macOS(.v13), .tvOS(.v16), .watchOS(.v9), .macCatalyst(.v16)], products: [ .library(name: "ModuleName", targets: ["ModuleName"]), ], dependencies: [ .package(url: "https://github.com/skiptools/skip.git", from: "1.7.0"), .package(url: "https://github.com/skiptools/skip-fuse.git", from: "1.0.0"), .package(url: "https://github.com/skiptools/skip-model.git", from: "1.0.0"), ], targets: [ .target(name: "ModuleName", , dependencies: [ .product(name: "SkipFuse", package: "skip-fuse") .product(name: "SkipModel", package: "skip-model") ], plugins: [ .plugin(name: "skipstone", package: "skip") ]), .testTarget(name: "ModuleNameTests", dependencies: ["ModuleName"], plugins: [.plugin(name: "skipstone", package: "skip")]), ])此配置包含了编写通用模型代码所需的依赖,包括能同时与 SwiftUI 和 Compose 用户界面配合的 @Observable。你可以根据特定需求自由编辑 Package.swift。
将现有应用迁移到 Skip
Section titled “将现有应用迁移到 Skip”将现有应用迁移到 Skip 并非易事。大多数应用包含许多仅限 iOS 的依赖,这使得 Android 移植具有挑战性。
此外,当你使用 skip create 或 skip init 创建新的 Skip 应用时,它会处理使应用能够同时构建 iOS 和 Android 版本的所有复杂细节。这个过程足够复杂,我们不建议尝试迁移现有的 Xcode 项目。相反,选择以下两种方案之一来创建现有应用的 Android 版本:
- 使用
skip create或skip init创建新的 Skip 应用,然后添加现有应用的依赖和代码。 - 保留现有的 Xcode 应用,使用 Android Studio 或你选择的 IDE 创建单独的 Android 应用。分别管理应用,但通过创建双平台框架共享代码。
无论你选择哪种方案,第一步都是相同的:
- 如果尚未模块化,将应用拆分为 Swift Package Manager 包。
- 从“基础”模块开始,逐步向上,尝试让每个模块都能为 Android 构建。
我们建议尽可能使用 Skip Fuse,因为它与现有 Swift 代码和依赖的兼容性更好。
将应用移植到全新平台并不容易,即使有 Skip 的帮助。请记住,我们随时愿意提供帮助。