How is a bridged Kotlin exception surfaced in Objective-C and Swift, and how do you recover the original Kotlin exception object on the Swift side?
answer
- Selector gains error:(NSError**) trailing param
- NSError domain = KotlinException
- Original throwable in userInfo["KotlinException"]
- Swift: method is `throws`, use try + do/catch
- Downcast kotlinException to a typed catch
basics
~10 sKotlin generates an extra error parameter in Objective-C and a throwing method in Swift. The original Kotlin exception is wrapped inside the NSError, so Swift can read it from the error's userInfo.
solid answer
~40 sFor a function marked `@Throws`, the Kotlin/Native compiler appends an `error:(NSError**)` out-parameter to the generated Objective-C selector. When a listed exception is thrown, the runtime allocates an `NSError` with domain `"KotlinException"` and stores the actual `KotlinBase`/`Kotlin…Exception` instance under the userInfo key `"KotlinException"` (also surfaced as the typed `kotlinException` property in newer interop). In Swift the method is imported as `throws`, so you call it with `try` inside `do/catch`; the caught value is a Swift `Error`/`NSError`. To inspect the original Kotlin exception you bridge back via `(error as NSError).userInfo["KotlinException"]` (or the generated `kotlinException` accessor) and downcast to the Kotlin type. The message is also exposed via `localizedDescription`. This lets Swift do typed `catch` against Kotlin exception subclasses.
code
kotlin · 12 linesclass MyDomainException(val code: Int, message: String) : Exception(message)
@Throws(MyDomainException::class)
fun load(id: String): Record {
if (id.isBlank()) throw MyDomainException(400, "blank id")
return fetch(id)
}
// Swift side:
// do { let r = try api.load(id: "") }
// catch let e as NSError {
// if let dom = e.kotlinException as? MyDomainException { print(dom.code) }
// }go deeper
Knows the exception shows up in Swift as an error you catch with do/catch.
Describes the NSError**/error: parameter, the KotlinException domain, and recovering the typed Kotlin object from userInfo.
Designs Swift-side typed catch flows and keeps version-safe access via the kotlinException accessor.
Standardizes a cross-language error model so Swift and Kotlin teams share consistent typed recovery and avoid lossy string-only handling.
## The generated shapes ### Objective-C A `@Throws`-annotated Kotlin function gets an extra trailing parameter in its selector: ```objc - (BOOL)parseInput:(NSString *)input error:(NSError **)error; ``` If the Kotlin call throws a **listed** exception, the runtime sets `*error` to a freshly built `NSError` and the method returns a failure sentinel (e.g. `NO`/`nil`). ### The NSError contents - **domain**: `"KotlinException"`. - **userInfo["KotlinException"]**: the **actual Kotlin throwable** (a `KotlinBase` subclass), so no information is lost. - **localizedDescription / userInfo**: carries the message string. Newer Kotlin/Native interop also exposes a typed convenience: the `NSError` has a `kotlinException` property you can read directly instead of digging into `userInfo`. ### Swift Swift imports the throwing method as `throws`: ```swift do { let result = try parser.parse(input: raw) } catch let error as NSError { if let kt = error.kotlinException as? MyDomainException { handle(kt.code) } } ``` ## Why this matters - **Typed recovery**: because the real Kotlin object survives inside `userInfo`, Swift can `as?`-downcast to specific exception subclasses and branch on Kotlin fields/properties. - **Message access**: `error.localizedDescription` gives the Kotlin `message`. ## Common pitfalls - Treating the Swift `Error` as opaque and only logging `localizedDescription`, losing the typed Kotlin payload. - Forgetting that only **listed** types reach this path; others crash before any `NSError` is built. - Assuming the userInfo key name; it is the literal `"KotlinException"` (prefer the generated `kotlinException` accessor to stay version-safe). ## Relationship to @Throws The bridge **only exists** because of `@Throws`. Without it there is no `error:` parameter generated at all, so there is no channel — the throw is fatal.
- What is the NSError domain for a bridged Kotlin exception?"KotlinException". The original throwable is stored in userInfo under the "KotlinException" key (also exposed via the kotlinException property).
- How does Swift do a typed catch on a specific Kotlin exception subclass?Catch the NSError, read its kotlinException (or userInfo["KotlinException"]), and use `as?` to downcast to the specific Kotlin exception class.
saying these in an interview costs you the question
- Saying the Kotlin object is discarded and only a string remains
- Inventing a different NSError domain name
- Claiming Swift gets a native Kotlin try/catch with no NSError involved
- Forgetting the generated error: out-parameter on the ObjC selector
- Assuming every exception type produces an NSError, even unlisted ones