[Enhancement] Extend CallJoinIntercepting with will-join and did-join hooks#1176
Conversation
📝 WalkthroughWalkthroughAdds ChangesCall join interceptor lifecycle hooks
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant JoiningStage
participant CallController
participant Interceptor as CallJoinIntercepting
participant JoinedStage
JoiningStage->>CallController: subscribe stagePublisher
CallController-->>JoiningStage: stage == .joining
JoiningStage->>Interceptor: callWillJoin(call)
Interceptor->>Interceptor: disable remote audio tracks
JoinedStage->>JoinedStage: subscribeToCallSettingsUpdates / subscribeToOwnCapabilitiesChanges
JoinedStage->>Interceptor: callDidJoin(call)
Interceptor->>Interceptor: restore remote audio tracks
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
f9e7e98 to
dcedd62
Compare
Public Interface+ extension CallJoinIntercepting
+
+ public func callWillJoin(_ call: Call)async
+ public func callDidJoin(_ call: Call)async
|
SDK Size
|
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@DemoApp/Sources/Components/DemoCallJoinInterceptor/DemoCallJoinInterceptor.swift`:
- Around line 156-178: `disableMediaTracksAndCallSettings(_:)` is capturing
`currentUser` too early, so the `$participants` sink can compare against a stale
nil session id and mute the local participant. Move the local-participant lookup
into the participant stream mapping, using the current
`call.state.localParticipant` (or `call.state.sessionId`) when filtering
participants, so the self participant is excluded based on the latest joined
state. Keep the rest of the mute/restore flow in `DemoCallJoinInterceptor`
unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 43aded53-765f-4d14-a7ed-164a4853f8f1
📒 Files selected for processing (9)
DemoApp/Sources/Components/Debug/Items/FeatureFlags/Components/CallJoinInterceptor.swiftDemoApp/Sources/Components/DemoCallJoinInterceptor/DemoCallJoinInterceptor.swiftSources/StreamVideo/CallStateMachine/Stages/Call+JoinedStage.swiftSources/StreamVideo/CallStateMachine/Stages/Call+JoiningStage.swiftSources/StreamVideo/Controllers/CallController.swiftSources/StreamVideo/Utils/CallJoinIntercepting.swiftStreamVideoTests/CallStateMachine/CallStateMachine/Stages/CallStateMachine_JoinedStageTests.swiftStreamVideoTests/CallStateMachine/CallStateMachine/Stages/CallStateMachine_JoiningStageTests.swiftStreamVideoTests/Mock/MockCallController.swift
StreamVideo XCSize
|
|



🔗 Issue Links
IOS-1799
🎯 Goal
CallJoinInterceptingpreviously exposed a single gate,callReadyToJoin(_:), which only let integrators run (and optionally veto) work at the very last step of a join. That isn't enough for use cases that need to bracket the entire connection window — for example muting remote media while the peer connection is still negotiating and restoring it once the call is live. This PR adds two best-effort lifecycle hooks around the existing gate so integrators get a clear before/after pair.📝 Summary
callWillJoin(_:)andcallDidJoin(_:)toCallJoinIntercepting, with default no-op implementations so onlycallReadyToJoin(_:)stays required and existing conformers remain source-compatible.CallController.stagePublisher, a projection of the WebRTC coordinator's state-machine stage IDs, so the join flow can react to lifecycle transitions without touching WebRTC internals.JoiningStageinvokescallWillJoin(_:)when WebRTC enters.joining;JoinedStageinvokescallDidJoin(_:)once the call is fully joined.DemoCallJoinInterceptorto mute remote audio incallWillJoinand restore it incallDidJoin, and defaulted the debug feature flag to.synchronised.🛠 Implementation
The three hooks now fire at distinct points in the join lifecycle:
callWillJoin(_:).joining(peer connection still negotiating)callReadyToJoin(_:)callDidJoin(_:)callWillJoinis wired throughCallController.stagePublisher:JoiningStage.observeCallStage(_:joinCallInterceptor:)subscribes to it, filters for.joining, and calls the hook from a task stored in the stage-scopedDisposableBag(so it's torn down on transition away).callDidJoinis invoked fromJoinedStage.execute()after the call-settings and own-capabilities subscriptions are in place, recovering the interceptor from the join input carried in the state-machine context.The two new methods were named
callWillJoin/callDidJoin(rather thanpre/post) to follow the Applewill/diddelegate convention and to keep them visually distinct from the throwingcallReadyToJoingate. They ship with default implementations, so the addition is non-source-breaking and needs no deprecations.🧪 Manual Testing Notes
CallJoinInterceptordebug feature flag (defaults to.synchronisedin debug builds).Automated coverage added:
StreamCallStateMachineStageJoiningStage_Tests.test_execute_whenWebRTCStageBecomesJoining_invokesInterceptorCallIsPreparingCallStateMachineStageJoinedStage_Tests.test_joiningTransition_joinInterceptorProvided_callDidJoinInvokedMockCallControllergained a controllablestagePublisherto drive stage transitions.☑️ Contributor Checklist
Summary by CodeRabbit
New Features
Bug Fixes