Classes
Swift classes are reference types. Each exported class instance corresponds to a single Swift heap object whose lifetime is managed by JNI reference counting on the JVM side.
Exporting a class
Section titled “Exporting a class”Apply @jvm to the class declaration:
import Swift4j
@jvmclass Counter { private var value = 0
func increment() { value += 1 } func decrement() { value -= 1 } func getValue() -> Int { value }}The generated Kotlin class has the same name and the same public members:
System.loadLibrary("MyLibrary")
val counter = Counter()counter.increment()counter.increment()println(counter.getValue()) // 2Constructors
Section titled “Constructors”All non-private init methods are exported. Parameters use the same type mapping as methods.
@jvmclass Connection { init(host: String, port: Int) { ... } init(url: String) { ... }}val conn = Connection(host: "localhost", port: 5432)Note: Swift argument labels become named parameters in Kotlin. The first unlabeled parameter (_) becomes a positional parameter.
Properties
Section titled “Properties”Stored properties and computed properties are exported as getters and setters:
@jvmclass User { var name: String let id: Int var displayName: String { "\(name) (\(id))" }
private(set) var score: Int = 0
init(id: Int, name: String) { self.id = id self.name = name }}varproperties with both getter and setter → read/write property in Kotlinletproperties → read-only (getter only)- Computed
{ get }properties → read-only private(set)properties → read-only from Kotlin
Static methods and properties
Section titled “Static methods and properties”Static members are exported as static methods on the generated class:
@jvmclass MathUtils { static func add(_ a: Int, _ b: Int) -> Int { a + b } static let pi = 3.14159}val sum = MathUtils.add(2, 3)val pi = MathUtils.piObject lifecycle
Section titled “Object lifecycle”A Swift object stays alive as long as the corresponding Kotlin/Java proxy object is reachable on the JVM heap. When the JVM garbage-collects the proxy, it calls the native deinit callback, which releases the ARC retain count and allows the Swift object to be freed.
deinit runs at GC time, not at scope exit. If your Swift class holds resources that must be released promptly (file handles, network connections), expose an explicit close() or dispose() method and call it from Kotlin before dropping the reference.
Excluding members from export
Section titled “Excluding members from export”@jvm emits a compiler warning for any member it cannot export — for example, a method whose parameter type is not an exported type or a built-in JNI-compatible type. Use @nonjvm to mark such members as intentionally excluded and suppress the warning:
@jvmclass Parser { func parse(input: String) -> [Token] { ... } // exported
@nonjvm func parseInternal(buffer: UnsafeBufferPointer<UInt8>) { ... } // excluded, no warning}@nonjvm has no runtime effect — it is a compile-time marker only.
Nested classes
Section titled “Nested classes”Nested classes can be exported if the enclosing class is also exported. On Swift 6, apply @jvm to both:
@jvmclass Parent { @jvm class Child { func hello() -> String { "Hello from Child" } }}val child = Parent.Child()println(child.hello())