ReviewOS

also looking at this

craft-native/craft

fix(native): interaction, audio, document and checkpoint gaps stranded after #401

#415
Merged glennmichael123 wants to merge codex/issues-388-400-344 into main
19 files +987 -50

Review threads live on the whole diff, not on one commit, so none are shown here - a thread's line means something in the branch's final form, and painting it into an intermediate step would put it on code it is not about.

docs/guides/native-stx.mdmodified+2-1
Changes to docs/guides/native-stx.md
@@ -1387,7 +1387,8 @@ dot. Non-JSON values and invalid names are rejected before a write is posted;
13871387the previous confirmed checkpoint remains available after a failed or
13881388interrupted replacement. If a screen closes while a checkpoint is still being
13891389written, its promise rejects instead of hanging; the host's later worker
1390completion cannot publish a second result for that request.
1390completion cannot publish a second result for that request. iOS also reports a
1391failed result when its writer completes after the screen runtime is disposed.
13911392
13921393The `media` capability (iOS) keeps video and audio on the phone for offline,
13931394in Application Support rather than the temporary directory, left out of
packages/ios/src/index.test.tsmodified+2-0
Changes to packages/ios/src/index.test.ts
@@ -543,6 +543,8 @@ describe('Craft iOS builder', () => {
543543 expect(native).toContain('pendingCapabilityDeadlines.values.forEach { $0.cancel() }')
544544 expect(native).toContain('CraftNativeScriptThread.run { timers.pending.values.forEach { $0.cancel() } }')
545545 expect(native).toContain('pendingCapabilityRequests.forEach { CraftNativeActions.cancel(requestToken: $0) }')
546 expect(native).toContain('self?.runtimeDisposed == true ? false : ok')
547 expect(native).toContain('never as a successful write into a dead runtime')
546548 expect(native).toContain('lifecycleObservers.forEach(NotificationCenter.default.removeObserver)')
547549 expect(native).not.toContain('WKWebView')
548550 })
packages/ios/templates/CraftNativeScreen.swiftmodified+11-4
Changes to packages/ios/templates/CraftNativeScreen.swift
@@ -6407,14 +6407,21 @@ final class CraftNativeScreenController: UIViewController, UIScrollViewDelegate,
64076407 CraftSnapshots.read(name)
64086408 }
64096409 let snapshotSet: @convention(block) (String, JSValue, JSValue) -> Void = { name, value, done in
6410 CraftSnapshots.write(name, json: value.isNull || value.isUndefined ? nil : value.toString()) { ok in
6411 CraftNativeScriptThread.run { done.call(withArguments: [ok]) }
6410 CraftSnapshots.write(name, json: value.isNull || value.isUndefined ? nil : value.toString()) { [weak self] ok in
6411 CraftNativeScriptThread.run {
6412 // A writer can finish after UIKit has disposed this
6413 // screen. Resolve the pending promise as a cancellation,
6414 // never as a successful write into a dead runtime.
6415 done.call(withArguments: [self?.runtimeDisposed == true ? false : ok])
6416 }
64126417 }
64136418 }
64146419 let snapshotClear: @convention(block) (JSValue, JSValue) -> Void = { keep, done in
64156420 let names = (keep.isUndefined || keep.isNull ? [] : (keep.toArray() ?? [])).compactMap { $0 as? String }
6416 CraftSnapshots.clear(keeping: Set(names)) { ok in
6417 CraftNativeScriptThread.run { done.call(withArguments: [ok]) }
6421 CraftSnapshots.clear(keeping: Set(names)) { [weak self] ok in
6422 CraftNativeScriptThread.run {
6423 done.call(withArguments: [self?.runtimeDisposed == true ? false : ok])
6424 }
64186425 }
64196426 }
64206427 let config = self.config