5. Read the position from iOS and Android
One module that calls CoreLocation on iOS and LocationManager on Android.
import { PLATFORM } from "lucent:platform";import { CLLocation, CLLocationManager } from "lucent:ios/CoreLocation";import { Location, LocationManager } from "lucent:android/android.location";import { appContext } from "lucent:android";import { main } from "lucent:thread";import type { Fix } from "./trip.lucent";
// --- iOS ---------------------------------------------------------------------
function fromCLLocation(location: CLLocation): Fix { const c = location.coordinate; return { latitude: c.latitude, longitude: c.longitude, time: location.timestamp.getTime() };}
// --- Android -----------------------------------------------------------------
function fromLocation(location: Location): Fix { return { latitude: location.getLatitude(), longitude: location.getLongitude(), time: Number(location.getTime()), };}
// --- The module --------------------------------------------------------------
/** The last position the device knows, without waiting for a new one. */export async function lastFix(): Promise<Fix | null> { if (PLATFORM === "ios") { const location = await main(() => new CLLocationManager().location); return location ? fromCLLocation(location) : null; } else { const manager = appContext().getSystemService(LocationManager); const location = manager?.getLastKnownLocation(LocationManager.GPS_PROVIDER) ?? null; return location ? fromLocation(location) : null; }}import { useEffect, useState } from "react";import { Text } from "react-native";import { lastFix } from "./src/location.lucent";import { Trip } from "./src/trip.lucent";
// A walk along the Seine, one fix every ten minutes.…
export default function App() { const [shape, setShape] = useState<number | null>(null); const [here, setHere] = useState("Locating…");
useEffect(() => { lastFix().then( (fix) => setHere( fix ? `You are at ${fix.latitude.toFixed(4)}, ${fix.longitude.toFixed(4)}.` : "No position yet.", ), (e: Error) => setHere(e.message), ); }, []);
useEffect(() => { const controller = new AbortController();… <Text> {summary.fixes} fixes, {(summary.meters / 1000).toFixed(2)} km, top speed{" "} {summary.topSpeed.toFixed(1)} m/s. {shape === null ? " Simplifying…" : ` ${shape} fixes shape the route.`} {shape === null ? " Simplifying…" : ` ${shape} fixes shape the route.`} {here} </Text> );}See the C++
// Generated by Lucent from location.lucent.ts. Do not edit.#import <CoreLocation/CoreLocation.h>#import <_LocationEssentials/LocationEssentials.h>#include <lucent/platform/ios.h>#include "m_location.h"
#pragma push_macro("c")#undef c#pragma push_macro("fromCLLocation")#undef fromCLLocation#pragma push_macro("lastFix")#undef lastFix#pragma push_macro("latitude")#undef latitude#pragma push_macro("location")#undef location#pragma push_macro("longitude")#undef longitude#pragma push_macro("time")#undef time
namespace lucent_app {
lucent::Ref<lucent_app::S_Fix> m_location::fromCLLocation(lucent::NativeRef p0_) {#line 11 "location.lucent.ts" lucent::Ref<lucent_app::S_CLLocationCoordinate2D> c = ({ auto s0_ = [((CLLocation*)lucent::objc::unwrap(p0_)) coordinate]; auto o0_ = std::make_shared<lucent_app::S_CLLocationCoordinate2D>(); o0_->latitude = static_cast<double>(s0_.latitude); o0_->longitude = static_cast<double>(s0_.longitude); o0_; });#line 12 "location.lucent.ts" double v3_ = c->latitude; double v5_ = c->longitude; lucent::Date v6_ = lucent::objc::fromNSDate([((CLLocation*)lucent::objc::unwrap(p0_)) timestamp], "CLLocation.timestamp"); double v7_ = v6_->getTime(); return ({ auto obj_3 = std::make_shared<lucent_app::S_Fix>(); obj_3->latitude = v3_; obj_3->longitude = v5_; obj_3->time = v7_; obj_3; });}
lucent::Promise<lucent::Opt<lucent::Ref<lucent_app::S_Fix>>> m_location::lastFix() {#line 29 "location.lucent.ts" {#line 30 "location.lucent.ts" lucent::Fn<lucent::Opt<lucent::NativeRef>()> v0_ = lucent::Fn<lucent::Opt<lucent::NativeRef>()>([]() mutable -> lucent::Opt<lucent::NativeRef> {#line 30 "location.lucent.ts" lucent::NativeRef v0_ = lucent::objc::wrap([[CLLocationManager alloc] init], "new CLLocationManager"); return lucent::objc::wrapOpt([((CLLocationManager*)lucent::objc::unwrap(v0_)) location]); });#line 30 "location.lucent.ts" lucent::Promise<lucent::Opt<lucent::NativeRef>> v1_ = lucent::runOnMain(v0_); lucent::Opt<lucent::NativeRef> v2_ = co_await v1_; lucent::Opt<lucent::NativeRef> location = std::move(v2_);#line 31 "location.lucent.ts" bool v4_ = lucent::truthy(location); lucent::Opt<lucent::Ref<lucent_app::S_Fix>> v11_{}; if (v4_) { lucent::Ref<lucent_app::S_Fix> v7_ = lucent_app::m_location::fromCLLocation(location.value()); v11_ = lucent::Opt<lucent::Ref<lucent_app::S_Fix>>(v7_); } else { v11_ = lucent::Opt<lucent::Ref<lucent_app::S_Fix>>(lucent::null); }#line 31 "location.lucent.ts" co_return v11_; }}
void m_location::init() {}
} // namespace lucent_app
#pragma pop_macro("time")#pragma pop_macro("longitude")#pragma pop_macro("location")#pragma pop_macro("latitude")#pragma pop_macro("lastFix")#pragma pop_macro("fromCLLocation")#pragma pop_macro("c")// Generated by Lucent from location.lucent.ts. Do not edit.#include <lucent/platform/android.h>#include "m_location.h"
#pragma push_macro("fromLocation")#undef fromLocation#pragma push_macro("lastFix")#undef lastFix#pragma push_macro("latitude")#undef latitude#pragma push_macro("location")#undef location#pragma push_macro("longitude")#undef longitude#pragma push_macro("manager")#undef manager#pragma push_macro("time")#undef time
namespace lucent_app {
lucent::Ref<lucent_app::S_Fix> m_location::fromLocation(lucent::NativeRef p0_) {#line 19 "location.lucent.ts" double v1_ = [&]() -> double { JNIEnv* env = lucent::jni::env(); auto recv_ = p0_; lucent::jni::LocalFrame frame_(env); static jclass cls_ = lucent::jni::findClass("android/location/Location"); static auto id_ = lucent::jni::method(cls_, "getLatitude", "()D"); auto r_ = env->CallDoubleMethod(lucent::jni::unwrap(recv_), id_); lucent::jni::check(env); return static_cast<double>(r_); }();#line 20 "location.lucent.ts" double v2_ = [&]() -> double { JNIEnv* env = lucent::jni::env(); auto recv_ = p0_; lucent::jni::LocalFrame frame_(env); static jclass cls_ = lucent::jni::findClass("android/location/Location"); static auto id_ = lucent::jni::method(cls_, "getLongitude", "()D"); auto r_ = env->CallDoubleMethod(lucent::jni::unwrap(recv_), id_); lucent::jni::check(env); return static_cast<double>(r_); }();#line 21 "location.lucent.ts" lucent::BigInt v3_ = [&]() -> lucent::BigInt { JNIEnv* env = lucent::jni::env(); auto recv_ = p0_; lucent::jni::LocalFrame frame_(env); static jclass cls_ = lucent::jni::findClass("android/location/Location"); static auto id_ = lucent::jni::method(cls_, "getTime", "()J"); auto r_ = env->CallLongMethod(lucent::jni::unwrap(recv_), id_); lucent::jni::check(env); return lucent::BigInt{r_}; }(); double v4_ = v3_.toDouble();#line 18 "location.lucent.ts" return ({ auto obj_3 = std::make_shared<lucent_app::S_Fix>(); obj_3->latitude = v1_; obj_3->longitude = v2_; obj_3->time = v4_; obj_3; });}
lucent::Promise<lucent::Opt<lucent::Ref<lucent_app::S_Fix>>> m_location::lastFix() {#line 29 "location.lucent.ts" {#line 33 "location.lucent.ts" lucent::NativeRef v0_ = lucent::jni::appContext(); lucent::Opt<lucent::NativeRef> manager = [&]() -> lucent::Opt<lucent::NativeRef> { JNIEnv* env = lucent::jni::env(); auto recv_ = v0_; lucent::jni::LocalFrame frame_(env); static jclass cls_ = lucent::jni::findClass("android/content/Context"); static auto id_ = lucent::jni::method(cls_, "getSystemService", "(Ljava/lang/Class;)Ljava/lang/Object;"); auto r_ = env->CallObjectMethod(lucent::jni::unwrap(recv_), id_, lucent::jni::findClass("android/location/LocationManager")); lucent::jni::check(env); return lucent::jni::wrapOpt(env, r_); }();#line 34 "location.lucent.ts" bool v4_ = !manager.has(); lucent::Opt<lucent::NativeRef> v10_{}; if (v4_) { v10_ = lucent::Opt<lucent::NativeRef>(lucent::undefined); } else { lucent::NativeRef v5_ = manager.value(); lucent::String v6_ = LUCENT_STR("gps"); v10_ = [&]() -> lucent::Opt<lucent::NativeRef> { JNIEnv* env = lucent::jni::env(); auto recv_ = v5_; lucent::jni::LocalFrame frame_(env); static jclass cls_ = lucent::jni::findClass("android/location/LocationManager"); static auto id_ = lucent::jni::method(cls_, "getLastKnownLocation", "(Ljava/lang/String;)Landroid/location/Location;"); auto r_ = env->CallObjectMethod(lucent::jni::unwrap(recv_), id_, lucent::jni::toJString(env, v6_)); lucent::jni::check(env); return lucent::jni::wrapOpt(env, r_); }(); }#line 34 "location.lucent.ts" bool v12_ = !v10_.has(); lucent::Opt<lucent::NativeRef> v15_{}; if (v12_) { v15_ = lucent::Opt<lucent::NativeRef>(lucent::null); } else { v15_ = v10_; }#line 34 "location.lucent.ts" lucent::Opt<lucent::NativeRef> location = v15_;#line 35 "location.lucent.ts" bool v17_ = lucent::truthy(location); lucent::Opt<lucent::Ref<lucent_app::S_Fix>> v24_{}; if (v17_) { lucent::Ref<lucent_app::S_Fix> v20_ = lucent_app::m_location::fromLocation(location.value()); v24_ = lucent::Opt<lucent::Ref<lucent_app::S_Fix>>(v20_); } else { v24_ = lucent::Opt<lucent::Ref<lucent_app::S_Fix>>(lucent::null); }#line 35 "location.lucent.ts" co_return v24_; }}
void m_location::init() {}
} // namespace lucent_app
#pragma pop_macro("time")#pragma pop_macro("manager")#pragma pop_macro("longitude")#pragma pop_macro("location")#pragma pop_macro("latitude")#pragma pop_macro("lastFix")#pragma pop_macro("fromLocation")One module holds both platforms. Inside if (PLATFORM === "ios"), the code uses CoreLocation; in the else, Android's LocationManager. Each platform's build compiles only its own branch.
fromCLLocationuses an iOS type, so it's iOS code and compiles only for iOS.fromLocationis Android code.CLLocationManageris main-thread only on iOS, so it's made insidemain(). Its result, aCLLocation, comes back as a promise.- Both branches turn SDK objects into a
Fixbefore returning. SDK objects can't cross to JavaScript. import type { Fix }shares the type with the trip module.- Java's
getTime()returns along, which Lucent gives as abigint. Milliseconds since 1970 fit in a number, soNumber()makes theFixfield one.
Android uses the platform's LocationManager, not Play services' fused provider, which the app doesn't include.
Run it
Section titled “Run it”Rebuild the app. On a simulator or emulator with a location set, the screen adds where you are.
Without a permission, iOS has no position to give, and Android throws a java.lang.SecurityException error, whose message the screen shows. Step 7 asks for the permission.