Menu

Dependencies

Skip project dependencies come in three forms:

  1. A dependency on another dual-platform Skip package.
  2. A dependency on a Swift Package Manager package used only by your iOS code.
  3. 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

To everything in a Kotlin package, use import com.xyz.__ instead of Kotlin’s import com.xyz.*. The latter is not valid Swift.

Adding Skip Package Dependencies

Use Package.swift to add a dependency on an external Skip SwiftPM 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 SwiftPM 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.

Kotlin Package Names

Skip transforms your CamelCase Swift module names into dot-separated lowercase Kotlin package names:

  • MyPackage becomes my.package
  • MyHTTPLibrary becomes my.http.library
  • Kotlin package names must have at least two segments, so Product becomes product.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 SwiftPM 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 SwiftPM 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 SwiftPM 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.