Skip.tools: Share Swift and Kotlin Code Across Platforms
PROGRAMMING LANGUAGES March 31, 2026, 5:30 a.m.

Skip.tools: Share Swift and Kotlin Code Across Platforms

Imagine writing a single piece of logic once and reusing it on both iOS and Android without the usual copy‑paste nightmare. That’s the promise of skip.tools, a lightweight platform that lets you share Swift and Kotlin code across mobile ecosystems. In this post we’ll walk through the core concepts, set up a real project, and explore practical scenarios where shared code can cut development time in half.

What Is skip.tools?

Skip.tools is a cloud‑hosted code‑sharing service that bridges the gap between Swift and Kotlin through an intermediate representation called Skip. You write your business logic in either language, upload it, and the service generates the counterpart automatically. The result is two synchronized modules—one Swift, one Kotlin—keeping the public API identical.

Under the hood, Skip parses the source, builds an abstract syntax tree (AST), and then emits equivalent code in the target language. It respects platform‑specific types, like String and Int, while translating idiomatic constructs such as guard statements to Kotlin’s require checks. The workflow feels like a “Git‑style” sync: push changes, pull updates, and let the service handle the translation.

Getting Started: Project Setup

1. Create a Skip Account

Head over to skip.tools and sign up with your GitHub or Google account. After verification, you’ll land on the dashboard where you can create a new repository. Think of this repo as the single source of truth for your shared code.

2. Initialize a Local Repository

Open your terminal and run the Skip CLI to bootstrap a project. The CLI will generate two folders—SwiftShared and KotlinShared—each containing a starter module.

# Install the CLI (once)
brew install skip-cli

# Initialize a new shared repo
skip init MyCrossPlatformLib
cd MyCrossPlatformLib

# Verify folder structure
ls -R

At this point you have a clean slate: a .skip config file, a README.md, and empty source directories. The CLI also adds a skip.yml that defines the translation rules you can tweak later.

3. Connect to Your Mobile Apps

In your iOS Xcode project, add SwiftShared as a Swift Package. For Android Studio, import KotlinShared as a Gradle module. Both platforms will now see the same public API, but each will compile the native language version.

Sharing Swift Code with Kotlin

Let’s dive into a concrete example: a simple CurrencyFormatter that formats monetary values according to locale. Write it once in Swift, push to Skip, and retrieve the Kotlin counterpart.

Swift Implementation

import Foundation

public struct CurrencyFormatter {
    private let locale: Locale

    public init(localeIdentifier: String) {
        self.locale = Locale(identifier: localeIdentifier)
    }

    public func format(amount: Double) -> String {
        let formatter = NumberFormatter()
        formatter.locale = locale
        formatter.numberStyle = .currency
        guard let result = formatter.string(from: NSNumber(value: amount)) else {
            return "\(amount)"
        }
        return result
    }
}

Notice the use of public to expose the struct’s API. Skip respects visibility modifiers, so the generated Kotlin code will also be public.

Push and Generate Kotlin

Run the following CLI command to sync the Swift file to the cloud and request a Kotlin translation:

skip push SwiftShared/CurrencyFormatter.swift
skip pull KotlinShared/CurrencyFormatter.kt

Skip returns a Kotlin file that mirrors the Swift logic, handling nullability and locale nuances automatically.

Kotlin Implementation (Generated)

package com.example.kotlinshared

import java.util.Locale
import java.text.NumberFormat

data class CurrencyFormatter(private val localeIdentifier: String) {
    private val locale: Locale = Locale.forLanguageTag(localeIdentifier)

    fun format(amount: Double): String {
        val formatter = NumberFormat.getCurrencyInstance(locale)
        return formatter.format(amount) ?: amount.toString()
    }
}

Even though the code was auto‑generated, you can still open it in Android Studio, add platform‑specific extensions, or adjust the skip.yml to fine‑tune the translation rules.

Sharing Kotlin Code with Swift

Now let’s flip the direction. Suppose you have a Kotlin data class that encapsulates user preferences, and you need the same model on iOS. Write it in Kotlin, push, and let Skip produce Swift code.

Kotlin Source

package com.example.kotlinshared

import kotlinx.serialization.Serializable

@Serializable
data class UserSettings(
    val darkMode: Boolean,
    val notificationsEnabled: Boolean,
    val preferredLanguage: String
)

The @Serializable annotation tells Kotlin to generate JSON serializers. Skip will preserve this intent by adding a comparable Swift Codable conformance.

Push and Pull Swift

skip push KotlinShared/UserSettings.kt
skip pull SwiftShared/UserSettings.swift

Swift Output (Generated)

import Foundation

public struct UserSettings: Codable {
    public let darkMode: Bool
    public let notificationsEnabled: Bool
    public let preferredLanguage: String

    public init(darkMode: Bool, notificationsEnabled: Bool, preferredLanguage: String) {
        self.darkMode = darkMode
        self.notificationsEnabled = notificationsEnabled
        self.preferredLanguage = preferredLanguage
    }
}

Because both languages share the same JSON schema, you can now send UserSettings objects over the network without writing a separate serializer for each platform.

Real‑World Use Cases

1. Business Logic Layer – Complex calculations (tax, discounts, loyalty points) are perfect candidates for sharing. Write the algorithm once, test it in Swift, and let Skip keep the Kotlin version in lockstep.

