mobiler 0.18.0

Build mobile apps in Rust — one core, native UI on Android, iOS, and the web (CLI)
import SharedTypes
import CoreLocation

/// Free bundled plugin: current device location. op "get" → "lat,lng", ok=false on denial/error.
/// CLLocationManager drives the when-in-use authorization prompt, then a one-shot requestLocation.
/// Needs NSLocationWhenInUseUsageDescription (added to Info.plist by `plugin add`).
@MainActor
enum GeolocationPlugin {
    static func handle(op: String, input: String) async -> PluginResponse {
        guard op == "get" else { return PluginResponse(ok: false, output: "unknown op '\(op)'") }
        return await withCheckedContinuation { cont in
            let delegate = LocationDelegate { cont.resume(returning: $0) }
            LocationDelegate.retained = delegate // CLLocationManager holds its delegate weakly
            delegate.start()
        }
    }
}

private final class LocationDelegate: NSObject, CLLocationManagerDelegate {
    static var retained: LocationDelegate?
    private let manager = CLLocationManager()
    private let onResult: (PluginResponse) -> Void
    private var done = false
    init(onResult: @escaping (PluginResponse) -> Void) {
        self.onResult = onResult
        super.init()
        manager.delegate = self
    }

    func start() { proceed(manager.authorizationStatus) }

    func locationManagerDidChangeAuthorization(_ m: CLLocationManager) { proceed(m.authorizationStatus) }

    private func proceed(_ status: CLAuthorizationStatus) {
        switch status {
        case .authorizedWhenInUse, .authorizedAlways: manager.requestLocation()
        case .denied, .restricted: finish(PluginResponse(ok: false, output: "denied"))
        default: manager.requestWhenInUseAuthorization() // .notDetermined → wait for the callback
        }
    }

    func locationManager(_ m: CLLocationManager, didUpdateLocations locs: [CLLocation]) {
        guard let c = locs.first?.coordinate else { finish(PluginResponse(ok: false, output: "no location")); return }
        finish(PluginResponse(ok: true, output: "\(c.latitude),\(c.longitude)"))
    }

    func locationManager(_ m: CLLocationManager, didFailWithError error: Error) {
        finish(PluginResponse(ok: false, output: error.localizedDescription))
    }

    private func finish(_ r: PluginResponse) {
        if done { return }
        done = true
        onResult(r)
        LocationDelegate.retained = nil
    }
}