AgentStack
SKILL verified Apache-2.0 Self-run

Skip Lite Transpilation

skill-skiptools-skills-skip-lite-transpilation · by skiptools

Swift-to-Kotlin transpilation rules and pitfalls for Skip Lite mode. Covers the high-impact bugs (silent integer overflow, `Double == Int` compile errors, `Foundation.sqrt`, `String.append(Character)`, `Array.remove(atOffsets:)` SwiftUI-only gotcha, variadic init issues, `String(describing:)` and Kotlin map iteration), the `MutableStruct`/`.sref()` value-semantics machinery and the `@nocopy` esca…

No reviews yet
0 installs
13 views
0.0% view→install

Install

$ agentstack add skill-skiptools-skills-skip-lite-transpilation

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Skip Lite Transpilation? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Skip Lite Transpilation Rules

CRITICAL: swift build does NOT validate Kotlin transpilation. You MUST run swift test to catch transpilation errors.

This skill is for Skip Lite (transpiled). If the module's Skip/skip.yml contains mode: 'native', you're in Skip Fuse, not Lite — most of the rules here (integer overflow, Double == Int, Foundation.sqrt, etc.) do not apply. See [skip-fuse](../skip-fuse/SKILL.md) for the Fuse workflow. The rules below still apply to any #if SKIP block inside a Fuse module (those blocks are transpiled to Kotlin even in Fuse mode).

Impact: CRITICAL — Silent Data Corruption

Integer Overflow

Swift Int is 64-bit. Kotlin Int is 32-bit (max ~2.1 billion). Intermediate arithmetic silently overflows in Kotlin.

// BAD: silently wrong on Android
let product = a * b  // if a * b > 2,147,483,647

// GOOD: use Int64 for large values
let product = Int64(a) * Int64(b)

Impact: HIGH — Kotlin Compile Errors

Double-to-Int Comparison

// BAD — Kotlin compile error
if someDouble == 0 { }

// GOOD
if someDouble == 0.0 { }

Foundation-Qualified Math

// BAD — Kotlin compile error
let x = Foundation.sqrt(2.0)

// GOOD
let x = sqrt(2.0)

String.append(Character)

// BAD — Kotlin compile error
result.append(someCharacter)

// GOOD
result += String(someCharacter)

Array.remove(atOffsets: IndexSet) in non-SwiftUI modules

The Array.remove(atOffsets:) method that pairs with SwiftUI's .onDelete { offsets in ... } is an extension on RangeReplaceableCollection provided by SwiftUI, not by Foundation. A pure-data module that doesn't import SwiftUI won't have it — the compiler error is No exact matches in call to instance method 'remove'.

// BAD — in a model module that doesn't import SwiftUI
import Foundation
public func remove(at offsets: IndexSet) {
    items.remove(atOffsets: offsets)   // 'remove' not found
}

