@tokamakdev/plugin-location 0.1.0-beta.128 → 0.1.0-beta.130

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,158 @@
1
+ import CoreLocation
2
+ import Foundation
3
+
4
+ extension TokamakLocationPlugin {
5
+ struct Coordinates: Codable {
6
+ let latitude: Double
7
+ let longitude: Double
8
+ let accuracy: Double
9
+ let altitude: Double?
10
+ let altitudeAccuracy: Double?
11
+ let heading: Double?
12
+ let speed: Double?
13
+ }
14
+
15
+ struct Position: Codable {
16
+ let coords: Coordinates
17
+ /// Milliseconds since the epoch.
18
+ let timestamp: Double
19
+
20
+ init(_ location: CLLocation) {
21
+ let coordinate = location.coordinate
22
+ let altitude = location.verticalAccuracy >= 0
23
+ coords = Coordinates(
24
+ latitude: coordinate.latitude,
25
+ longitude: coordinate.longitude,
26
+ accuracy: location.horizontalAccuracy,
27
+ altitude: altitude ? location.altitude : nil,
28
+ altitudeAccuracy: altitude ? location.verticalAccuracy : nil,
29
+ heading: location.course >= 0 ? location.course : nil,
30
+ speed: location.speed >= 0 ? location.speed : nil
31
+ )
32
+ timestamp = location.timestamp.milliseconds
33
+ }
34
+ }
35
+
36
+ struct LocationUpdate: Codable {
37
+ let positions: [Position]
38
+ /// True on the last update before updates pause while the device is still.
39
+ let stationary: Bool?
40
+ }
41
+
42
+ enum Transition: String, Codable {
43
+ case enter
44
+ case exit
45
+ }
46
+
47
+ struct RegionTransition: Codable {
48
+ let id: String
49
+ let transition: Transition
50
+ /// Milliseconds since the epoch.
51
+ let timestamp: Double
52
+
53
+ init(id: String, entered: Bool, at date: Date) {
54
+ self.id = id
55
+ transition = entered ? .enter : .exit
56
+ timestamp = date.milliseconds
57
+ }
58
+ }
59
+
60
+ struct Visit: Codable {
61
+ let latitude: Double
62
+ let longitude: Double
63
+ let accuracy: Double
64
+ /// Milliseconds since the epoch, or nil when unknown.
65
+ let arrival: Double?
66
+ /// Milliseconds since the epoch, or nil before the user leaves.
67
+ let departure: Double?
68
+
69
+ init(_ visit: CLVisit) {
70
+ latitude = visit.coordinate.latitude
71
+ longitude = visit.coordinate.longitude
72
+ accuracy = visit.horizontalAccuracy
73
+ arrival = visit.arrivalDate == .distantPast ? nil : visit.arrivalDate.milliseconds
74
+ departure = visit.departureDate == .distantFuture ? nil : visit.departureDate.milliseconds
75
+ }
76
+ }
77
+
78
+ struct LocationErrorEvent: Codable {
79
+ let name: String
80
+ let message: String
81
+ /// The region's id, when the failure concerns one.
82
+ let id: String?
83
+ }
84
+
85
+ enum Permission: String, Codable {
86
+ case granted
87
+ case denied
88
+ case prompt
89
+ }
90
+
91
+ enum Precision: String, Codable {
92
+ case precise
93
+ case approximate
94
+ }
95
+
96
+ struct LocationPermission: Codable {
97
+ let inUse: Permission
98
+ let background: Permission
99
+ let precision: Precision
100
+ }
101
+
102
+ struct PermissionRequest: Codable {
103
+ let background: Bool?
104
+ let precision: Precision?
105
+ /// The key in the app's `NSLocationTemporaryUsageDescriptionDictionary` of the reason to show.
106
+ let purpose: String?
107
+ }
108
+
109
+ struct SetupConfig: Codable {
110
+ let significantChanges: Bool?
111
+ let visits: Bool?
112
+ }
113
+
114
+ /// The live update configuration, by Apple's name.
115
+ enum Activity: String, Codable {
116
+ case `default`
117
+ case automotiveNavigation
118
+ case otherNavigation
119
+ case fitness
120
+ case airborne
121
+ case maritime
122
+ }
123
+
124
+ struct UpdateOptions: Codable {
125
+ let activity: Activity?
126
+ }
127
+
128
+ struct Region: Codable {
129
+ let id: String
130
+ let latitude: Double
131
+ let longitude: Double
132
+ /// In metres.
133
+ let radius: Double
134
+ let onEntry: Bool?
135
+ let onExit: Bool?
136
+ }
137
+
138
+ enum RegionState: String, Codable {
139
+ case inside
140
+ case outside
141
+ case unknown
142
+ }
143
+
144
+ struct MonitoredRegion: Codable {
145
+ let id: String
146
+ let latitude: Double
147
+ let longitude: Double
148
+ let radius: Double
149
+ let onEntry: Bool
150
+ let onExit: Bool
151
+ /// Whether the device is inside the region, as the system last determined.
152
+ let state: RegionState
153
+ }
154
+ }
155
+
156
+ extension Date {
157
+ fileprivate var milliseconds: Double { timeIntervalSince1970 * 1000 }
158
+ }
@@ -1,7 +1,7 @@
1
1
  import CoreLocation
