Fix iOS cancel-during-launch race with cancellable launch task wrappers#269
Fix iOS cancel-during-launch race with cancellable launch task wrappers#269Kief5555 merged 1 commit intokief5555/iosfrom
Conversation
| startSessionTasks() | ||
| lastError = nil | ||
| } catch is CancellationError { | ||
| showStreamLoading = false |
There was a problem hiding this comment.
[🟡 Medium]
scheduleLaunch/scheduleResume can cancel an older task and immediately start a newer one, but the cancelled task’s catch is CancellationError still unconditionally sets showStreamLoading = false, which can hide the loading overlay while the newer launch/resume is still running. This produces incorrect UI state and can let users interact as if launch finished when it has not. Track task identity before mutating shared UI state (e.g., capture a local task token and only clear showStreamLoading if the completing/cancelled task is still launchTask) or move loading-state ownership to the scheduler. ```swift
// ios/OpenNOWiOS/OpenNOWiOS/OpenNOWStore.swift
} catch is CancellationError {
showStreamLoading = false
return
} catch {
This PR fixes a race condition in OpenNOWiOS where tapping Cancel on
StreamLoadingViewduringisLaunchingSessionwould leave the in-flightapi.startSession()running becauseendSession()early-returned whenactiveSession == nil. The pending launch would then complete and start the session, ignoring the user's cancel request.OpenNOWStore.swift:
launchTaskprivate property to track the in-flight launch or resume operation.deinitnow cancelslaunchTaskin addition totelemetryTaskandsessionPollTask.CancellationErrorhandling inlaunch()andresumeSession()to dismiss the loading overlay without setting an error when cancelled.scheduleLaunch(game:)andscheduleResume(candidate:)synchronous wrappers that cancel any existinglaunchTaskand start the new operation.endSession()to cancel and nil outlaunchTaskimmediately before checkingactiveSession, ensuring the pending API call is aborted even whenactiveSessionis not yet set.View Call Sites:
HomeView.swift: ReplacedTask { await store.launch(game:) }withstore.scheduleLaunch(game:)in both the featured section and the game grid.BrowseView.swift: ReplacedTask { await store.launch(game:) }withstore.scheduleLaunch(game:).LibraryView.swift: ReplacedTask { await store.launch(game:) }withstore.scheduleLaunch(game:).SessionView.swift: ReplacedTask { await store.resumeSession(candidate:) }withstore.scheduleResume(candidate:).