// GOOD — expose an Int-array API and let the view convert
public func remove(at indices: [Int]) {
    for index in indices.sorted(by: >) where index ` — Kotlin can't add a protocol to an existing type. See [`references/swift-kotlin-rules.md`](references/swift-kotlin-rules.md) for the full pattern and replacement examples.

### `String(describing:)` doesn't transpile

Use string interpolation instead:

```swift
// BAD — Kotlin sees String(describing = value), not a valid constructor
let s = String(describing: someValue)

// GOOD
let s = "\(someValue)"

weak / unowned are silently ignored

Skip uses Kotlin's GC. weak and unowned compile but produce strong references on Kotlin. If you use weak to detect deallocation (weakRef.object == nil as a signal), the check will lie.

deinit becomes Kotlin finalize and may not run

Indeterminate timing, may never execute. Don't put critical cleanup (file close, lock release, network disconnect) in deinit — use defer, try/catch, or an explicit close() method the caller invokes.

Variadic initialisers don't transpile

// BAD
init(_ events: HapticEvent...) { self.events = events }

// GOOD
init(_ events: [HapticEvent]) { self.events = events }

Structs and value semantics

Skip wraps mutable structs in a MutableStruct protocol and inserts .sref() calls automatically to preserve value semantics. You'll see .sref() calls all over your transpiled output; that's normal.

If a struct has var properties but is never modified after construction, you can opt out of the wrapping with the // SKIP @nocopy directive to avoid the copy overhead:

// SKIP @nocopy
struct Config {
    var apiURL: URL
    var timeout: TimeInterval
}

Two related rules:

  • Prefer final class over struct for complex model types with many methods. .sref() insertion is fine for plain-data structs but can be subtle with method-heavy ones.
  • Prefer named methods (add(), multiply()) over operator overloads for non-trivial types.

Generics — limits and the @inline(__always) escape

Generic type parameters are erased at runtime. is and as? against generic types ignore the generics:

let xs: Any = [1, 2, 3]
if xs is [String] {       // True on Kotlin — generics erased
    let strings = xs as! [String]
    // strings.first crashes — they're actually Ints
}

Also unsupported:

  • Static members of generic types that use the type's generic parameters.
  • Inner types of generic outer types.

When you need the type available at runtime, annotate the function with @inline(__always) — Skip transpiles it to a Kotlin inline fun :

@inline(__always)
func decode(_ data: Data) throws -> T {
    return try JSONDecoder().decode(T.self, from: data)
}

Use sparingly — every call site inlines the body.

The Kotlish dialect and SKIP comment directives

Inside #if SKIP blocks, Skip relaxes its Swift parsing so you can write Kotlin-ish code that looks like Swift to Xcode but transpiles to real Kotlin. This is Kotlish.

When Kotlish can't express what you need (anonymous inner classes, : class references, property delegates), fall back to a SKIP comment directive:

  • // SKIP INSERT: — inject literal Kotlin at this point. The Swift compiler ignores it.
  • // SKIP REPLACE: — replace the next statement with literal Kotlin.
  • // SKIP DECLARE: — override only the declaration line; transpile the body normally.
  • // SKIP NOWARN — silence a transpiler warning/error on the next line.
  • // SKIP SYMBOLFILE — mark the file as declaration-only; the implementation lives in a parallel .kt.
  • // SKIP EXTERN — JNI / native function reference.
  • // SKIP @nocopy — disable MutableStruct wrapping on a struct (see above).
  • // SKIP @nodispatch — prevent automatic dispatch of an async function onto a Kotlin coroutine context.
  • // SKIP @bridge / @bridgeMembers / @nobridge — control JNI bridging in Fuse mode (see [skip-fuse](../skip-fuse/SKILL.md)).

The directives and the Kotlish syntax (named parameters use : not =; closures use in not ->; wildcard imports use .__ not .*) are covered in detail in [references/swift-kotlin-rules.md](references/swift-kotlin-rules.md).

Kotlin interop — calling Kotlin and Java APIs

#if SKIP
import android.content.Context
let context = ProcessInfo.processInfo.androidContext
let uuid = java.util.UUID.randomUUID().toString()
#endif

For iterating over a Kotlin Map, use entry.value and entry.key:

#if SKIP
for entry in someMap {
    let key = entry.key
    let value = entry.value
}
#endif

The Swift for (k, v) in dict form does not work for Kotlin Maps.

Supported features

Closures, generics (with limits — see above), optionals, async/await, @Observable, pattern matching, guard/if let, defer, Codable, string interpolation, collection operations (map, filter, reduce).

Unsupported features

Key paths as values, complex macros, C interop (use SkipFFI), Mirror / reflection, variadic generics, UIViewRepresentable. The Array.remove(atOffsets:) SwiftUI-extension trap is its own case — see above.

Diagnosing failures

swift test --filter XCSkipTests    # Run transpilation + Kotlin compilation
swift test --filter XCSkipTests -v # Verbose Gradle errors

Kotlin compile errors appear with .kt file paths and line numbers; cross-reference with the original Swift source.

References

  • [references/swift-kotlin-rules.md](references/swift-kotlin-rules.md) — full long-form rules: strings, structs/MutableStruct/.sref(), generics, GC behaviour, Kotlish dialect, every SKIP comment directive with examples, .kotlin() conversion.
  • [references/supported-features.md](references/supported-features.md) — feature support table.

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.