2
2
  import Foundation
3
3
 
4
- /// The JSON of the running updates' options, as `startUpdates` received them.
4
+ /// The JSON of the running updates' options.
5
5
  private let updatesKey = "tokamak.location.updates"
6
6
 
7
7
  /// Continuous updates from `CLLocationUpdate.liveUpdates`. iOS launches an app
@@ -22,16 +22,16 @@ final class TokamakLocationUpdates {
22
22
  }
23
23
 
24
24
  /// The options of the running updates, or nil.
25
- var options: [String: Any]? {
25
+ var options: TokamakLocationPlugin.UpdateOptions? {
26
26
  defaults.data(forKey: updatesKey)
27
- .flatMap { try? JSONSerialization.jsonObject(with: $0) as? [String: Any] }
27
+ .flatMap { try? JSONDecoder().decode(TokamakLocationPlugin.UpdateOptions.self, from: $0) }
28
28
  }
29
29
 
30
30
  /// Starts updates with `options`, replacing any running.
31
- func start(_ options: [String: Any]) throws(TokamakPluginError) {
32
- let configuration = try Self.configuration(options)
31
+ func start(_ options: TokamakLocationPlugin.UpdateOptions) throws(TokamakPluginError) {
32
+ let configuration = try Self.configuration(options.activity ?? .default)
33
33
  stop()
34
- defaults.set(try? JSONSerialization.data(withJSONObject: options), forKey: updatesKey)
34
+ defaults.set(try? JSONEncoder().encode(options), forKey: updatesKey)
35
35
  run(configuration)
36
36
  }
37
37
 
@@ -39,7 +39,8 @@ final class TokamakLocationUpdates {
39
39
  /// their session for only a few seconds after it launches the app, so call
40
40
  /// it before launch finishes.
41
41
  func resume() {
42
- guard let options, let configuration = try? Self.configuration(options) else { return }
42
+ guard let options, let configuration = try? Self.configuration(options.activity ?? .default)
43
+ else { return }
43
44
  run(configuration)
44
45
  }
45
46
 
@@ -60,16 +61,9 @@ final class TokamakLocationUpdates {
60
61
  session = CLBackgroundActivitySession()
61
62
  }
62
63
  #endif
63
- task = Task { @MainActor [weak self] in
64
- do {
65
- for try await update in CLLocationUpdate.liveUpdates(configuration) {
66
- self?.receive(update)
67
- }
68
- } catch {
69
- if !Task.isCancelled {
70
- self?.events.fail(
71
- .locationUnavailable(error.localizedDescription))
72
- }
64
+ task = events.task { [weak self] in
65
+ for try await update in CLLocationUpdate.liveUpdates(configuration) {
66
+ self?.receive(update)
73
67
  }
74
68
  }
75
69
  }
@@ -104,17 +98,17 @@ final class TokamakLocationUpdates {
104
98
  return flags.filter(\.0).map { TokamakPluginError(name: $0.1, message: $0.2) }
105
99
  }
106
100
 
107
- /// The live update configuration `options` name as their `activity`.
101
+ /// The live update configuration of `activity`.
108
102
  private static func configuration(
109
- _ options: [String: Any]
103
+ _ activity: TokamakLocationPlugin.Activity
110
104
  ) throws(TokamakPluginError) -> CLLocationUpdate.LiveConfiguration {
111
- switch options["activity"] as? String ?? "default" {
112
- case "default": return .default
113
- case "automotiveNavigation": return .automotiveNavigation
114
- case "otherNavigation": return .otherNavigation
115
- case "fitness": return .fitness
116
- case "airborne": return .airborne
117
- case "maritime":
105
+ switch activity {
106
+ case .default: return .default
107
+ case .automotiveNavigation: return .automotiveNavigation
108
+ case .otherNavigation: return .otherNavigation
109
+ case .fitness: return .fitness
110
+ case .airborne: return .airborne
111
+ case .maritime:
118
112
  // The SDKs that have `maritime` ship with Swift 6.4.
119
113
  #if compiler(>=6.4)
120
114
  if #available(iOS 27, macOS 27, *) {
@@ -122,8 +116,6 @@ final class TokamakLocationUpdates {
122
116
  }
123
117
  #endif
124
118
  throw .notSupported("The maritime activity needs iOS 27 or macOS 27")
125
- default:
126
- throw TokamakPluginError(name: "TypeError", message: "activity is not a live configuration")
127
119
  }
128
120
  }
129
121
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tokamakdev/plugin-location",
3
- "version": "0.1.0-beta.128",
3
+ "version": "0.1.0-beta.130",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/mantty/tokamak.git",
@@ -9,16 +9,17 @@
9
9
  "type": "module",
10
10
  "exports": {
11
11
  ".": "./src/index.ts",
12
- "./events": "./events/index.ts"
12
+ "./apple": "./src/apple.generated.ts",
13
+ "./android": "./src/android.generated.ts",
14
+ "./events": "./src/events.ts"
13
15
  },
14
16
  "peerDependencies": {
15
- "@tokamakdev/plugin": "0.1.0-beta.128"
17
+ "@tokamakdev/plugin": "0.1.0-beta.130"
16
18
  },
17
19
  "files": [
18
20
  "android",
19
21
  "apple",
20
22
  "build",
21
- "events",
22
23
  "src",
23
24
  "web",
24
25
  "tokamak-plugin.json",
@@ -0,0 +1,170 @@
1
+ // Generated by `tok plugin generate android`. Do not edit.
2
+ import { NativeEndpoint, type JsonStream, type TokamakContext } from "@tokamakdev/plugin";
3
+ import { defineEvent } from "@tokamakdev/plugin/events";
4
+
5
+ export type Accuracy = "high" | "balanced" | "low";
6
+
7
+ export interface Coordinates {
8
+ readonly latitude: number;
9
+ readonly longitude: number;
10
+ readonly accuracy: number;
11
+ readonly altitude?: number;
12
+ readonly altitudeAccuracy?: number;
13
+ readonly heading?: number;
14
+ readonly speed?: number;
15
+ }
16
+
17
+ export interface LocationErrorEvent {
18
+ readonly name: string;
19
+ readonly message: string;
20
+ }
21
+
22
+ export interface LocationPermission {
23
+ readonly inUse: Permission;
24
+ readonly background: Permission;
25
+ readonly precision: Precision;
26
+ }
27
+
28
+ export interface LocationUpdate {
29
+ readonly positions: readonly Position[];
30
+ }
31
+
32
+ export interface MonitoredRegion {
33
+ readonly id: string;
34
+ readonly latitude: number;
35
+ readonly longitude: number;
36
+ readonly radius: number;
37
+ readonly onEntry: boolean;
38
+ readonly onExit: boolean;
39
+ }
40
+
41
+ export type Permission = "granted" | "denied" | "prompt";
42
+
43
+ export interface PermissionRequest {
44
+ readonly background?: boolean;
45
+ readonly precision?: Precision;
46
+ }
47
+
48
+ export interface Position {
49
+ readonly coords: Coordinates;
50
+ readonly timestamp: number;
51
+ }
52
+
53
+ export type Precision = "precise" | "approximate";
54
+
55
+ export interface Region {
56
+ readonly id: string;
57
+ readonly latitude: number;
58
+ readonly longitude: number;
59
+ readonly radius: number;
60
+ readonly onEntry?: boolean;
61
+ readonly onExit?: boolean;
62
+ }
63
+
64
+ export interface RegionTransition {
65
+ readonly id: string;
66
+ readonly transition: Transition;
67
+ readonly timestamp: number;
68
+ }
69
+
70
+ export interface ServiceNotification {
71
+ readonly title: string;
72
+ readonly body: string;
73
+ readonly channel: string;
74
+ }
75
+
76
+ export interface SetupConfig {
77
+ readonly significantChanges?: boolean;
78
+ }
79
+
80
+ export type Transition = "enter" | "exit";
81
+
82
+ export interface UpdateOptions {
83
+ readonly accuracy?: Accuracy;
84
+ readonly distance?: number;
85
+ readonly interval?: number;
86
+ readonly foregroundService?: ServiceNotification;
87
+ }
88
+
89
+ /** The location plugin's native methods on Android. */
90
+ export interface Android {
91
+ /** Applies long-term location awareness, replacing the previous configuration. */
92
+ setup(config: SetupConfig): Promise<void>;
93
+ /** The provider's last known position, when it is no older than `maximumAge`. */
94
+ recentPosition(maximumAge: number): Promise<Position | null>;
95
+ /** A new fix, or null when Android finds none. Cancelling the call stops locating. */
96
+ currentPosition(): Promise<Position | null>;
97
+ /** Positions as the device moves. */
98
+ watchPosition(): JsonStream<Position>;
99
+ /** The app's in-use and background permission, and the precision it gets. */
100
+ permission(): Promise<LocationPermission>;
101
+ /** Asks for the permission `request` names where the user has not answered. */
102
+ requestPermission(request?: PermissionRequest | null): Promise<LocationPermission>;
103
+ /** Whether location services are on for the device. */
104
+ servicesEnabled(): Promise<boolean>;
105
+ /** Opens the app's page in the system settings. */
106
+ openSettings(): Promise<void>;
107
+ /** Starts continuous updates, replacing any running, until `stopUpdates`. */
108
+ startUpdates(options?: UpdateOptions | null): Promise<void>;
109
+ stopUpdates(): Promise<void>;
110
+ /** The options of the running updates, or null when none are running. */
111
+ getUpdates(): Promise<UpdateOptions | null>;
112
+ /** Monitors a circular region, replacing one with the same `id`. */
113
+ addRegion(region: Region): Promise<void>;
114
+ /** The monitored regions. */
115
+ getRegions(): Promise<MonitoredRegion[]>;
116
+ removeRegion(id: string): Promise<void>;
117
+ /** The updates the Worker's listeners receive. */
118
+ onLocationUpdate(): JsonStream<LocationUpdate>;
119
+ /** The transitions the Worker's listeners receive. */
120
+ onRegionTransition(): JsonStream<RegionTransition>;
121
+ /** The failures the Worker's listeners receive. */
122
+ onLocationError(): JsonStream<LocationErrorEvent>;
123
+ }
124
+
125
+ /**
126
+ * The location plugin's native methods. Off Android they reject with `NotSupportedError`.
127
+ * Aborting `signal` cancels their calls.
128
+ */
129
+ export function android(ctx: TokamakContext, signal?: AbortSignal): Android {
130
+ const endpoint = new NativeEndpoint(ctx, "location", ["android"], signal);
131
+ return {
132
+ setup: (config) => endpoint.call("setup", { config }),
133
+ recentPosition: (maximumAge) => endpoint.call("recentPosition", { maximumAge }),
134
+ currentPosition: () => endpoint.call("currentPosition"),
135
+ watchPosition: () => endpoint.stream("watchPosition", {}, "dropOldest"),
136
+ permission: () => endpoint.call("permission"),
137
+ requestPermission: (request) => endpoint.call("requestPermission", { request }),
138
+ servicesEnabled: () => endpoint.call("servicesEnabled"),
139
+ openSettings: () => endpoint.call("openSettings"),
140
+ startUpdates: (options) => endpoint.call("startUpdates", { options }),
141
+ stopUpdates: () => endpoint.call("stopUpdates"),
142
+ getUpdates: () => endpoint.call("getUpdates"),
143
+ addRegion: (region) => endpoint.call("addRegion", { region }),
144
+ getRegions: () => endpoint.call("getRegions"),
145
+ removeRegion: (id) => endpoint.call("removeRegion", { id }),
146
+ onLocationUpdate: () => endpoint.stream("onLocationUpdate", {}, "dropOldest"),
147
+ onRegionTransition: () => endpoint.stream("onRegionTransition"),
148
+ onLocationError: () => endpoint.stream("onLocationError"),
149
+ };
150
+ }
151
+
152
+ declare global {
153
+ // eslint-disable-next-line @typescript-eslint/no-namespace -- events are declared by merging into Tokamak.Events.
154
+ namespace Tokamak {
155
+ interface Events {
156
+ "location.android.update": LocationUpdate;
157
+ "location.android.region": RegionTransition;
158
+ "location.android.error": LocationErrorEvent;
159
+ }
160
+ }
161
+ }
162
+
163
+ /** Receives continuous updates and significant changes, including those that start the app. */
164
+ export const onUpdate = defineEvent("location.android.update", { platforms: ["android"] });
165
+
166
+ /** Receives each region entry or exit, including one that starts the app. */
167
+ export const onRegion = defineEvent("location.android.region", { platforms: ["android"] });
168
+
169
+ /** Receives failures of background services, such as a region Android cannot monitor. */
170
+ export const onError = defineEvent("location.android.error", { platforms: ["android"] });
@@ -0,0 +1,183 @@
1
+ // Generated by `tok plugin generate apple`. Do not edit.
2
+ import { NativeEndpoint, type JsonStream, type TokamakContext } from "@tokamakdev/plugin";
3
+ import { defineEvent } from "@tokamakdev/plugin/events";
4
+
5
+ export type Activity = "default" | "automotiveNavigation" | "otherNavigation" | "fitness" | "airborne" | "maritime";
6
+
7
+ export interface Coordinates {
8
+ readonly latitude: number;
9
+ readonly longitude: number;
10
+ readonly accuracy: number;
11
+ readonly altitude?: number;
12
+ readonly altitudeAccuracy?: number;
13
+ readonly heading?: number;
14
+ readonly speed?: number;
15
+ }
16
+
17
+ export interface LocationErrorEvent {
18
+ readonly name: string;
19
+ readonly message: string;
20
+ readonly id?: string;
21
+ }
22
+
23
+ export interface LocationPermission {
24
+ readonly inUse: Permission;
25
+ readonly background: Permission;
26
+ readonly precision: Precision;
27
+ }
28
+
29
+ export interface LocationUpdate {
30
+ readonly positions: readonly Position[];
31
+ readonly stationary?: boolean;
32
+ }
33
+
34
+ export interface MonitoredRegion {
35
+ readonly id: string;
36
+ readonly latitude: number;
37
+ readonly longitude: number;
38
+ readonly radius: number;
39
+ readonly onEntry: boolean;
40
+ readonly onExit: boolean;
41
+ readonly state: RegionState;
42
+ }
43
+
44
+ export type Permission = "granted" | "denied" | "prompt";
45
+
46
+ export interface PermissionRequest {
47
+ readonly background?: boolean;
48
+ readonly precision?: Precision;
49
+ readonly purpose?: string;
50
+ }
51
+
52
+ export interface Position {
53
+ readonly coords: Coordinates;
54
+ readonly timestamp: number;
55
+ }
56
+
57
+ export type Precision = "precise" | "approximate";
58
+
59
+ export interface Region {
60
+ readonly id: string;
61
+ readonly latitude: number;
62
+ readonly longitude: number;
63
+ readonly radius: number;
64
+ readonly onEntry?: boolean;
65
+ readonly onExit?: boolean;
66
+ }
67
+
68
+ export type RegionState = "inside" | "outside" | "unknown";
69
+
70
+ export interface RegionTransition {
71
+ readonly id: string;
72
+ readonly transition: Transition;
73
+ readonly timestamp: number;
74
+ }
75
+
76
+ export interface SetupConfig {
77
+ readonly significantChanges?: boolean;
78
+ readonly visits?: boolean;
79
+ }
80
+
81
+ export type Transition = "enter" | "exit";
82
+
83
+ export interface UpdateOptions {
84
+ readonly activity?: Activity;
85
+ }
86
+
87
+ export interface Visit {
88
+ readonly latitude: number;
89
+ readonly longitude: number;
90
+ readonly accuracy: number;
91
+ readonly arrival?: number;
92
+ readonly departure?: number;
93
+ }
94
+
95
+ /** The location plugin's native methods on iOS and macOS. */
96
+ export interface Apple {
97
+ /** Applies long-term location awareness, replacing the previous configuration. */
98
+ setup(config: SetupConfig): Promise<void>;
99
+ /** Core Location's latest position, when it is no older than `maximumAge`. */
100
+ recentPosition(maximumAge: number): Promise<Position | null>;
101
+ /** A new fix. Cancelling the call stops locating. */
102
+ currentPosition(): Promise<Position>;
103
+ /** Positions as the device moves. */
104
+ watchPosition(): JsonStream<Position>;
105
+ /** The app's in-use and background permission, and the precision it gets. */
106
+ permission(): Promise<LocationPermission>;
107
+ /** Asks for the permission `request` names where the user has not answered. */
108
+ requestPermission(request?: PermissionRequest | null): Promise<LocationPermission>;
109
+ /** Whether location services are on for the device. */
110
+ servicesEnabled(): Promise<boolean>;
111
+ /** Opens the app's page in the system settings. */
112
+ openSettings(): Promise<void>;
113
+ /** Starts continuous updates, replacing any running, until `stopUpdates`. */
114
+ startUpdates(options?: UpdateOptions | null): Promise<void>;
115
+ stopUpdates(): Promise<void>;
116
+ /** The options of the running updates, or null when none are running. */
117
+ getUpdates(): Promise<UpdateOptions | null>;
118
+ /** Monitors a circular region, replacing one with the same `id`. */
119
+ addRegion(region: Region): Promise<void>;
120
+ /** The monitored regions. */
121
+ getRegions(): Promise<MonitoredRegion[]>;
122
+ removeRegion(id: string): Promise<void>;
123
+ /** The updates the Worker's listeners receive. */
124
+ onLocationUpdate(): JsonStream<LocationUpdate>;
125
+ /** The transitions the Worker's listeners receive. */
126
+ onRegionTransition(): JsonStream<RegionTransition>;
127
+ /** The visits the Worker's listeners receive. */
128
+ onVisit(): JsonStream<Visit>;
129
+ /** The failures the Worker's listeners receive. */
130
+ onLocationError(): JsonStream<LocationErrorEvent>;
131
+ }
132
+
133
+ /**
134
+ * The location plugin's native methods. Off iOS and macOS they reject with `NotSupportedError`.
135
+ * Aborting `signal` cancels their calls.
136
+ */
137
+ export function apple(ctx: TokamakContext, signal?: AbortSignal): Apple {
138
+ const endpoint = new NativeEndpoint(ctx, "location", ["ios", "macos"], signal);
139
+ return {
140
+ setup: (config) => endpoint.call("setup", { config }),
141
+ recentPosition: (maximumAge) => endpoint.call("recentPosition", { maximumAge }),
142
+ currentPosition: () => endpoint.call("currentPosition"),
143
+ watchPosition: () => endpoint.stream("watchPosition", {}, "dropOldest"),
144
+ permission: () => endpoint.call("permission"),
145
+ requestPermission: (request) => endpoint.call("requestPermission", { request }),
146
+ servicesEnabled: () => endpoint.call("servicesEnabled"),
147
+ openSettings: () => endpoint.call("openSettings"),
148
+ startUpdates: (options) => endpoint.call("startUpdates", { options }),
149
+ stopUpdates: () => endpoint.call("stopUpdates"),
150
+ getUpdates: () => endpoint.call("getUpdates"),
151
+ addRegion: (region) => endpoint.call("addRegion", { region }),
152
+ getRegions: () => endpoint.call("getRegions"),
153
+ removeRegion: (id) => endpoint.call("removeRegion", { id }),
154
+ onLocationUpdate: () => endpoint.stream("onLocationUpdate", {}, "dropOldest"),
155
+ onRegionTransition: () => endpoint.stream("onRegionTransition"),
156
+ onVisit: () => endpoint.stream("onVisit"),
157
+ onLocationError: () => endpoint.stream("onLocationError"),
158
+ };
159
+ }
160
+
161
+ declare global {
162
+ // eslint-disable-next-line @typescript-eslint/no-namespace -- events are declared by merging into Tokamak.Events.
163
+ namespace Tokamak {
164
+ interface Events {
165
+ "location.apple.update": LocationUpdate;
166
+ "location.apple.region": RegionTransition;
167
+ "location.apple.visit": Visit;
168
+ "location.apple.error": LocationErrorEvent;
169
+ }
170
+ }
171
+ }
172
+
173
+ /** Receives continuous updates and significant changes, including those that launch the app. */
174
+ export const onUpdate = defineEvent("location.apple.update", { platforms: ["ios", "macos"] });
175
+
176
+ /** Receives each region entry or exit, including one that launches the app. */
177
+ export const onRegion = defineEvent("location.apple.region", { platforms: ["ios", "macos"] });
178
+
179
+ /** Receives each arrival at or departure from a place, including one that launches the app. */
180
+ export const onVisit = defineEvent("location.apple.visit", { platforms: ["ios", "macos"] });
181
+
182
+ /** Receives failures of background services, such as a region Core Location cannot monitor. */
183
+ export const onError = defineEvent("location.apple.error", { platforms: ["ios", "macos"] });
package/src/events.ts ADDED
@@ -0,0 +1,31 @@
1
+ import type { Listener } from "@tokamakdev/plugin/events";
2
+
3
+ import * as android from "./android.generated.js";
4
+ import * as apple from "./apple.generated.js";
5
+ import type { LocationErrorEvent, LocationUpdate, RegionTransition, Visit } from "./types.js";
6
+
7
+ /**
8
+ * Receives the positions of continuous updates and significant changes in native builds: on
9
+ * screen, off screen, or after the system launches the app for them.
10
+ */
11
+ export function onLocationUpdate(listener: Listener<LocationUpdate>): void {
12
+ apple.onUpdate(listener);
13
+ android.onUpdate(listener);
14
+ }
15
+
16
+ /** Receives each region entry or exit, including one that launches the app. */
17
+ export function onRegionTransition(listener: Listener<RegionTransition>): void {
18
+ apple.onRegion(listener);
19
+ android.onRegion(listener);
20
+ }
21
+
22
+ /** Apple only: receives each arrival at or departure from a place, including one that launches the app. */
23
+ export function onVisit(listener: Listener<Visit>): void {
24
+ apple.onVisit(listener);
25
+ }
26
+
27
+ /** Receives failures of background location services, such as a region the system cannot monitor. */
28
+ export function onLocationError(listener: Listener<LocationErrorEvent>): void {
29
+ apple.onError(listener);
30
+ android.onError(listener);
31
+ }