Fully Native Cross-Platform Swift Apps
In our series on using native Swift on Android, we have covered the basics of the Swift-Android toolchain in Part 1, bridging between compiled Swift and Kotlin in Part 2, and the creation of a cross-platform app with a shared Swift model layer in Part 3.
We are pleased to unveil the culmination of all of this work: Skip 1.5 now has the ability to create a 100% native Swift and SwiftUI app for both iOS and Android! You can now enjoy the safety, efficiency, and expressiveness of pure Swift throughout your entire cross-platform app, including the ability to tap into the vast ecosystem of Swift packages to support your app development.
This blog post will go over the process of creating and developing a Swift cross-platform app, explore the underlying technologies, and discuss our current status and next steps.
Interested in following Swift-on-Android’s march towards official status? Follow the Android category at swift.org for Working Group meeting notes and general discussion.
Getting Started
Ensure that you are on a macOS 14+ machine with Xcode 16, Android Studio 2025, and Homebrew installed.
First, create and launch an Android emulator for testing.
Next, open Terminal and type the following commands to install Skip and the native Swift-Android toolchain.
$ brew install skiptools/skip/skip
$ skip upgrade
$ skip android sdk install
Verify that everything is working with an additional Terminal command:
$ skip checkup --native
If you haven’t used Skip before, this command may take a long time to complete! Skip has to download and install all of the libraries necessary for Android app development. If any steps in the checkup command fail, consult the generated log file, which should contain an error message describing the failure. You can seek assistance on our Slack or discussion forums.
You’re now ready to create your first fully native cross-platform Swift app:
$ skip init --native-app --open-xcode --appid=bundle.id.HowdySkip howdy-skip HowdySkip
Assuming the app initialized successfully, your project will open in Xcode. Run the app against an iPhone simulator destination (the first build may take some time) and your app will launch on both the iOS simulator and the running Android emulator at the same time!
The App Template
The skip init
command creates a template app to get you started. It is a TabView
containing a “Welcome” view, a list of editable and re-orderable “Items”, and a “Settings” view:
You can also browse the template app online in the Howdy Skip repository.
ContentView.swift
Rather than importing SwiftUI
, you will notice that the Sources/HowdySkip/ContentView.swift
file instead imports SkipFuseUI
. When building for iOS, this simply redirects to a SwiftUI
import. Skip’s philosophy is to not intrude on the iOS side of the application: on Darwin platforms you are still using direct, non-intermediated SwiftUI, just as if you were building an app without Skip at all.
On Android, however, the SkipFuseUI
module bridges the SwiftUI API onto Jetpack Compose, Android’s modern Kotlin UI toolkit. It does this through the intermediate SkipUI
module. Bridging to pure Jetpack Compose gives Android users a fully native user experience rather than the uncanny-valley replica generated by many cross-platform frameworks.
Consult the SkipUI
module’s documentation for a listing of currently-supported SwiftUI constructs on Android. You can also examine the ShowcaseFuse cross-platform sample app, which displays and exercises most supported SwiftUI components:

Use #if os(Android)
conditions to customize your Swift and SwiftUI for Android, including excluding unsupported SwiftUI from your Android build. You don’t have to let an Android limitation affect your iOS experience! Later we’ll see how to augment your Android UI with pure Jetpack Compose.
ViewModel.swift
The “Items” tab displays an editable list of items which are managed by Sources/HowdySkip/ViewModel.swift
, which handles loading and saving the list of items to a simple JSON file. This code uses standard Foundation types (URL
, Data
, Date
, FileManager
, JSONEncoder
, JSONDecoder
, etc.) to handle the management and persistence of the items. Note that unlike the SwiftUI in ContentView
, none of this code is bridged into Kotlin: it is using the Apple swift-foundation types directly, just as on iOS.
Despite only using native Foundation types, you will notice that ViewModel.swift
imports SkipFuse
. Just as SkipFuseUI
bridges your UI to Android, SkipFuse
bridges model-layer code. We use it here to enable our @Observable
view model to communicate changes to the Jetpack Compose user interface. This is discussed further in Part 2 of the series. You generally don’t need to be concerned with the details other than to remember to import SkipFuse
(or SkipFuseUI
) any time you implement an @Observable
.
The Skip Xcode plugin will issue a warning if you define an @Observable
without importing SkipFuse
or SkipFuseUI
. The SkipFuse
module bridges other non-UI functionality as well, such as routing your OSLog.Logger
messages to Android’s standard Logcat logging system.
Bridging into Kotlin and Compose
The final tab of the sample app is the “Settings” screen. This exposes various settings and displays some information about the app. It also presents a little heart emoji, which is blue on iOS and green on Android.
We use the green heart emoji to demonstrate a powerful feature of Skip: the ability to embed code that directly calls Kotlin and Jetpack Compose APIs! Examining the SettingsView
in ContentView.swift
, you will see the inclusion of a PlatformHeartView
, whose implementation looks like this:
/// A view that shows a blue heart on iOS and a green heart on Android.
struct PlatformHeartView : View {
var body: some View {
#if os(Android)
ComposeView {
HeartComposer()
}
#else
Text(verbatim: "đź’™")
#endif
}
}
#if SKIP
/// Use a ContentComposer to integrate Compose content. This code will be transpiled to Kotlin.
struct HeartComposer : ContentComposer {
@Composable func Compose(context: ComposeContext) {
androidx.compose.material3.Text("đź’š", modifier: context.modifier)
}
}
#endif
What is going on here? Notice that on Android, we’re rendering the heart with a ComposeView
, a special SwiftUI view for including Jetpack Compose content in the form of a ContentComposer
.
/// Encapsulation of Composable content.
public protocol ContentComposer {
@Composable func Compose(context: ComposeContext)
}
But how can we define @Composable
functions and call Android APIs from within our native Swift code?
The magic lies in Skip’s ability to transpile Swift code into Kotlin, and SkipFuse
’s ability to bridge between Kotlin and your compiled Swift. Any code in a #if SKIP
block will be transformed into the equivalent Kotlin, so it is free to call other Kotlin and Java APIs directly. The generated #if SKIP
Kotlin will also be automatically bridged so that you can call it from your native Swift.
ComposeView
enables you to easily mix custom Compose views into your app - whether by preference or to work around any Skip limitations. For a less contrived use case, see how the MapView
in our TravelBookings sample app displays a GoogleMap
Composable.

Ecosystem Access
The ability to effectively embed Kotlin code is immensely powerful. It not only provides direct access Jetpack Compose and the Android SDK, but also enables you to tap into the complete Android ecosystem of libraries through Kotlin and Java dependencies.
Of course, using native Swift allows you to take advantage of the vast and growing ecosystem of available Swift packages as well. In our post on Bringing Swift Packages to Android we introduced the swift-everywhere.org site that tracks packages that are successfully building for Android. Since that post, community members have been implementing Android support, bringing the number of known packages that can be used on Android to 2,240! Popular projects like Alamofire, flatbuffers, SwiftSoup, swift-protobuf, and swift-sqlcipher (just to name a few) can be added directly to your app and used in the same way on both iOS and Android.
Skip’s unique ability to directly call both Swift and Kotlin/Java APIs separates it from most cross-platform development frameworks, where integrating with the host system often requires bespoke adapters and extra layers of indirection.
Skip Notes
As a demonstration and validation of this technology, we have published one of our sample apps, Skip Notes, to both the Google Play Store and Apple App Store. This fully native Swift app integrates with the swift-sqlcipher database library to provide persistence for a simple list of notes.
Status and Next Steps
Despite being generally available, Skip’s native support is currently a technology preview. We are working on updating our documentation, finding and squashing remaining bugs and limitations, reducing build times, and generating smaller Android binaries. Even as a preview, however, you can build complete, production-ready cross-platform Swift apps, as Skip Notes demonstrates.
As always, you can seek assistance on our Slack or discussion forums.
Cross-platform Swift and SwiftUI have been a dream of ours for a long time. We are immensely excited about the possibilities this unlocks for creating truly best-in-class apps for both iOS and Android from a single codebase!