Skip to content

Structs

Swift structs are value types. When exported with @jvm, they are heap-allocated by swift4j and managed the same way as classes — each struct instance has a corresponding Swift object on the heap, held alive by JNI reference counting.

Value-type semantics (copy on assignment) do not carry over to Kotlin. From the JVM side, an exported struct behaves like a reference type.

import Swift4j
@jvm
struct Point {
var x: Double
var y: Double
func distanceTo(_ other: Point) -> Double {
let dx = x - other.x
let dy = y - other.y
return (dx * dx + dy * dy).squareRoot()
}
}
val a = Point(x: 0.0, y: 0.0)
val b = Point(x: 3.0, y: 4.0)
println(a.distanceTo(b)) // 5.0

Stored var properties are exported as read/write; let properties are read-only:

@jvm
struct Player {
var name: String
let id: Int
}
val player = Player(name: "Alice", id: 42)
player.name = "Bob"
println(player.name) // Bob

Swift inout parameters allow a method to mutate a struct passed by the caller. In the generated Kotlin code, inout parameters are passed as single-element arrays — the method reads array[0], modifies the Swift copy, and writes the result back to array[0].

@jvm
struct Team {
func addScore(to player: inout Player, points: Int) {
player.name = "\(player.name) [+\(points)]"
}
}
val team = Team()
val player = Player(name: "Alice", id: 1)
val playerRef = arrayOf(player)
team.addScore(to: playerRef, points: 10)
println(playerRef[0].name) // Alice [+10]

Static methods work the same as on classes:

@jvm
struct Geometry {
static func circleArea(radius: Double) -> Double {
Double.pi * radius * radius
}
}
val area = Geometry.circleArea(radius: 5.0)