import Foundation import Capacitor import CoreLocation import UIKit @objc(DumonGeolocationPlugin) public class DumonGeolocationPlugin: CAPPlugin, CAPBridgedPlugin { public let identifier = "DumonGeolocationPlugin" public let jsName = "DumonGeolocation" public let pluginMethods: [CAPPluginMethod] = [ CAPPluginMethod(name: "startPositioning", returnType: CAPPluginReturnPromise), CAPPluginMethod(name: "stopPositioning", returnType: CAPPluginReturnPromise), CAPPluginMethod(name: "getLatestPosition", returnType: CAPPluginReturnPromise), CAPPluginMethod(name: "checkAndRequestPermissions", returnType: CAPPluginReturnPromise), CAPPluginMethod(name: "setOptions", returnType: CAPPluginReturnPromise), CAPPluginMethod(name: "getGnssStatus", returnType: CAPPluginReturnPromise), CAPPluginMethod(name: "getLocationServicesStatus", returnType: CAPPluginReturnPromise), CAPPluginMethod(name: "startBackgroundTracking", returnType: CAPPluginReturnPromise), CAPPluginMethod(name: "stopBackgroundTracking", returnType: CAPPluginReturnPromise), CAPPluginMethod(name: "isBackgroundTrackingActive", returnType: CAPPluginReturnPromise), CAPPluginMethod(name: "getBackgroundLatestPosition", returnType: CAPPluginReturnPromise), CAPPluginMethod(name: "openBackgroundPermissionSettings", returnType: CAPPluginReturnPromise), CAPPluginMethod(name: "openNotificationPermissionSettings", returnType: CAPPluginReturnPromise), CAPPluginMethod(name: "setAuthTokens", returnType: CAPPluginReturnPromise), CAPPluginMethod(name: "clearAuthTokens", returnType: CAPPluginReturnPromise), CAPPluginMethod(name: "getAuthState", returnType: CAPPluginReturnPromise), CAPPluginMethod(name: "setBackgroundPostUrl", returnType: CAPPluginReturnPromise), CAPPluginMethod(name: "getBackgroundPostUrl", returnType: CAPPluginReturnPromise), CAPPluginMethod(name: "configureEdgeToEdge", returnType: CAPPluginReturnPromise), CAPPluginMethod(name: "setGpsMode", returnType: CAPPluginReturnPromise) ] private let implementation = DumonGeolocation() private var pendingPermissionCall: CAPPluginCall? private var pendingBackgroundCall: CAPPluginCall? public override func load() { implementation.onPositionUpdate = { [weak self] data in self?.notifyListeners("onPositionUpdate", data: data) } implementation.onGnssStatus = { [weak self] data in self?.notifyListeners("onGnssStatus", data: data) } implementation.onAuthorizationChange = { [weak self] status in self?.handleAuthorizationChange(status) } } @objc func startPositioning(_ call: CAPPluginCall) { let status = implementation.authorizationStatus() if !isLocationAuthorized(status) { call.reject("Location permission not granted") return } implementation.startPositioning() call.resolve() } @objc func stopPositioning(_ call: CAPPluginCall) { implementation.stopPositioning() call.resolve() } @objc func getLatestPosition(_ call: CAPPluginCall) { call.resolve(implementation.getLatestPosition()) } @objc func checkAndRequestPermissions(_ call: CAPPluginCall) { let status = implementation.authorizationStatus() if status == .notDetermined { pendingPermissionCall = call implementation.requestWhenInUsePermission() return } call.resolve(permissionStatusDict(status)) } @objc func setOptions(_ call: CAPPluginCall) { var options: [String: Any] = [:] for (key, value) in call.options { if let key = key as? String { options[key] = value } } implementation.setOptions(options) call.resolve() } @objc func getGnssStatus(_ call: CAPPluginCall) { if let data = implementation.getGnssStatus() { call.resolve(data) } else { call.resolve([:]) } } @objc func getLocationServicesStatus(_ call: CAPPluginCall) { call.resolve(implementation.getLocationServicesStatus()) } @objc func startBackgroundTracking(_ call: CAPPluginCall) { if let postUrl = call.getString("postUrl") { implementation.setBackgroundPostUrl(postUrl) } let status = implementation.authorizationStatus() if status == .authorizedAlways { implementation.startBackgroundTracking() call.resolve() return } pendingBackgroundCall = call implementation.requestAlwaysPermission() } @objc func stopBackgroundTracking(_ call: CAPPluginCall) { implementation.stopBackgroundTracking() call.resolve() } @objc func isBackgroundTrackingActive(_ call: CAPPluginCall) { call.resolve(["active": implementation.isBackgroundTrackingActive()]) } @objc func getBackgroundLatestPosition(_ call: CAPPluginCall) { if let data = implementation.getBackgroundLatestPosition() { call.resolve(data) } else { call.resolve([:]) } } @objc func openBackgroundPermissionSettings(_ call: CAPPluginCall) { openAppSettings(call) } @objc func openNotificationPermissionSettings(_ call: CAPPluginCall) { openAppSettings(call) } @objc func setAuthTokens(_ call: CAPPluginCall) { guard let access = call.getString("accessToken"), !access.isEmpty else { call.reject("accessToken is required") return } guard let refresh = call.getString("refreshToken"), !refresh.isEmpty else { call.reject("refreshToken is required") return } implementation.setAuthTokens(accessToken: access, refreshToken: refresh) call.resolve() } @objc func clearAuthTokens(_ call: CAPPluginCall) { implementation.clearAuthTokens() call.resolve() } @objc func getAuthState(_ call: CAPPluginCall) { call.resolve(["present": implementation.getAuthState()]) } @objc func setBackgroundPostUrl(_ call: CAPPluginCall) { let url = call.getString("url") implementation.setBackgroundPostUrl(url) call.resolve() } @objc func getBackgroundPostUrl(_ call: CAPPluginCall) { let url = implementation.getBackgroundPostUrl() call.resolve(["url": url ?? NSNull()]) } @objc func configureEdgeToEdge(_ call: CAPPluginCall) { // No-op on iOS; UI handled by app. call.resolve() } @objc func setGpsMode(_ call: CAPPluginCall) { let mode = call.getString("mode") ?? "normal" implementation.setGpsMode(mode) call.resolve() } private func handleAuthorizationChange(_ status: CLAuthorizationStatus) { if let call = pendingPermissionCall { pendingPermissionCall = nil call.resolve(permissionStatusDict(status)) } if let call = pendingBackgroundCall { pendingBackgroundCall = nil if status == .authorizedAlways { implementation.startBackgroundTracking() call.resolve() } else { call.reject("Background location permission not granted") } } } private func permissionStatusDict(_ status: CLAuthorizationStatus) -> [String: String] { let granted = isLocationAuthorized(status) return [ "location": granted ? "granted" : "denied", "wifi": "granted" ] } private func isLocationAuthorized(_ status: CLAuthorizationStatus) -> Bool { return status == .authorizedAlways || status == .authorizedWhenInUse } private func openAppSettings(_ call: CAPPluginCall) { guard let url = URL(string: UIApplication.openSettingsURLString) else { call.reject("Unable to open settings") return } DispatchQueue.main.async { UIApplication.shared.open(url, options: [:], completionHandler: nil) call.resolve() } } }