Getting Started
- System Requirements
- Installation
- Activation
- Creating an App
- Creating a Dual-Platform Framework
- Migrating an Existing App to Skip
- Updating Skip
- Additional Resources
System Requirements
Skip requires a macOS 13 development machine with Xcode 16.1 or later, Android Studio 2023 or later, and Homebrew installed.
Installation
Install Skip by running the Terminal command:
brew install skiptools/skip/skip
This will download and install the skip
tool itself, as well as the gradle
and JDK dependencies that are necessary for building and testing the Kotlin/Android side of your apps.
Ensure that the basic development prerequisites are satisfied by running:
skip checkup
If the checkup fails, try running again with skip checkup --verbose
to get more details, and check the FAQ for common solutions.
Once the checkup passes, install the native Swift Android toolchain:
skip android sdk install
If this command is successful, running swift sdk list
will list the local SDKs, which should include an entry like: swift-6.0.2-RELEASE-android-24-0.1
.
To verify that the toolchain is setup properly and able to build a test app with native Swift integration, run:
skip checkup --native
The output should end with:
[âś“] Archive iOS ipa (28.01s)
[âś“] Assemble HelloSkip-release.ipa 405 KB
[âś“] Verify HelloSkip-release.ipa 405 KB
[âś“] Assembling Android apk (133.23s)
[âś“] Verify HelloSkip-release.apk 212 MB
[âś“] Check Swift Package (0.97s)
[âś“] Check Skip Updates: 1.1.24
[âś“] Skip 1.1.24 checkup succeeded in 314.86s
Now you’re ready to start developing with Skip!
Activation
After a trial period, Skip will require a license key for closed-source or commercial development. See License Keys for instructions on how to obtain and install your license key when your free trial ends.
Creating an App
There are two primary ways to structure a Skip app. The most common structure is as a single, dual-platform app project. Creating a dual-platform app project does not prevent you from customizing your app for Android, even to the point of e.g. writing your entire Android UI in Kotlin and Compose. But it generally assumes that you’ll manage the iOS and Android versions of your app through Xcode as a single logical application.
The other common structure is to create separate iOS and Android apps that use a set of dual-platform frameworks for shared functionality. Using this option, it is up to you to create and manage the separate iOS and Android applications using Xcode and Android Studio (or your Android IDE of choice), respectively.
Creating a Dual-Platform App
Create a new dual-platform app project with the command:
skip init --appid=bundle.id project-name AppName
Your appid
must contain at least two words, and each word must be separated by a .
. It is conventional to use reverse-DNS naming, such as com.companyname.AppName
. Also make sure that your project-name
and AppName
are different. It is conventional to use a lowercase, hyphenated name for your project (which Skip uses to create your app’s main SwiftPM package name), and UpperCamelCase for your app name.
Skip has two modes: native mode, in which your Swift is compiled, and transpiled mode, in which your Swift is translated to Kotlin. We discuss the tradeoffs and use cases for each mode in Native or Transpiled. If you want to use Skip’s native mode for your app’s model layer, use the following command instead:
skip init --native --appid=bundle.id project-name AppName AppNameModel
In the future, the --native
option will use native SwiftUI as well a native model, but at this time native SwiftUI support is incomplete.
Pass the --open-xcode
argument to immediately open the project in Xcode. For example:
skip init --open-xcode --appid=bundle.id.HelloSkip hello-skip HelloSkip
This will create a hello-skip/
folder with a new SwiftPM package containing a single module named HelloSkip
, along with folders named Darwin
and Android
and the shared Skip.env
app configuration file. The Darwin
folder will contain a HelloSkip.xcodeproj
project with a HelloSkip
target, which can be opened in Xcode.
See the command line reference for a complete listing of skip init
options.
If you include the --native
flag and add a model module name to the end of the command, you instead get a multi-module project with a compiled model module and a transpiled UI module:
skip init --native --appid=bundle.id.HelloSkip hello-skip HelloSkip HelloSkipModel
hello-skip
├── Package.swift
└── Sources
├── HelloSkip
│  ├── ContentView.swift
│  └── Skip
│  └── skip.yml
└── HelloSkiptModel
├── Skip
│  └── skip.yml
└── ViewModel.swift
skip init
creates a functional template app, but before you can build and launch it, an Android emulator needs to be running. Launch Android Studio.app
and open the Virtual Device Manager
from the ellipsis menu of the Welcome dialog. From there, Create Device
(e.g., “Pixel 6”) and then Launch
the emulator.
Once the Android emulator is running, select and run the HelloSkip
target in Xcode. The first build will take some time to compile the Skip libraries, and you may be prompted with a dialog to affirm that you trust the Skip plugin. Once the build and run action completes, the SwiftUI app will open in the selected iOS simulator, and at the same time the Android app will launch in the currently-running Android emulator.
Browse to the ContentView.swift
file and make a small change and re-run the target: the app will be re-built and re-run on both platforms simultaneously with your changes.
You’re now ready to continue working on your first Skip app! For more information - including common issues and workarounds - see the Development chapter. Consider browsing our other documentation as well. Happy Skipping!
Creating a Multi-Module App
Skip is designed to accommodate and encourage using multi-module projects. You can create a modularized project by specifying additional module names to skip init
at the end of the chain. For example:
skip init --appid=bundle.id.HelloSkip multi-project HelloSkip HelloModel HelloCore
This command will create a SwiftPM project with three modules: HelloSkip
, HelloModel
, and HelloCore
. The heuristics of such module creation is that the modules will all be dependent on their subsequent peer module, with the first module (HelloSkip
) having an initial dependency on SkipUI
, the second module depending on SkipModel
, and the final module in the chain depending on SkipFoundation
. The Package.swift
file can be manually edited to shuffle around dependencies, or to add new dependencies on external Skip frameworks such as the SkipSQL or SkipFirebase libraries.
Creating Separate iOS and Android Apps
You might choose to share functionality using dual-platform frameworks, but create separate iOS and Android apps. Some development teams, for example, would like to share common model and business logic layers, but write the UI separately for each platform.
The Travel Posters sample app provides an example of this pattern. It has the following top-level entries:
travel-posters-model
: This SwiftPM package builds a dual-platform framework containing a common model layer for the iOS and Android apps. Skip ensures that the@Observable
types you write in Swift can power not only a SwiftUI interface, but a Compose interface as well. See the Development documentation for details.iOS
: Directory containing the TravelPosters iOS app and Xcode project, which hastravel-posters-model
as a package dependency.Android
: Directory containing the Android version of the app. TheAndroid/lib
directory contains exported archives oftravel-posters-model
and the various Skip frameworks that it depends on.TravelPosters.xcworkspace
: A workspace that includes both the iOS app and thetravel-posters-model
package.
Use TravelPosters.xcworkspace
to iterate on the iOS app and/or shared model layer. To donate the latest travel-posters-model
code to the Android app:
skip export --project travel-posters-model -d Android/lib/debug/ --debug
skip export --project travel-posters-model -d Android/lib/release/ --release
There are many ways to automate this process, from simple scripting to git submodules to publishing Skip’s transpiled Android travel-posters-model
output to a local Maven repository. Use whatever system fits your team’s workflow best.
See the Deployment chapter for more information on skip export
.
Additional notes:
- You must “Sync Project with Gradle Files” in Android Studio after updating the exported libraries.
- Using an exported library function which has transitive dependencies on additional Android libraries can cause a runtime error. You must ensure that all transitive dependencies are in your own app’s
build.gradle.kts
.
The TravelPosters sample linked above uses transpiled Swift. Read a case study on sharing a native Swift model between iOS and Android apps in this blog post.
Creating a Dual-Platform Framework
Skip framework projects are pure SwiftPM packages that encapsulate common functionality. Frameworks are simpler than app projects, as they do not need Darwin/
and Android/
folders.
Each of the core SkipStack frameworks (SkipLib, SkipUnit, SkipFoundation, and SkipUI) are Skip framework projects. Other commonly-used projects include SkipSQL and SkipFirebase. These existing libraries are rich sources of examples of various strategies for providing dual-platform functionality.
A new framework project can be created and opened with:
skip init lib-name ModuleName
This will create a new lib-name
folder containing a Package.swift
with targets of ModuleName
and ModuleNameTests
.
The generated package can be opened in Xcode.app, which you can use to build and run the unit tests. Or use swift build
and swift test
from the Terminal for headless testing as part of a continuous integration process.
Due to limitations on Xcode plugins, building your framework target only builds the iOS version. To build the Android version, you must run your unit tests. The Android build occurs as part of the testing process.
Just as before, include the --native
option to create a native module. The module will be preconfigured to compile natively for Android, but there are a couple of additional steps you should take when sharing a model between iOS and Android apps:
-
In order for
@Observables
to work on Android, we need a dependency onskip-model
. If you’ll be using@Observables
in you module, edit the generatedPackage.swift
to add the required dependency, as in the following example:... let package = Package( name: "travel-posters-model", ... dependencies: [ .package(url: "https://source.skip.tools/skip.git", from: "1.2.0"), .package(url: "https://source.skip.tools/skip-model.git", from: "1.0.0"), // <-- Insert .package(url: "https://source.skip.tools/skip-fuse.git", from: "1.0.0") ], targets: [ .target(name: "TravelPostersModel", dependencies: [ .product(name: "SkipFuse", package: "skip-fuse"), .product(name: "SkipModel", package: "skip-model") // <-- Insert ], plugins: [.plugin(name: "skipstone", package: "skip")]), ... ] )
-
The
--native
option we passed toskip init
will configure Skip to automatically bridge our model’s public API from compiled Swift to Android’s ART Java runtime. This is done through theskip.yml
configuration file included in every Skip module. By default, however, Skip assumes that you’ll be bridging to transpiled Swift and SwiftUI code. If you’ll be consuming the model from pure Kotlin, you’ll want to optimize the bridging for Kotlin compatibility. In this case, you can runskip init --native --kotlincompat
to create a module with Kotlin compatiblity, which is the equivalent to editingskip.yml
to look like this:skip: mode: 'native' bridging: auto: true options: 'kotlincompat'
We discuss bridging and skip.yml
later.
Skip Framework Structure
The structure of a Skip framework is exactly the same as any other SwiftPM package:
lib-name
├── Package.resolved
├── Package.swift
├── README.md
├── Sources
│  └── ModuleName
│  ├── ModuleName.swift
│  ├── Resources
│  │  └── Localizable.xcstrings
│  └── Skip
│  └── skip.yml
└── Tests
└── ModuleNameTests
├── ModuleNameTests.swift
├── Resources
│  └── TestData.json
├── Skip
│  └── skip.yml
└── XCSkipTests.swift
Skip frameworks use a standard Package.swift
file, with the exception of an added dependency on skip
and use of the skipstone
plugin for transpilation:
// swift-tools-version: 5.8
import 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://source.skip.tools/skip.git", from: "0.7.31"),
.package(url: "https://source.skip.tools/skip-foundation.git", from: "0.0.0"),
],
targets: [
.target(name: "ModuleName", plugins: [.plugin(name: "skipstone", package: "skip")]),
.testTarget(name: "ModuleNameTests", dependencies: ["ModuleName"], plugins: [.plugin(name: "skipstone", package: "skip")]),
]
)
Migrating an Existing App to Skip
Migrating an existing app to Skip is not trivial. Most apps contain many iOS-only dependencies that make an Android port challenging.
Additionally, when you use skip init
to create a new Skip app, it handles all the messy details involved in making an app that can build for both iOS and Android. The process is complex enough that we do not recommend trying to migrate an existing Xcode project. Instead, choose one of two options to create an Android version of your existing app:
- Use
skip init
to create a new Skip app, then add your existing app’s dependencies and code. - Keep your existing Xcode app, and create a separate Android app using Android Studio or your IDE of choice. Manage the apps separately, but share code by creating dual-platform frameworks.
Regardless of which option you choose, your first steps are the same:
- Modularize your app into Swift Package Manager packages, if it isn’t already.
- Starting with your “base” module and working your way up the stack, attempt to get each module building for Android.
We recommend using Skip’s native mode where possible, as it offers greater compatibility with existing Swift code and dependencies.
Porting an app to an entirely new platform isn’t easy, even with Skip. Remember that we’re here to help.
Updating Skip
To update the skip
command line tool:
% skip upgrade
To update your Xcode project to use the latest version of the Skip transpiler and libraries allowed by your Package.swift
configuration, use the File -> Packages -> Update to Latest Package Versions
Xcode menu option.
Additional Resources
- Use
skip help
for a complete list ofskip
tool commands. - Check the general help page for troubleshooting and contact information.
- Continue browsing this documentation to learn more about developing with Skip.