@craft-native/ios 0.0.101 → 0.0.104

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/README.md CHANGED
@@ -143,6 +143,34 @@ Edit `craft.config.json` in your iOS project:
143
143
  }
144
144
  ```
145
145
 
146
+ The app name is shown as written (`"HQ.training"` is fine); the Xcode
147
+ project, target and Swift types use a PascalCase identifier made from it
148
+ (`HQTraining`). A key left out of the file keeps its default.
149
+
150
+ Appearance and navigation:
151
+
152
+ ```json
153
+ {
154
+ "appearance": "system",
155
+ "backgroundColor": "#f8fafc",
156
+ "backgroundColorDark": "#020617",
157
+ "swipeNavigation": true
158
+ }
159
+ ```
160
+
161
+ - `appearance`: `"system"` follows the phone's Light/Dark setting (and the
162
+ page's `prefers-color-scheme`), with a status bar that reads on either;
163
+ `"light"` and `"dark"` pin one. Without it, `darkMode` decides as before.
164
+ - `backgroundColorDark`: the launch screen and webview background in Dark Mode.
165
+ - `swipeNavigation`: an edge swipe goes back and forward through the page's
166
+ history, pushState entries included.
167
+
168
+ Debug builds let Safari's Develop menu inspect the webview, and relay the
169
+ page's console, uncaught errors, rejected promises and failed script loads to
170
+ the device log as `[craft page] …`, so a page that only breaks inside the app
171
+ can be read with `xcrun simctl spawn booted log show --predicate 'process ==
172
+ "YourApp"'`. Release builds do neither.
173
+
146
174
  When push notifications are enabled, generated Debug builds use the APNs
147
175
  development environment and Release builds use the production environment.
148
176
  The generated entitlements select the correct value from the active Xcode
@@ -240,6 +268,15 @@ console.log(nfcData); // Tag content
240
268
  await window.craft.requestHealthAuthorization(['stepCount', 'heartRate']);
241
269
  const steps = await window.craft.getHealthData('stepCount', startDate, endDate);
242
270
 
271
+ // Through craft-native/mobile's `health` service:
272
+ import { health } from 'craft-native/mobile'
273
+ // Read only: the sheet asks for access, not "access and update".
274
+ await health.requestAuthorization(['workouts', 'heartRate', 'sleep'], { write: false })
275
+ // Apple Health workouts (the watch's and every app's), newest first.
276
+ const workouts = await health.getWorkouts({ startDate: Date.now() - 30 * 86400000 })
277
+ // One value per local day: sums, averages, the latest weight, hours asleep.
278
+ const hrv = await health.getDailyStatistics('heartRateVariability', { startDate: Date.now() - 30 * 86400000 })
279
+
243
280
  // Screen Capture
244
281
  const screenshotBase64 = await window.craft.takeScreenshot();
245
282
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@craft-native/ios",
3
- "version": "0.0.101",
3
+ "version": "0.0.104",
4
4
  "homepage": "https://github.com/craft-native/craft/tree/main/packages/ios#readme",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1703,12 +1703,32 @@ struct CraftWebView: UIViewRepresentable {
1703
1703
  decisionHandler(.allow)
1704
1704
  return
1705
1705
  }
1706
+ // An iframe the app's page embeds loads in place. Only the main
1707
+ // frame's navigations leave for Safari: cancelling the frame's
1708
+ // too left every embed blank, a YouTube or Vimeo player among them.
1709
+ if let frame = navigationAction.targetFrame, !frame.isMainFrame, isEmbeddableFrameURL(url) {
1710
+ decisionHandler(.allow)
1711
+ return
1712
+ }
1706
1713
  if navigationAction.targetFrame?.isMainFrame != false {
1707
1714
  UIApplication.shared.open(url)
1708
1715
  }
1709
1716
  decisionHandler(.cancel)
1710
1717
  }
1711
1718
 
1719
+ /// What an iframe inside the app's page may load: a video player, a
1720
+ /// payment form, a map. It stays inside its frame, and the bridge
1721
+ /// answers only `isTrustedOrigin`, so loading one reaches nothing
1722
+ /// native. Plain http is left to the trusted local origins.
1723
+ private func isEmbeddableFrameURL(_ url: URL) -> Bool {
1724
+ switch url.scheme?.lowercased() {
1725
+ case "https", "about", "data", "blob":
1726
+ return true
1727
+ default:
1728
+ return false
1729
+ }
1730
+ }
1731
+
1712
1732
  func webViewWebContentProcessDidTerminate(_ webView: WKWebView) {
1713
1733
  webView.reload()
1714
1734
  }
@@ -4244,6 +4264,10 @@ struct CraftWebView: UIViewRepresentable {
4244
4264
  }
4245
4265
  isLocationRecordingPaused = false
4246
4266
  persistLocationRecordingState()
4267
+ // Set here too: a recording restored at launch while paused never
4268
+ // got a delegate, so resuming it started updates nobody received
4269
+ // and the route stopped at the pause.
4270
+ locationManager?.delegate = self
4247
4271
  requestLocationAuthorization()
4248
4272
  configureBackgroundLocationIfNeeded()
4249
4273
  locationManager?.startUpdatingLocation()