Skip to content
Lucent
SEARCH LUCENT

Search guides, APIs, and examples.

GitHub

Very early and experimental. The language, the generated native code and every package API change without notice. Do not use Lucent in production.

How an SDK call reaches iOS and Android

An SDK call compiles to an Objective-C message send on iOS and a JNI call on Android, typed from the SDKs on your machine.

An SDK call compiles to an Objective-C message send on iOS and a JNI call on Androiddevice.lucent.tsone moduleUIDevice.current.modelthe iOS branchBuild.MODELthe Android branchIOSANDROIDmessage send[UIDevice currentDevice]JNI callGetStaticObjectFieldUIKitfrom your Xcodeandroid.os.Buildfrom your Android SDKTypes come from the SDKs on your machine,read on first import and cached.
device.lucent.ts
import { PLATFORM } from "lucent:platform";
import { UIDevice } from "lucent:ios/UIKit";
import { Build } from "lucent:android/android.os";
import { main } from "lucent:thread";
export async function model(): Promise<string> {
if (PLATFORM === "ios") return main(() => UIDevice.current.model);
else return Build.MODEL ?? "unknown";
}
See the C++
m_device.mm
// Generated by Lucent from device.lucent.ts. Do not edit.
#import <UIKit/UIKit.h>
#include <lucent/platform/ios.h>
#include "m_device.h"
#pragma push_macro("model")
#undef model
namespace lucent_app {
lucent::Promise<lucent::String> m_device::model() {
#line 7 "device.lucent.ts"
{
lucent::Fn<lucent::String()> v0_ = lucent::Fn<lucent::String()>([]() mutable -> lucent::String {
#line 7 "device.lucent.ts"
lucent::NativeRef v0_ = lucent::objc::wrap([UIDevice currentDevice], "UIDevice.current");
return lucent::objc::fromNSString([((UIDevice*)lucent::objc::unwrap(v0_)) model], "UIDevice.model");
});
#line 7 "device.lucent.ts"
lucent::Promise<lucent::String> v1_ = lucent::runOnMain(v0_);
lucent::String v2_ = co_await v1_;
co_return v2_;
}
}
void m_device::init() {
}
} // namespace lucent_app
#pragma pop_macro("model")

One module holds both platforms. Each platform's build compiles its own branch, and "See the C++" shows both results.

The first time a module imports lucent:ios/UIKit or lucent:android/android.os, Lucent reads it from your installed Xcode or Android SDK. The result is cached in ~/.cache/lucent for that SDK version, so later builds reuse it.

  • Android: any package of android.jar, and of the app's Gradle dependencies. Reading the jar takes a few hundred milliseconds.
  • iOS: any framework of the SDK, and of the app's pods. A large framework such as UIKit takes about 40 seconds, once.
  • lucent sdk prefetch reads them ahead of time. Without an SDK, that platform's imports are untyped and lucent build skips the platform, saying so.
  • iOS: a message send with the SDK's own selector, in Objective-C++ compiled against the real headers. Enum values are checked against the SDK at compile time.
  • Android: a JNI call. Class and member IDs are looked up once per call site, and local references are freed after each call.
  • SDK objects stay in native code. JavaScript never sees them, so return plain values.
  • A Java exception becomes an error whose code is the exception's class. An NSError becomes an error whose code is domain:code.
  • nil or null where the SDK promises a value throws a TypeError.
  • APIs marked main-thread only, such as UIKit's, compile only inside main(), or in a block or protocol requirement the SDK calls on the main thread (LUCENT3006).