2. Data Models & Serialization – As demonstrated, data classes annotated for serialization can be mirrored automatically, ensuring API contracts stay consistent across iOS and Android.

3. Feature Flags & Remote Config – Centralize flag evaluation logic. When a flag changes, only the shared module updates, and both apps instantly respect the new rules.

Testing Shared Code

Testing is a critical piece of any cross‑platform strategy. Skip encourages you to write tests in the language of origin and then run them on both platforms.

Swift Unit Test

import XCTest
@testable import SwiftShared

final class CurrencyFormatterTests: XCTestCase {
    func testUSLocale() {
        let formatter = CurrencyFormatter(localeIdentifier: "en_US")
        let result = formatter.format(amount: 1234.56)
        XCTAssertEqual(result, "$1,234.56")
    }
}

After the test passes, push the Swift file. Skip will generate Kotlin code, and you can add a corresponding Kotlin test to verify parity.

Kotlin Unit Test

package com.example.kotlinshared

import kotlin.test.Test
import kotlin.test.assertEquals

class CurrencyFormatterTest {
    @Test
    fun testUSLocale() {
        val formatter = CurrencyFormatter("en-US")
        val result = formatter.format(1234.56)
        assertEquals("$1,234.56", result)
    }
}

Running both test suites gives you confidence that the translation layer didn’t introduce subtle bugs.

Pro Tip: Keep your shared modules pure—avoid referencing platform‑specific APIs like UIKit or Android Views. If you need UI, wrap the shared logic in thin adapters on each side.

Advanced Configuration

Skip’s skip.yml file offers granular control over how types map between languages. For instance, you can tell Skip to translate Swift’s Data to Kotlin’s ByteArray or to treat certain enums as sealed classes.

typeMappings:
  Data: ByteArray
  URL: java.net.URL

enumMapping:
  swift: sealed
  kotlin: sealed

These tweaks are especially useful when dealing with cryptographic primitives or custom networking layers where the default mapping isn’t optimal.

Custom Templates

If your project follows a specific coding style—say you prefer Kotlin’s flow over LiveData—you can supply custom Jinja2 templates to the Skip generator. This way, the output adheres to your conventions without manual post‑processing.

# custom_template.kt.j2
package {{ package_name }}

{% for import in imports %}
import {{ import }}
{% endfor %}

// Generated by Skip – DO NOT EDIT
{{ generated_code }}

Place the template in the .skip/templates directory and reference it in skip.yml. The next skip pull will use your template.

Performance Considerations

Because Skip generates native code, there’s no runtime translation overhead. The compiled Swift and Kotlin binaries run as fast as hand‑written equivalents. However, keep an eye on large generated files; they can increase build times.

To mitigate this, enable incremental builds in Xcode and Gradle, and consider splitting a monolithic shared module into feature‑specific sub‑modules. Skip supports multiple repositories, so you can have a PaymentsShared repo separate from AuthShared.

Pro Tip: Use Skip’s --watch flag during development. It watches local changes and automatically pushes updates, giving you near‑instant feedback across both platforms.

Integrating with CI/CD Pipelines

Automation is key for a smooth workflow. Add Skip commands to your CI scripts so that every pull request validates both Swift and Kotlin code.

# .github/workflows/skip.yml
name: Skip Sync

on:
  push:
    paths:
      - 'SwiftShared/**'
      - 'KotlinShared/**'

jobs:
  sync:
    runs-on: macos-latest
    steps:
      - uses: actions/checkout@v3
      - name: Install Skip CLI
        run: brew install skip-cli
      - name: Push Swift, Pull Kotlin
        run: |
          skip push SwiftShared/
          skip pull KotlinShared/
      - name: Commit Kotlin changes
        run: |
          git config user.name "github-actions"
          git config user.email "actions@github.com"
          git add KotlinShared/
          git commit -m "Sync Kotlin from Swift"
          git push

This pipeline ensures that any change in the Swift side automatically updates the Kotlin side, and vice versa, keeping the two codebases forever in sync.

Common Pitfalls & How to Avoid Them

  • Platform‑specific APIs in shared code – If you accidentally reference UIKit or android.view, Skip will either fail to translate or produce stub code. Keep shared modules pure.
  • Inconsistent naming conventions – Swift prefers camelCase for methods, while Kotlin uses the same style but may favor property syntax. Configure skip.yml to enforce a naming strategy.
  • Version drift – When Skip updates its translation engine, regenerated code may change subtly. Pin the Skip CLI version in your CI to avoid unexpected breakages.

Future Roadmap

Skip is evolving rapidly. Upcoming features include direct WebAssembly output for sharing logic with React Native, and a visual diff tool that highlights translation differences line‑by‑line. Keeping an eye on the roadmap helps you plan migrations and adopt new capabilities early.

Conclusion

Sharing Swift and Kotlin code has long been a dream for mobile teams seeking true cross‑platform efficiency. With Skip.tools, that dream becomes a practical reality: write once, sync automatically, and let each platform compile native binaries. By following the setup steps, leveraging the CLI, and respecting the pure‑logic rule, you can dramatically reduce duplication, improve consistency, and accelerate feature delivery.

Start small—pick a utility class or a data model, push it through Skip, and watch the counterpart appear. As confidence grows, expand the shared layer to encompass business rules, validation, and even complex algorithms. The result is a cleaner codebase, happier developers, and faster time‑to‑market for your apps.

Share this article