How do you consume an Apple Objective-C framework (like Foundation/UIKit) from Kotlin/Native, and how do Objective-C types and nullability map into Kotlin?
answer
- platform.Foundation/UIKit.* = prebuilt Apple bindings, no .def
- Class→class, method→function, protocol→interface, category→extension
- nullable→T?, nonnull→T, un-audited→platform type T!
- Obj-C objects bridged via ARC, auto reference-counted
- Custom framework: .def with language = Objective-C, modules = ...
basics
~10 sApple frameworks ship with prebuilt cinterop bindings, so you just import them (e.g. platform.Foundation.*) and call them. Objective-C classes become Kotlin classes, methods become Kotlin functions, and nullable Obj-C types become Kotlin nullable types.
solid answer
~30 sKotlin/Native bundles cinterop bindings for the Apple SDK frameworks; you import them from the `platform.*` packages (e.g. `platform.Foundation.NSDate`, `platform.UIKit.UIView`). Objective-C **classes** map to Kotlin classes, **methods** to Kotlin functions (Obj-C selectors like `dateWithTimeIntervalSince1970:` become idiomatic Kotlin names), **protocols** to interfaces (`*Protocol`), and **categories** to extensions. Obj-C **nullability audits** (`nullable`/`nonnull`) map to Kotlin `T?`/`T`; un-audited pointers become **platform types** (`T!`). `id` maps roughly to `Any?`/`NSObject`. Memory is bridged through ARC interop — Obj-C objects are reference-counted and managed automatically. For custom frameworks you write a `.def` with `language = Objective-C` and the `modules`/`linkerOpts` for the framework.
code
kotlin · 10 linesimport platform.Foundation.*
import kotlinx.cinterop.ExperimentalForeignApi
@OptIn(ExperimentalForeignApi::class)
fun documentsDir(): String? {
val urls = NSFileManager.defaultManager
.URLsForDirectory(NSDocumentDirectory, NSUserDomainMask)
// platform type handled defensively
return (urls.firstOrNull() as? NSURL)?.path
}go deeper
Knows Apple frameworks are importable from platform.* without extra setup.
Maps classes/methods/protocols correctly and handles nullability/platform types when calling Foundation APIs.
Explains ARC bridging, writes a .def for a custom Obj-C framework with correct linkerOpts, and reasons about platform-type safety.
Defines an interop layer/policy for a shared KMP module so iOS consumers get clean, null-safe Kotlin surfaces over messy native headers.
## Built-in Apple bindings Kotlin/Native ships **prebuilt cinterop bindings** for the Apple platform SDKs. You access them via the **`platform.*`** package namespace: - `platform.Foundation.*` — `NSString`, `NSDate`, `NSURL`, `NSArray`, … - `platform.UIKit.*`, `platform.AppKit.*`, `platform.CoreGraphics.*`, etc. - `platform.darwin.*` — low-level types. No `.def` needed for these — just `import` and call. ## Type mapping (Objective-C → Kotlin) - **Class** → Kotlin class. `NSMutableArray` is a Kotlin class extending `NSArray`. - **Method** → Kotlin function. The Obj-C selector `stringWithFormat:` becomes a function; named parameters from the selector become parameter names. - **Class (factory) methods** → companion / static-like functions; many `init…` selectors are exposed as constructors or factory functions. - **Protocol** → Kotlin interface, named `…Protocol` (e.g. `NSCopyingProtocol`). - **Category** → Kotlin extension functions on the class. - **`id`** → `Any?` / `NSObject`-ish. - **Blocks** → Kotlin function types (lambdas). ## Nullability and platform types Objective-C headers can be **nullability-audited** with `nullable` / `_Nonnull` / `_Nullable` / `NS_ASSUME_NONNULL_BEGIN`: - `nullable` → Kotlin **`T?`**. - `nonnull` → Kotlin **`T`**. - **Un-audited** pointer → Kotlin **platform type `T!`** — you may treat it as nullable or not, but a null at runtime throws if you assumed non-null. Treat platform types defensively. ## Memory / ARC interop Objective-C uses **ARC (Automatic Reference Counting)**. Kotlin/Native bridges into ARC: Obj-C objects are retained/released for you, so you don't manually manage them like raw C pointers. (Raw C pointers, by contrast, still use `memScoped`/`CPointer`.) ## Custom Obj-C frameworks For a third-party `.framework`, write a `.def`: ``` language = Objective-C modules = MyFramework package = com.example.myframework ``` plus `compilerOpts`/`linkerOpts` like `-framework MyFramework -F/path/to/Frameworks`, then declare it in the `cinterops` block. ## Calling example ```kotlin import platform.Foundation.* @OptIn(ExperimentalForeignApi::class) fun now(): String { val date = NSDate() val fmt = NSDateFormatter().apply { dateFormat = "yyyy-MM-dd" } return fmt.stringFromDate(date) } ``` This is the foundation of **sharing Kotlin code with iOS** and consuming native frameworks directly.
- What is a Kotlin platform type and why does Obj-C interop produce them?T! means nullability is unknown because the header wasn't audited; Kotlin lets you treat it as T or T?, but assuming non-null risks an NPE if it's null at runtime.
- Do you manage retain/release manually when using NSObject subclasses?No, Kotlin/Native bridges Obj-C ARC, so reference counting is automatic for Obj-C objects.
- How do Objective-C blocks appear in Kotlin?As Kotlin function types, so you pass lambdas where the API expects a block.
The Apple frameworks come pre-translated into Kotlin the way a phrasebook gives you ready-made sentences — you only write a .def (your own glossary) for words the phrasebook doesn't cover.
saying these in an interview costs you the question
- Thinking you must write a .def for Foundation/UIKit
- Assuming all Obj-C pointers map to non-null Kotlin types
- Manually retaining/releasing Obj-C objects
- Confusing protocols (interfaces) with classes
- Ignoring platform types and getting surprise NPEs