Skip to content

Commit d7196ad

Browse files
committed
[Fix]Show joining state after accepting an incoming call
Move CallViewModel into .joining as soon as an incoming call is accepted so the joining UI is shown while enterCall completes asynchronously. Add a regression test that verifies the transient joining state is emitted before the final in-call state, and document the fix in the changelog. # Conflicts: # CHANGELOG.md
1 parent 6b78beb commit d7196ad

File tree

3 files changed

+35
-0
lines changed

3 files changed

+35
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
1616
- Fix label color when presenting. [#1077](https://github.com/GetStream/stream-video-swift/pull/1077)
1717
- Ensure CallKit push token updates and invalidation mutate `deviceToken` on the main actor to avoid Swift concurrency/actor-isolation issues. [#1076](https://github.com/GetStream/stream-video-swift/pull/1076)
1818
- Ensure CallKit joins keep the answer action completion alive until WebRTC has configured the audio device module. [#1081](https://github.com/GetStream/stream-video-swift/pull/1081)
19+
- Update incoming call acceptance to move `CallViewModel` into `.joining` before the call finishes entering, so the joining UI appears immediately. [#1079](https://github.com/GetStream/stream-video-swift/pull/1079)
1920

2021
# [1.43.0](https://github.com/GetStream/stream-video-swift/releases/tag/1.43.0)
2122
_February 27, 2026_

Sources/StreamVideoSwiftUI/CallViewModel.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,11 @@ open class CallViewModel: ObservableObject {
579579
do {
580580
hasAcceptedCall = true
581581
try await call.accept()
582+
583+
// Mirror `joinCall` so the incoming UI is dismissed before
584+
// `enterCall` finishes the async join flow.
585+
await MainActor.run { self.setCallingState(.joining) }
586+
582587
enterCall(
583588
call: call,
584589
callType: callType,

StreamVideoSwiftUITests/CallViewModel_Tests.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Copyright © 2026 Stream.io Inc. All rights reserved.
33
//
44

5+
import Combine
56
@testable import StreamVideo
67
@testable import StreamVideoSwiftUI
78
import StreamWebRTC
@@ -405,6 +406,34 @@ final class CallViewModel_Tests: XCTestCase, @unchecked Sendable {
405406
await assertCallingState(.inCall)
406407
}
407408

409+
func test_incomingCall_acceptCall_updatesCallingStateToJoiningBeforeInCall() async throws {
410+
// Given
411+
await prepareIncomingCallScenario()
412+
let joiningStateExpectation = expectation(
413+
description: "CallingState becomes joining"
414+
)
415+
joiningStateExpectation.assertForOverFulfill = false
416+
var cancellable: AnyCancellable?
417+
418+
// Capture the transient state because `acceptCall` continues into the
419+
// async `enterCall` flow immediately after the acceptance request.
420+
cancellable = subject.$callingState
421+
.dropFirst()
422+
.sink { state in
423+
if state == .joining {
424+
joiningStateExpectation.fulfill()
425+
}
426+
}
427+
defer { cancellable?.cancel() }
428+
429+
// When
430+
subject.acceptCall(callType: callType, callId: callId)
431+
432+
// Then
433+
await fulfillment(of: [joiningStateExpectation], timeout: defaultTimeout)
434+
await assertCallingState(.inCall)
435+
}
436+
408437
func test_incomingCall_acceptedFromSameUserElsewhere_callingStateChangesToIdle() async throws {
409438
// Given
410439
await prepareIncomingCallScenario()

0 commit comments

Comments
 (0)