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.
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++
// 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")// Generated by Lucent from device.lucent.ts. Do not edit.#include <lucent/platform/android.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" {#line 8 "device.lucent.ts" lucent::Opt<lucent::String> v0_ = [&]() -> lucent::Opt<lucent::String> { JNIEnv* env = lucent::jni::env(); lucent::jni::LocalFrame frame_(env); static jclass cls_ = lucent::jni::findClass("android/os/Build"); static auto id_ = lucent::jni::staticField(cls_, "MODEL", "Ljava/lang/String;"); auto r_ = env->GetStaticObjectField(cls_, id_); lucent::jni::check(env); return lucent::jni::fromJStringOpt(env, static_cast<jstring>(r_)); }(); bool v2_ = !v0_.has(); lucent::String v5_{}; if (v2_) { v5_ = LUCENT_STR("unknown"); } else { v5_ = v0_.value(); }#line 8 "device.lucent.ts" co_return v5_; }}
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.
Where the types come from
Section titled “Where the types come from”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 prefetchreads them ahead of time. Without an SDK, that platform's imports are untyped andlucent buildskips the platform, saying so.
What a call compiles to
Section titled “What a call compiles to”- 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.
What comes back
Section titled “What comes back”- SDK objects stay in native code. JavaScript never sees them, so return plain values.
- A Java exception becomes an error whose
codeis the exception's class. AnNSErrorbecomes an error whosecodeisdomain:code. nilornullwhere the SDK promises a value throws aTypeError.- 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).