Dependencies
Skip project dependencies come in three forms:
- A dependency on another dual-platform Skip package.
- A dependency on a Swift Package Manager package used only by your iOS code.
- A dependency on a Kotlin or Java package library used only by your Android code.
In the latter two cases, you will have to gate your code so that your Kotlin does not attempt to import or use any iOS-only API, and your iOS Swift does not attempt to import or use any Kotlin-only API.
#if SKIP
import com.xyz.kotlin.package
#else
import XYZSwiftPackage
#endif
#if SKIP
... use Kotlin-only API ...
#endif
...
#if !SKIP
... use Swift-only API ...
#endif
Adding Skip Package Dependencies
Use Package.swift
to add a dependency on an external Skip SPM package, just as you would for a standard Swift package. Skip will detect the presence of the dependency’s Skip/skip.yml
file and add a corresponding Gradle dependency on the local transpiled output of that project. In other words, adding an SPM dependency on the Swift side will automatically create a Gradle dependency on the Kotlin side. So when your project depends only on other Skip projects, you generally don’t need to perform any additional customization in your own skip.yml
file.
Note: Skip transforms your CamelCase Swift module names into dot-separated lowercase Kotlin package names:
MyPackage
becomesmy.package
MyHTTPLibrary
becomesmy.http.library
- Kotlin package names must have at least two segments, so
Product
becomesproduct.module
Skip also removes the Tests
suffix from module names during translation, so that your tests and source end up in the same Kotlin package. Kotlin does not have an equivalent of Swift’s @testable
attribute, so this is the only way to allow your tests to access internal module API.
Adding Swift Dependencies
Add any iOS-only SPM packages to Package.swift
in the standard manner. When Skip fails to find a Skip/skip.yml
file in a module, it excludes that module from the resulting Gradle project. Thus the dependency will only exist on the iOS side.
Adding Java/Kotlin Dependencies
Sometimes the Android side of a project might need to utilize external Java or Kotlin libraries that aren’t provided by the Skip frameworks. An example of this would be a Skip framework that provides a unified dual-platform API, but whose underlying implementation depends on an external project artifact.
As a concrete example, the SkipScript framework relies on the built-in JavaScriptCore framework on iOS (and thus has no external dependencies on the iOS side), but on Android it depends on external jsc-android
libraries that contain the script engine and other supporting functionality.
In this case, you can use skip.yml
to add dependencies to the Gradle side of the Skip project. We discuss skip.yml
and Gradle here.
For example, here is the skip-script/Sources/SkipScript/Skip/skip.yml
file containing parameters that specify its dependencies on external jsc
libraries. These will be aggregated and included in the resulting build.gradle.kts
file:
# the blocks to add to build.gradle.kts
build:
contents:
- block: 'dependencies'
contents:
- 'implementation("org.webkit:android-jsc-cppruntime:r245459@aar")'
- 'implementation("org.webkit:android-jsc:r245459@aar")'
# the blocks to add to settings.gradle.kts
settings:
contents:
- block: 'dependencyResolutionManagement'
contents:
- block: 'repositories'
contents:
- 'maven("https://github.com/jectivex/jsc-android/raw/main/releases")'
In many cases, dependencies will be available from the built-in mavenCentral
and google
repositories. When a dependency is provided by one of these built-in repositories, only the build
block needs to be added with a list of dependencies to add to the project, and the settings
block can be excluded.
The resulting build.gradle.kts
file contains the aggregated blocks from this module and all the dependent modules, and will look something like this:
// build.gradle.kts generated by Skip for the SkipScript module.
// This file is generated by the Skip transpiler plugin and is
// derived from the aggregate Skip/skip.yml files from the SPM project.
// Edits made directly to this file will be overwritten.
dependencies {
testImplementation("org.json:json:20180813")
testImplementation("org.jetbrains.kotlin:kotlin-test")
testImplementation("org.jetbrains.kotlin:kotlin-test-junit")
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3")
androidTestImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3")
testImplementation("org.robolectric:robolectric:4.10.3")
androidTestImplementation("androidx.test:runner:1.5.2")
testImplementation("org.jetbrains.kotlin:kotlin-test-junit")
androidTestImplementation("org.jetbrains.kotlin:kotlin-test-junit")
testImplementation("androidx.test:core:1.5.0")
androidTestImplementation("androidx.test:core:1.5.0")
testImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
implementation("net.java.dev.jna:jna:5.13.0@aar")
testImplementation("net.java.dev.jna:jna:5.13.0")
implementation("org.webkit:android-jsc-cppruntime:r245459@aar")
implementation("org.webkit:android-jsc:r245459@aar")
implementation(project(":SkipFoundation"))
implementation(project(":SkipLib"))
testImplementation(project(":SkipUnit"))
androidTestImplementation(project(":SkipUnit"))
implementation(project(":SkipFFI"))
}
plugins {
kotlin("android") version "1.9.0"
id("com.android.library") version "8.1.0"
}
kotlin {
jvmToolchain(17)
}
android {
namespace = group as String
compileSdk = 34
defaultConfig {
minSdk = 29
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "17"
}
testOptions {
unitTests {
isIncludeAndroidResources = true
}
}
}
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>() {
kotlinOptions {
suppressWarnings = true
}
}
tasks.withType<Test>().configureEach {
systemProperties.put("robolectric.logging", "stdout")
systemProperties.put("robolectric.graphicsMode", "NATIVE")
testLogging {
this.showStandardStreams = true
}
}
And in this case, since the settings
block was used, the resulting settings.gradle.kts
file will include the customized repositories:
// This is the top-level Gradle settings for the project.
// The module dependencies it contains may be symbolic links to peer folders.
//
// This file is generated by the Skip transpiler plugin and is
// derived from the aggregate Skip/skip.yml files from the SPM project.
// Edits made directly to this file will be overwritten.
//
// Open with External Editor to build and run this project in an IDE.
//
pluginManagement {
repositories {
gradlePluginPortal()
mavenCentral()
google()
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
mavenCentral()
google()
maven("https://github.com/jectivex/jsc-android/raw/main/releases")
}
}
rootProject.name = "skip.script"
include(":SkipScript")
include(":SkipFoundation")
include(":SkipLib")
include(":SkipUnit")
include(":SkipFFI")
The generated build files will be overwritten the next time the Skip transpiler is run, and so the generated output shouldn’t be edited directly. Instead, any changes to the module’s Skip/skip.yml
properties will result in the build.gradle.kts
and settings.gradle.kts
being regenerated and included as part of the build.