craft-native 0.0.151 → 0.0.153
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.
- package/dist/api/mobile.d.ts +113 -0
- package/dist/cli.js +1 -1
- package/dist/index.cjs +2 -1
- package/dist/index.js +2 -1
- package/dist/ios/src/index.d.ts +9 -0
- package/dist/ios/src/index.js +6 -1
- package/dist/ios/templates/CraftApp.swift +56 -1
- package/dist/ios/templates/CraftHeartRate.swift +699 -0
- package/dist/ios/templates/CraftNativeActions.swift +106 -2
- package/dist/ios/templates/CraftNativeFiles.swift +343 -0
- package/dist/ios/templates/CraftNativeScreen.swift +11 -5
- package/dist/mobile.js +163 -0
- package/package.json +1 -1
package/dist/api/mobile.d.ts
CHANGED
|
@@ -825,6 +825,118 @@ export declare const music: {
|
|
|
825
825
|
*/
|
|
826
826
|
onChange(callback: (nowPlaying: NowPlaying) => void): () => void;
|
|
827
827
|
};
|
|
828
|
+
export type HeartRateAuthorization = 'authorized' | 'denied' | 'restricted' | 'notDetermined' | 'unavailable';
|
|
829
|
+
export type BluetoothState = 'poweredOn' | 'poweredOff' | 'unauthorized' | 'unsupported' | 'resetting' | 'unknown';
|
|
830
|
+
export type HeartRateConnection = 'disconnected' | 'connecting' | 'connected' | 'reconnecting';
|
|
831
|
+
export interface HeartRateDevice {
|
|
832
|
+
id: string;
|
|
833
|
+
name: string;
|
|
834
|
+
/** Signal strength, dBm; 0 for a strap the phone is already connected to. */
|
|
835
|
+
rssi: number | null;
|
|
836
|
+
}
|
|
837
|
+
export interface HeartRateRecordingState {
|
|
838
|
+
id: string | null;
|
|
839
|
+
active: boolean;
|
|
840
|
+
paused: boolean;
|
|
841
|
+
/** ms since the epoch. */
|
|
842
|
+
startedAt: number | null;
|
|
843
|
+
sampleCount: number | null;
|
|
844
|
+
}
|
|
845
|
+
export interface HeartRateState {
|
|
846
|
+
/** False off a phone, in a build without `enableHeartRate`, or on a phone without Bluetooth. */
|
|
847
|
+
available: boolean;
|
|
848
|
+
bluetooth: BluetoothState;
|
|
849
|
+
authorization: HeartRateAuthorization;
|
|
850
|
+
scanning: boolean;
|
|
851
|
+
connection: HeartRateConnection;
|
|
852
|
+
/** The strap connected, or being connected to. */
|
|
853
|
+
device: HeartRateDevice | null;
|
|
854
|
+
/** The strap `reconnect()` asks for. */
|
|
855
|
+
lastDevice: HeartRateDevice | null;
|
|
856
|
+
/** The latest reading while it is fresh (ten seconds), else null. */
|
|
857
|
+
bpm: number | null;
|
|
858
|
+
lastReadingAt: number | null;
|
|
859
|
+
recording: HeartRateRecordingState;
|
|
860
|
+
}
|
|
861
|
+
export interface HeartRateReading {
|
|
862
|
+
bpm: number;
|
|
863
|
+
/** Milliseconds between beats, oldest first; empty when the strap sends none. */
|
|
864
|
+
rrIntervals: number[];
|
|
865
|
+
/** Whether the strap touches skin; null when it cannot tell. */
|
|
866
|
+
contact: boolean | null;
|
|
867
|
+
energyExpended: number | null;
|
|
868
|
+
/** ms since the epoch. */
|
|
869
|
+
timestamp: number;
|
|
870
|
+
deviceId: string | null;
|
|
871
|
+
}
|
|
872
|
+
/** A reading as a recording keeps it. */
|
|
873
|
+
export interface HeartRateSample {
|
|
874
|
+
/** ms since the epoch. */
|
|
875
|
+
timestamp: number;
|
|
876
|
+
bpm: number;
|
|
877
|
+
/** Milliseconds between beats, when the strap sent them. */
|
|
878
|
+
rr?: number[];
|
|
879
|
+
}
|
|
880
|
+
export type HeartRateMethod = 'getState' | 'requestAuthorization' | 'scan' | 'stopScan' | 'getDevices' | 'connect' | 'reconnect' | 'disconnect' | 'startRecording' | 'pauseRecording' | 'resumeRecording' | 'stopRecording' | 'getRecordingState' | 'readRecording';
|
|
881
|
+
export declare function normalizeHeartRateDevice(value: unknown): HeartRateDevice | null;
|
|
882
|
+
/** A state from native, made safe to render whatever an older or newer build sent. */
|
|
883
|
+
export declare function normalizeHeartRateState(value: unknown): HeartRateState;
|
|
884
|
+
/** A reading event's detail, or null for one that is not a usable reading. */
|
|
885
|
+
export declare function normalizeHeartRateReading(value: unknown): HeartRateReading | null;
|
|
886
|
+
/** A recording's samples, oldest first, one per timestamp, implausible ones left out. */
|
|
887
|
+
export declare function normalizeHeartRateSamples(value: unknown): HeartRateSample[];
|
|
888
|
+
export declare const heartRate: {
|
|
889
|
+
/** Whether this app can read a heart-rate strap. */
|
|
890
|
+
isAvailable(): boolean;
|
|
891
|
+
/** Whether this app build answers `method`; an older build may lack one. */
|
|
892
|
+
supports(method: HeartRateMethod): boolean;
|
|
893
|
+
/** Bluetooth, the connection, the strap and its latest reading, without asking anything. */
|
|
894
|
+
getState: () => Promise<HeartRateState>;
|
|
895
|
+
/**
|
|
896
|
+
* Ask for Bluetooth: iOS's prompt the first time, its remembered answer
|
|
897
|
+
* after that. Scanning and connecting ask too; call this from the person's
|
|
898
|
+
* tap to ask first.
|
|
899
|
+
*/
|
|
900
|
+
requestAuthorization: () => Promise<HeartRateAuthorization>;
|
|
901
|
+
/** Look for straps for `timeout` seconds (15 by default); `onDevice` hears each one. */
|
|
902
|
+
scan: (options?: {
|
|
903
|
+
timeout?: number;
|
|
904
|
+
}) => Promise<HeartRateState>;
|
|
905
|
+
stopScan: () => Promise<HeartRateState>;
|
|
906
|
+
/** What the last scan found, strongest signal first. */
|
|
907
|
+
getDevices: () => Promise<HeartRateDevice[]>;
|
|
908
|
+
/**
|
|
909
|
+
* Connect to a strap from `scan` and subscribe to its readings; it is
|
|
910
|
+
* remembered for `reconnect()`. Rejects with code TIMEOUT when it does not
|
|
911
|
+
* answer within 15 s, while still asking for it: a strap wakes up when worn.
|
|
912
|
+
*/
|
|
913
|
+
connect: (id: string) => Promise<HeartRateState>;
|
|
914
|
+
/** Ask for the remembered strap again, without waiting for it. */
|
|
915
|
+
reconnect: () => Promise<HeartRateState>;
|
|
916
|
+
/** Stop asking for the strap; `forget` also drops it from memory. */
|
|
917
|
+
disconnect: (options?: {
|
|
918
|
+
forget?: boolean;
|
|
919
|
+
}) => Promise<HeartRateState>;
|
|
920
|
+
/** Keep every reading natively from now on: screen locked, page suspended or app closed. */
|
|
921
|
+
startRecording: () => Promise<HeartRateRecordingState>;
|
|
922
|
+
pauseRecording: () => Promise<HeartRateRecordingState>;
|
|
923
|
+
resumeRecording: () => Promise<HeartRateRecordingState>;
|
|
924
|
+
/** Ends the recording and hands back every reading it kept. */
|
|
925
|
+
stopRecording: () => Promise<HeartRateRecordingState & {
|
|
926
|
+
samples: HeartRateSample[];
|
|
927
|
+
}>;
|
|
928
|
+
getRecordingState: () => Promise<HeartRateRecordingState>;
|
|
929
|
+
/** The recording so far; only what came after `since` (ms) when given. */
|
|
930
|
+
readRecording: (options?: {
|
|
931
|
+
since?: number;
|
|
932
|
+
}) => Promise<HeartRateSample[]>;
|
|
933
|
+
/** Every reading from the connected strap, about once a second. */
|
|
934
|
+
onReading: (callback: (reading: HeartRateReading) => void) => (() => void);
|
|
935
|
+
/** Each strap a scan finds. */
|
|
936
|
+
onDevice: (callback: (device: HeartRateDevice) => void) => (() => void);
|
|
937
|
+
/** Bluetooth, scanning or the connection changed. */
|
|
938
|
+
onState: (callback: (state: HeartRateState) => void) => (() => void);
|
|
939
|
+
};
|
|
828
940
|
export declare const deepLinks: {
|
|
829
941
|
getInitialURL(): Promise<string | null>;
|
|
830
942
|
/**
|
|
@@ -1308,6 +1420,7 @@ declare const mobile: {
|
|
|
1308
1420
|
brightness: typeof brightness;
|
|
1309
1421
|
speech: typeof speech;
|
|
1310
1422
|
music: typeof music;
|
|
1423
|
+
heartRate: typeof heartRate;
|
|
1311
1424
|
deepLinks: typeof deepLinks;
|
|
1312
1425
|
network: typeof network;
|
|
1313
1426
|
appReview: typeof appReview;
|
package/dist/cli.js
CHANGED
|
@@ -3303,7 +3303,7 @@ function pinCraftNativeDependency(packagePath, craftVersion) {
|
|
|
3303
3303
|
`);
|
|
3304
3304
|
}
|
|
3305
3305
|
// package.json
|
|
3306
|
-
var version = "0.0.
|
|
3306
|
+
var version = "0.0.153";
|
|
3307
3307
|
|
|
3308
3308
|
// bin/cli.ts
|
|
3309
3309
|
var spawnedFrom = process6.env[CRAFT_CLI_SPAWN_MARKER];
|
package/dist/index.cjs
CHANGED
|
@@ -310,7 +310,7 @@ function craftBinaryNotFoundMessage(triedPath) {
|
|
|
310
310
|
`);
|
|
311
311
|
}
|
|
312
312
|
// package.json
|
|
313
|
-
var version = "0.0.
|
|
313
|
+
var version = "0.0.153";
|
|
314
314
|
|
|
315
315
|
// src/sdk-version.ts
|
|
316
316
|
var SDK_VERSION = version;
|
|
@@ -5325,6 +5325,7 @@ var notifications = {
|
|
|
5325
5325
|
};
|
|
5326
5326
|
var speakingOnTheWeb = new Set;
|
|
5327
5327
|
var musicWatch = { listeners: new Set, stop: null };
|
|
5328
|
+
var heartRateWatch = { readings: new Set, devices: new Set, states: new Set, stop: null };
|
|
5328
5329
|
function onCraftEvent(eventName, callback) {
|
|
5329
5330
|
if (typeof globalThis === "undefined")
|
|
5330
5331
|
return () => {};
|
package/dist/index.js
CHANGED
|
@@ -58,7 +58,7 @@ function craftBinaryNotFoundMessage(triedPath) {
|
|
|
58
58
|
`);
|
|
59
59
|
}
|
|
60
60
|
// package.json
|
|
61
|
-
var version = "0.0.
|
|
61
|
+
var version = "0.0.153";
|
|
62
62
|
|
|
63
63
|
// src/sdk-version.ts
|
|
64
64
|
var SDK_VERSION = version;
|
|
@@ -5085,6 +5085,7 @@ var notifications = {
|
|
|
5085
5085
|
};
|
|
5086
5086
|
var speakingOnTheWeb = new Set;
|
|
5087
5087
|
var musicWatch = { listeners: new Set, stop: null };
|
|
5088
|
+
var heartRateWatch = { readings: new Set, devices: new Set, states: new Set, stop: null };
|
|
5088
5089
|
function onCraftEvent(eventName, callback) {
|
|
5089
5090
|
if (typeof globalThis === "undefined")
|
|
5090
5091
|
return () => {};
|
package/dist/ios/src/index.d.ts
CHANGED
|
@@ -68,6 +68,15 @@ export interface CraftConfig {
|
|
|
68
68
|
* `craft.music.requestAuthorization()`, never at launch.
|
|
69
69
|
*/
|
|
70
70
|
enableAppleMusic?: boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Read a Bluetooth heart-rate strap, or a watch broadcasting its heart rate
|
|
73
|
+
* (the standard Heart Rate Service), through `craft.heartRate`: scan,
|
|
74
|
+
* connect, live readings with RR intervals, and a recording kept natively.
|
|
75
|
+
* Adds the `bluetooth-central` background mode, so readings keep coming
|
|
76
|
+
* during a workout with the screen locked. iOS asks for Bluetooth the first
|
|
77
|
+
* time the page scans or connects.
|
|
78
|
+
*/
|
|
79
|
+
enableHeartRate?: boolean;
|
|
71
80
|
/**
|
|
72
81
|
* Purpose strings in the app's own words, by Info.plist key
|
|
73
82
|
* (`{ NSAppleMusicUsageDescription: 'Shows the song playing during a workout.' }`).
|
package/dist/ios/src/index.js
CHANGED
|
@@ -170,6 +170,7 @@ var DEFAULT_CONFIG = {
|
|
|
170
170
|
enableAR: false,
|
|
171
171
|
enableMLKit: false,
|
|
172
172
|
enableAppleMusic: false,
|
|
173
|
+
enableHeartRate: false,
|
|
173
174
|
iosVersion: "16.0",
|
|
174
175
|
watchosVersion: "9.0",
|
|
175
176
|
teamId: "",
|
|
@@ -226,7 +227,7 @@ function renderUsageDescriptions(config) {
|
|
|
226
227
|
[config.enableBackgroundLocation, "NSLocationAlwaysAndWhenInUseUsageDescription", `${config.appName} continues recording your route when the screen is locked or the app is in the background.`],
|
|
227
228
|
[config.enableContacts, "NSContactsUsageDescription", `${config.appName} accesses contacts only when you choose a contact feature.`],
|
|
228
229
|
[config.enableCalendar, "NSCalendarsUsageDescription", `${config.appName} accesses your calendar only when you choose a calendar feature.`],
|
|
229
|
-
[config.enableBluetooth, "NSBluetoothAlwaysUsageDescription", `${config.appName} uses Bluetooth to connect to nearby devices.`],
|
|
230
|
+
[config.enableBluetooth || config.enableHeartRate, "NSBluetoothAlwaysUsageDescription", config.enableHeartRate ? `${config.appName} connects to your heart-rate monitor over Bluetooth to show and record your heart rate during a workout.` : `${config.appName} uses Bluetooth to connect to nearby devices.`],
|
|
230
231
|
[config.enableMotionSensors, "NSMotionUsageDescription", `${config.appName} uses motion data for activity features.`],
|
|
231
232
|
[config.enableNFC, "NFCReaderUsageDescription", `${config.appName} reads NFC tags when you start a scan.`],
|
|
232
233
|
[config.enableLocalNetwork, "NSLocalNetworkUsageDescription", `${config.appName} connects to your own devices on this network.`],
|
|
@@ -309,6 +310,8 @@ function renderBackgroundModes(config) {
|
|
|
309
310
|
modes.add("processing");
|
|
310
311
|
if (config.enablePushNotifications)
|
|
311
312
|
modes.add("remote-notification");
|
|
313
|
+
if (config.enableHeartRate)
|
|
314
|
+
modes.add("bluetooth-central");
|
|
312
315
|
if (!modes.size)
|
|
313
316
|
return "";
|
|
314
317
|
return ` <key>UIBackgroundModes</key>
|
|
@@ -787,9 +790,11 @@ async function init(options) {
|
|
|
787
790
|
cpSync(join(TEMPLATES_DIR, "CraftNativeActions.swift"), join(output, "Sources", "CraftNativeActions.swift"));
|
|
788
791
|
cpSync(join(TEMPLATES_DIR, "CraftHealth.swift"), join(output, "Sources", "CraftHealth.swift"));
|
|
789
792
|
cpSync(join(TEMPLATES_DIR, "CraftMusic.swift"), join(output, "Sources", "CraftMusic.swift"));
|
|
793
|
+
cpSync(join(TEMPLATES_DIR, "CraftHeartRate.swift"), join(output, "Sources", "CraftHeartRate.swift"));
|
|
790
794
|
cpSync(join(TEMPLATES_DIR, "CraftNativeMutation.swift"), join(output, "Sources", "CraftNativeMutation.swift"));
|
|
791
795
|
cpSync(join(TEMPLATES_DIR, "CraftNativeUI.swift"), join(output, "Sources", "CraftNativeUI.swift"));
|
|
792
796
|
cpSync(join(TEMPLATES_DIR, "CraftNativeServices.swift"), join(output, "Sources", "CraftNativeServices.swift"));
|
|
797
|
+
cpSync(join(TEMPLATES_DIR, "CraftNativeFiles.swift"), join(output, "Sources", "CraftNativeFiles.swift"));
|
|
793
798
|
cpSync(join(TEMPLATES_DIR, "CraftWebAudio.swift"), join(output, "Sources", "CraftWebAudio.swift"));
|
|
794
799
|
cpSync(join(TEMPLATES_DIR, "CraftHybrid.swift"), join(output, "Sources", "CraftHybrid.swift"));
|
|
795
800
|
const infoPlistTemplate = readFileSync(join(TEMPLATES_DIR, "Info.plist.template"), "utf-8");
|
|
@@ -1119,6 +1119,9 @@ struct CraftConfig: Codable {
|
|
|
1119
1119
|
var enableMLKit: Bool = false
|
|
1120
1120
|
/// The Music app's song and transport through `craft.music` (CraftMusic.swift).
|
|
1121
1121
|
var enableAppleMusic: Bool = false
|
|
1122
|
+
/// A Bluetooth heart-rate strap (or a watch broadcasting its heart rate)
|
|
1123
|
+
/// through `craft.heartRate` (CraftHeartRate.swift).
|
|
1124
|
+
var enableHeartRate: Bool = false
|
|
1122
1125
|
var devServerURL: String? = nil
|
|
1123
1126
|
var trustedOrigins: [String] = []
|
|
1124
1127
|
/// The longest the launch splash stays up when the page neither says it is
|
|
@@ -1641,6 +1644,16 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
1641
1644
|
if config.enableMotionSensors {
|
|
1642
1645
|
motionManager = CMMotionManager()
|
|
1643
1646
|
}
|
|
1647
|
+
if config.enableHeartRate {
|
|
1648
|
+
// The strap's readings and connection changes reach the page,
|
|
1649
|
+
// and through sendToWeb every native screen too. False once
|
|
1650
|
+
// this coordinator is gone: native screens still hear them.
|
|
1651
|
+
CraftHeartRate.shared.pageSink = { [weak self] detail in
|
|
1652
|
+
guard let self else { return false }
|
|
1653
|
+
self.sendToWeb("craftHeartRate", data: detail)
|
|
1654
|
+
return true
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1644
1657
|
if config.enableWatchApp {
|
|
1645
1658
|
setupWatchConnectivity()
|
|
1646
1659
|
}
|
|
@@ -2213,6 +2226,22 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
2213
2226
|
} else {
|
|
2214
2227
|
rejectCallback(callbackId, error: "Apple Music is disabled", code: "CAPABILITY_DISABLED")
|
|
2215
2228
|
}
|
|
2229
|
+
// MARK: - Heart rate (CraftHeartRate.swift)
|
|
2230
|
+
// Gated on enableHeartRate. One action for every method, as a
|
|
2231
|
+
// native screen's HeartRate module calls them; asking for
|
|
2232
|
+
// Bluetooth has its own, which waits on the person.
|
|
2233
|
+
case "heartRate", "heartRateRequestAuthorization":
|
|
2234
|
+
if !config.enableHeartRate {
|
|
2235
|
+
rejectCallback(callbackId, error: "Heart rate is disabled", code: "CAPABILITY_DISABLED")
|
|
2236
|
+
} else {
|
|
2237
|
+
let method = action == "heartRate" ? body["method"] as? String ?? "" : "requestAuthorization"
|
|
2238
|
+
CraftHeartRate.shared.perform(method: method, args: [body["options"] as? [String: Any] ?? [:]]) { [weak self] result in
|
|
2239
|
+
switch result {
|
|
2240
|
+
case .success(let value): self?.resolveCallback(callbackId, result: value)
|
|
2241
|
+
case .failure(let failure): self?.rejectCallback(callbackId, error: failure.message, code: failure.code)
|
|
2242
|
+
}
|
|
2243
|
+
}
|
|
2244
|
+
}
|
|
2216
2245
|
// MARK: - Orientation Lock
|
|
2217
2246
|
case "lockOrientation":
|
|
2218
2247
|
if config.enableOrientationLock {
|
|
@@ -3198,6 +3227,7 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
3198
3227
|
flashlight: true,
|
|
3199
3228
|
brightness: true,
|
|
3200
3229
|
music: \(config.enableAppleMusic),
|
|
3230
|
+
heartRate: \(config.enableHeartRate),
|
|
3201
3231
|
speech: true,
|
|
3202
3232
|
network: true,
|
|
3203
3233
|
deviceInfo: true,
|
|
@@ -4737,6 +4767,7 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
4737
4767
|
var personFacing = {
|
|
4738
4768
|
requestHealthAuthorization: true,
|
|
4739
4769
|
musicRequestAuthorization: true,
|
|
4770
|
+
heartRateRequestAuthorization: true,
|
|
4740
4771
|
requestPermission: true,
|
|
4741
4772
|
signInWithApple: true,
|
|
4742
4773
|
dialogAlert: true,
|
|
@@ -4951,6 +4982,30 @@ struct CraftWebView: UIViewRepresentable {
|
|
|
4951
4982
|
},
|
|
4952
4983
|
observe: function(enabled) { return craft._invoke('musicObserve', {enabled: enabled !== false}); }
|
|
4953
4984
|
};
|
|
4985
|
+
// A Bluetooth heart-rate strap, or a watch broadcasting its
|
|
4986
|
+
// heart rate. Readings, devices found by scan() and connection
|
|
4987
|
+
// changes arrive as craftHeartRate events ({type: 'reading' |
|
|
4988
|
+
// 'device' | 'state'}); startRecording() keeps every reading
|
|
4989
|
+
// natively, screen locked or app closed, until stopRecording().
|
|
4990
|
+
(function() {
|
|
4991
|
+
function heartRate(method, options) { return craft._invoke('heartRate', {method: method, options: options || {}}); }
|
|
4992
|
+
craft.heartRate = {
|
|
4993
|
+
getState: function() { return heartRate('getState'); },
|
|
4994
|
+
requestAuthorization: function() { return craft._invoke('heartRateRequestAuthorization'); },
|
|
4995
|
+
scan: function(options) { return heartRate('scan', {timeout: options && typeof options.timeout === 'number' ? options.timeout : null}); },
|
|
4996
|
+
stopScan: function() { return heartRate('stopScan'); },
|
|
4997
|
+
getDevices: function() { return heartRate('getDevices'); },
|
|
4998
|
+
connect: function(id) { return heartRate('connect', {id: typeof id === 'string' ? id : id && id.id}); },
|
|
4999
|
+
reconnect: function() { return heartRate('reconnect'); },
|
|
5000
|
+
disconnect: function(options) { return heartRate('disconnect', {forget: !!(options && options.forget)}); },
|
|
5001
|
+
startRecording: function() { return heartRate('startRecording'); },
|
|
5002
|
+
pauseRecording: function() { return heartRate('pauseRecording'); },
|
|
5003
|
+
resumeRecording: function() { return heartRate('resumeRecording'); },
|
|
5004
|
+
stopRecording: function() { return heartRate('stopRecording'); },
|
|
5005
|
+
getRecordingState: function() { return heartRate('getRecordingState'); },
|
|
5006
|
+
readRecording: function(options) { return heartRate('readRecording', {since: options && typeof options.since === 'number' ? options.since : null}); }
|
|
5007
|
+
};
|
|
5008
|
+
})();
|
|
4954
5009
|
craft.liveActivity = {
|
|
4955
5010
|
start: function(options) { return craft._invoke('startLiveActivity', options || {}); },
|
|
4956
5011
|
update: function(idOrState, state) {
|
|
@@ -8450,7 +8505,7 @@ final class CraftZigRuntime: NSObject {
|
|
|
8450
8505
|
enum CraftWebServices {
|
|
8451
8506
|
private static var waiting: [String: (Result<Any, CraftNativeActionError>) -> Void] = [:]
|
|
8452
8507
|
/// Coordinator events a native screen hears too.
|
|
8453
|
-
static let forwardedEvents: Set<String> = ["craftLocationUpdate", "craftLocationError", "craftMusicChange"]
|
|
8508
|
+
static let forwardedEvents: Set<String> = ["craftLocationUpdate", "craftLocationError", "craftMusicChange", "craftHeartRate"]
|
|
8454
8509
|
static let event = Notification.Name("craftWebServicesEvent")
|
|
8455
8510
|
|
|
8456
8511
|
static var isAvailable: Bool { CraftSwiftShim.coordinator != nil }
|