
Wraps the language's Result into a Swift-visible reference type, forwarding the Result API, offering Swift helpers (try get(), convert to Swift.Result), preserving cancellation, adding test assertions.
kotlin.Result for Kotlin Multiplatform, usable from Swift — for KMP libraries
whose APIs return results to Swift callers.
kotlin.Result is a value class, and Kotlin/Native's Objective-C export erases
it to an untyped Any?, so a KMP library can't return one to Swift.
KotlinResult's Result<T> is a thin reference class that wraps kotlin.Result
and forwards to it:
Result API and behaviour — isSuccess,
getOrThrow, fold, map, recover, onFailure, … with the stdlib's
contracts — as members, so there's nothing to import beyond Result.KotlinResult<T> with helpers: try r.get() returns the value
or throws the Kotlin exception itself, and r.result(as:) gives a
Swift.Result.// gradle/libs.versions.toml
[libraries]
kotlinresult = { module = "com.happycodelucky.kotlinresult:kotlinresult", version = "1.1.0" }
kotlinresult-testing = { module = "com.happycodelucky.kotlinresult:kotlinresult-testing", version = "1.1.0" }
// build.gradle.kts
commonMain.dependencies { api(libs.kotlinresult) } // it's in your public API
commonTest.dependencies { implementation(libs.kotlinresult.testing) } // assertSuccess / assertFailureSwift reaches KotlinResult through your library's framework. Build that
framework with SKIE, and export KotlinResult into
it:
kotlin {
targets.withType<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget>().configureEach {
binaries.withType<org.jetbrains.kotlin.gradle.plugin.mpp.Framework>().configureEach {
export(libs.kotlinresult)
}
}
}The Swift helpers ship inside the library, and SKIE compiles them into your
framework — but only where KotlinResult keeps its plain Swift name, which the
export guarantees. Without it, the link fails with
cannot find type 'KotlinResult' in scope. Don't declare your own
extension KotlinThrowable: Error; KotlinResult provides it.
Return a Result<T>, failing with the exceptions you handle — ideally a sealed
hierarchy, so callers can match every case:
import com.happycodelucky.kotlinresult.Result
sealed class PortException(message: String) : Exception(message) {
class NotANumber(val text: String) : PortException("not a number: $text")
class OutOfRange(val port: Int) : PortException("out of range: $port")
}
fun parsePort(text: String): Result<Int> {
val port = text.toIntOrNull() ?: return Result.failure(PortException.NotANumber(text))
return if (port in 0..65535) Result.success(port) else Result.failure(PortException.OutOfRange(port))
}In files that import it, Result means KotlinResult's; write the stdlib type as
kotlin.Result. Convert between them with toStdlibResult() and toResult().
The kotlin.Result API, as members:
parsePort("8080")
.onSuccess { port -> println("listening on $port") }
.onFailure { e ->
when (e as PortException) {
is PortException.NotANumber -> println("not a number: ${e.text}")
is PortException.OutOfRange -> println("out of range: ${e.port}")
}
}
val port = parsePort(input).getOrDefault(8080)One difference from the stdlib: getOrElse, getOrDefault, recover and
recoverCatching return T, not a supertype of it. To widen, use fold or
getOrNull() ?: default.
get() returns the value — name its type — or throws the Kotlin exception, which
you catch by class and switch over exhaustively with SKIE's onEnum(of:):
do {
let port: Int = try parsePort(text: "8080").get()
print("listening on \(port)")
} catch let e as PortException {
switch onEnum(of: e) {
case .notANumber(let x): print("not a number: \(x.text)")
case .outOfRange(let x): print("out of range: \(x.port)")
}
}try result.get() on a KotlinResult<KotlinUnit> just returns or throws.result.get(as: Int.self) names the type inline, and result.result(as: Int.self)
gives a Swift.Result<Int, any Error>.KotlinResultTypeMismatchError.CancellationError, never as a failed result.kotlinresult-testing adds assertions that work with any test framework
(kotlin.test, JUnit, Kotest):
val port = parsePort("8080").assertSuccess() // the value, or fails with the stack trace
val error = parsePort("nope").assertFailure<PortException.NotANumber>() // the exception, typedIn Swift, build a result to return from a fake:
let ok = KotlinResult<NSString>(value: "hi")
let failed = KotlinResult<KotlinUnit>(failure: KotlinThrowable(message: "boom"))| Kotlin | 2.4 |
| JVM | 21+. Code that calls Result's inline members (fold, map, getOrElse, …) must compile with jvmTarget 21 or higher. |
| Android |
minSdk 30; compile against SDK 34+. |
| iOS / macOS |
iosArm64, iosSimulatorArm64, macosArm64, through your framework (built with SKIE, exporting KotlinResult). Your framework sets the minimum OS. |
The documentation site has guides, per-platform notes, the changelog, and the API reference.
See CONTRIBUTING.md.
kotlin.Result for Kotlin Multiplatform, usable from Swift — for KMP libraries
whose APIs return results to Swift callers.
kotlin.Result is a value class, and Kotlin/Native's Objective-C export erases
it to an untyped Any?, so a KMP library can't return one to Swift.
KotlinResult's Result<T> is a thin reference class that wraps kotlin.Result
and forwards to it:
Result API and behaviour — isSuccess,
getOrThrow, fold, map, recover, onFailure, … with the stdlib's
contracts — as members, so there's nothing to import beyond Result.KotlinResult<T> with helpers: try r.get() returns the value
or throws the Kotlin exception itself, and r.result(as:) gives a
Swift.Result.// gradle/libs.versions.toml
[libraries]
kotlinresult = { module = "com.happycodelucky.kotlinresult:kotlinresult", version = "1.1.0" }
kotlinresult-testing = { module = "com.happycodelucky.kotlinresult:kotlinresult-testing", version = "1.1.0" }
// build.gradle.kts
commonMain.dependencies { api(libs.kotlinresult) } // it's in your public API
commonTest.dependencies { implementation(libs.kotlinresult.testing) } // assertSuccess / assertFailureSwift reaches KotlinResult through your library's framework. Build that
framework with SKIE, and export KotlinResult into
it:
kotlin {
targets.withType<org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget>().configureEach {
binaries.withType<org.jetbrains.kotlin.gradle.plugin.mpp.Framework>().configureEach {
export(libs.kotlinresult)
}
}
}The Swift helpers ship inside the library, and SKIE compiles them into your
framework — but only where KotlinResult keeps its plain Swift name, which the
export guarantees. Without it, the link fails with
cannot find type 'KotlinResult' in scope. Don't declare your own
extension KotlinThrowable: Error; KotlinResult provides it.
Return a Result<T>, failing with the exceptions you handle — ideally a sealed
hierarchy, so callers can match every case:
import com.happycodelucky.kotlinresult.Result
sealed class PortException(message: String) : Exception(message) {
class NotANumber(val text: String) : PortException("not a number: $text")
class OutOfRange(val port: Int) : PortException("out of range: $port")
}
fun parsePort(text: String): Result<Int> {
val port = text.toIntOrNull() ?: return Result.failure(PortException.NotANumber(text))
return if (port in 0..65535) Result.success(port) else Result.failure(PortException.OutOfRange(port))
}In files that import it, Result means KotlinResult's; write the stdlib type as
kotlin.Result. Convert between them with toStdlibResult() and toResult().
The kotlin.Result API, as members:
parsePort("8080")
.onSuccess { port -> println("listening on $port") }
.onFailure { e ->
when (e as PortException) {
is PortException.NotANumber -> println("not a number: ${e.text}")
is PortException.OutOfRange -> println("out of range: ${e.port}")
}
}
val port = parsePort(input).getOrDefault(8080)One difference from the stdlib: getOrElse, getOrDefault, recover and
recoverCatching return T, not a supertype of it. To widen, use fold or
getOrNull() ?: default.
get() returns the value — name its type — or throws the Kotlin exception, which
you catch by class and switch over exhaustively with SKIE's onEnum(of:):
do {
let port: Int = try parsePort(text: "8080").get()
print("listening on \(port)")
} catch let e as PortException {
switch onEnum(of: e) {
case .notANumber(let x): print("not a number: \(x.text)")
case .outOfRange(let x): print("out of range: \(x.port)")
}
}try result.get() on a KotlinResult<KotlinUnit> just returns or throws.result.get(as: Int.self) names the type inline, and result.result(as: Int.self)
gives a Swift.Result<Int, any Error>.KotlinResultTypeMismatchError.CancellationError, never as a failed result.kotlinresult-testing adds assertions that work with any test framework
(kotlin.test, JUnit, Kotest):
val port = parsePort("8080").assertSuccess() // the value, or fails with the stack trace
val error = parsePort("nope").assertFailure<PortException.NotANumber>() // the exception, typedIn Swift, build a result to return from a fake:
let ok = KotlinResult<NSString>(value: "hi")
let failed = KotlinResult<KotlinUnit>(failure: KotlinThrowable(message: "boom"))| Kotlin | 2.4 |
| JVM | 21+. Code that calls Result's inline members (fold, map, getOrElse, …) must compile with jvmTarget 21 or higher. |
| Android |
minSdk 30; compile against SDK 34+. |
| iOS / macOS |
iosArm64, iosSimulatorArm64, macosArm64, through your framework (built with SKIE, exporting KotlinResult). Your framework sets the minimum OS. |
The documentation site has guides, per-platform notes, the changelog, and the API reference.
See CONTRIBUTING.md.