In general, Skip app development is modern iOS app development. You develop in Xcode. You code in Swift and SwiftUI. As much as possible, Skip’s goal is to disappear.

The following sections do not attempt to teach you iOS development. There are other available resources for that. Rather, we focus on where Skip development differs from standard iOS development, including how to use Skip’s tools, how to work on the Android side of your app, and what to do when things go wrong.

Building and Running

Assuming you followed the app creation instructions, each successful build of your Skip app will automatically attempt to launch it on the running Android emulator or device. If you are having trouble with Skip’s Xcode plugin, check the Troubleshooting section for help.

Running on the Android Emulator

You can setup a new emulator by running Android, opening the Device Manager from the ellipsis menu of the Welcome dialog, and then setting up an emulator. Exactly one emulator must be running in order for the Skip project’s Launch APK script phase to install and run the app successfully.

Once an emulator has been setup, you can choose to launch it from the command line rather than running Android Studio. To run the emulator without Android Studio, use the terminal command:

~/Library/Android/sdk/emulator/emulator @Pixel_6_API_33

Running on an Android Device

To run on an Android device rather than the emulator, pair the Android device with your development machine and set the ANDROID_SERIAL variable in your project’s .xcconfig file to the device’s identifier. Running the /opt/homebrew/bin/adb devices command will show the available paired identifiers. Setting ANDROID_SERIAL is also the only way to run the app when there are multiple emulators running.


Coding with Skip resembles coding any iOS app. In fact we believe that the less you have to think about Skip, the more successful we’ve been. Writing dual-platform code with Skip, however, does introduce complications not found in pure iOS development:

  1. While Swift and Kotlin are similar in many ways, there are some language differences that Skip cannot resolve. Additionally, the Skip transpiler’s type inference engine is not as sophisticated as that of the full Swift compiler. As a result, you will sometimes have to adjust your Swift code so that it can be transpiled into valid Kotlin. Typically, Skip’s Xcode plugin will tell you when and why this is necessary when you build. For more information on what Swift language features Skip supports, see the Swift Support Reference.
  2. At some point, you will likely find yourself wanting to use an iOS API, framework, or feature that is not yet supported. This section discusses your options when you encounter a Skip limitation.
  3. Writing a dual-platform apps means using dual-platform libraries. As part of its unique transparent adoption, Skip automatically maps your Swift imports of standard iOS frameworks to the correct Skip library for Android, so that your Swift code doesn’t depend on Skip. For example, import Foundation in Swift becomes import* in Kotlin. Our documentation on dependencies discusses how to use other dual-platform libraries as well as iOS and Android-specific libraries.

Structs, Garbage Collection, Numeric Types, and More

Swift and Kotlin differ in more than just syntax. The Special Topics section of the Swift Language Reference covers how Skip addresses deeper differences like value types vs. reference types and reference counting vs. garbage collection. We strongly encourage you to read these topics, as they will help you understand how your code will behave on Android and keep you from making many common mistakes.


Messages that you print do not appear in Android logging. Instead, SkipFoundation includes support for importing OSLog and using the standard OSLog.Logger API for dual-platform logging:

import OSLog
let logger = Logger(subsystem: "my.subsystem", category: "MyCategory")
..."My message")

When you log a message in your app, the OSLog messages from the Swift side of the app will appear in Xcode’s console as usual. The Kotlin implementation of OSLog.Logger, on the other hand, forwards log messages to logcat, which is Android’s native logging mechanism. Using the logcat tab in Android Studio is a good way to browse and filter the app’s log messages on the Android side. You can also view logcat output in the Terminal using the command adb logcat, which has a variety of filtering flags that can applied to the default (verbose) output.

Unsupported iOS Features

Skip is new. Its functionality will fill in rapidly, but there are many iOS APIs, frameworks, and features that are not yet implemented for Android.

  1. APIs. Using an iOS API that is not yet supported will result in either an unavailable API error from the Skip Xcode plugin, or an unknown reference error from the Kotlin compiler. These errors will come at build time.

    Check each of Skip’s modules to learn more about its current status and supported API set. For example, the SkipUI module includes a table listing which SwiftUI APIs have been ported to Android.

    When you encounter missing API, you have options. You may be able to use alternate, supported APIs to accomplish the task. You can use Skip’s iOS and Android customization techniques to implement separate iOS and Android code paths, taking advantage of each platforms’ respective native solutions. And if the API you want to use is in a framework already mirrored for Android - either as a Skip open source library or a community library - you may be able to easily add the missing API to the existing library. If you augment an existing library, please consider contributing your improvements back to the Skip community. Follow the instructions here to configure Xcode for local Skip library development.

  2. Frameworks. If you want to use an iOS framework that does not have a Skip or community library available, you might consider creating your own dual-platform library. Again, please consider contributing your work as a community library.
  3. Features. Some iOS app extensions and features are not yet implemented for Android, or have no direct Android counterpart. Use the techniques in Platform Customization to implement iOS-only or Android-only solutions. For example, you might use a Skip compiler directive to exclude your iOS widget from transpilation, and use a pure Kotlin file to implement a native widget for Android.


  • Stay on the happy path. Many Foundation and SwiftUI APIs are mirrored for Android, but many more are still in progress. Use known working components and APIs as documented in the individual Skip modules and previewed in the sample Showcase and Weather apps. Experimentation and iteration is the best way to explore the ever-expanding boundaries of the Skip’s Android implementations.
  • Re-run frequently. For app projects, re-run the app on both platforms constantly. Skip’s transpiler is designed to accommodate incremental development and enable the re-launching of an app on both iOS simulator and Android emulator in mere seconds. Only by re-running frequently will you be able to quickly identify and resolve platform-specific issues and API limitations before they accumulate.
  • Modularize your projects for faster build and test iteration cycles. See the Getting Started documentation for instructions on creating multi-module apps.
  • Re-test frequently. Dividing up your app into separately-testable component modules makes it easier to iterate on that part of an app. See Testing.
  • Use the Skip.env file. Skip app customization should be primarily done by directly editing the included Skip.env file, rather than changing the app’s settings in Xcode. Only properties that are set in the Skip.env file, such as PRODUCT_NAME, PRODUCT_BUNDLE_IDENTIFIER and MARKETING_VERSION, will carry-through to both HelloSkip.xcconfig and AndroidManifest.xml. This is how a subset of the app’s metadata can be kept in sync between the iOS and Android versions, but it requires that you forgo using the Xcode Build Settings interface (which does not modify the .xcconfig file, but instead overrides the settings in the local .xcodeproj/ folder).