feat: stereo audio output support RN-332#2038
Conversation
# Conflicts: # sample-apps/react-native/dogfood/ios/Podfile.lock
# Conflicts: # packages/client/src/Call.ts # sample-apps/react-native/dogfood/ios/Podfile.lock
|
📝 WalkthroughWalkthroughAdds a global in-call facade bound to native APIs; integrates callManager.setup()/start()/stop() into client call lifecycle; introduces optional stereo audio output controls and platform audio refactors on Android and iOS; removes the Android audio setup store utility; samples updated to opt into stereo and bumped WebRTC dep. Changes
Sequence Diagram(s)sequenceDiagram
participant Client as Client (call flow)
participant Global as globalThis.streamRNVideoSDK
participant RNModule as RN StreamInCallManager NativeModule
participant Native as Native In-Call Manager
Client->>Global: callManager.setup()
Global->>RNModule: setup()
RNModule->>Native: setup()
Client->>Global: callManager.start()
Global->>RNModule: start()
RNModule->>Native: start() / configure role & stereo
Client->>Global: callManager.stop()
Global->>RNModule: stop()
RNModule->>Native: stop() / deactivate audio
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
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 |
| intendedCategory = .playAndRecord | ||
| intendedMode = defaultAudioDevice == .speaker ? .videoChat : .voiceChat | ||
|
|
||
| // XCode 16 and older don't expose .allowBluetoothHFP | ||
| // https://forums.swift.org/t/xcode-26-avaudiosession-categoryoptions-allowbluetooth-deprecated/80956 | ||
| #if compiler(>=6.2) // For Xcode 26.0+ | ||
| let bluetoothOption: AVAudioSession.CategoryOptions = .allowBluetoothHFP | ||
| #else | ||
| let bluetoothOption: AVAudioSession.CategoryOptions = .allowBluetooth | ||
| #endif | ||
| intendedOptions = defaultAudioDevice == .speaker ? [bluetoothOption, .defaultToSpeaker] : [bluetoothOption] | ||
| } |
There was a problem hiding this comment.
fyi.. default config adjusted here..
There was a problem hiding this comment.
Actionable comments posted: 9
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
packages/react-native-sdk/src/modules/call-manager/CallManager.ts (1)
95-105: Explicitly disable stereo to avoid leaking state across calls.When
audioRole === 'listener' && enableStereoAudioOutputis true, stereo is enabled. But when either condition is false (or config is missing),setEnableStereoAudioOutputis never called to disable it. If native state persists acrossstart()calls, stereo from a listener session will carry over to the next communicator session.Proposed fix
start = (config?: StreamInCallManagerConfig): void => { - NativeManager.setAudioRole(config?.audioRole ?? 'communicator'); - if (config?.audioRole === 'communicator') { + const audioRole = config?.audioRole ?? 'communicator'; + NativeManager.setAudioRole(audioRole); + if (audioRole === 'communicator') { const type = config.deviceEndpointType ?? 'speaker'; NativeManager.setDefaultAudioDeviceEndpointType(type); } - if (config?.audioRole === 'listener' && config.enableStereoAudioOutput) { + const shouldEnableStereo = + audioRole === 'listener' && !!config?.enableStereoAudioOutput; + // Explicitly set stereo to avoid leaking previous state across calls. + NativeManager.setEnableStereoAudioOutput(shouldEnableStereo); - NativeManager.setEnableStereoAudioOutput(true); - } NativeManager.start(); };packages/client/src/Call.ts (1)
879-896: MovecallManager.start()to ensure lifecycle pairing withstop().The
callManager.start()call at line 896 is invoked before the join retry loop. Ifjoin()fails after all retries,start()will have been called butstop()will not be invoked unlessleave()is explicitly called. This creates a lifecycle imbalance.Either move
callManager.start()into the successful join flow (afterdoJoin()completes successfully) or document thatstart()is idempotent and callers are responsible for callingleave()on join failure to ensure proper cleanup.
🤖 Fix all issues with AI agents
In
@packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/audio/utils/AudioFocusUtil.kt:
- Around line 37-48: The private setup(audioAttributes: AudioAttributes,
reactContext: ReactContext) currently force-unwraps the WebRTCModule via
reactContext.getNativeModule(WebRTCModule::class.java)!! which can NPE; change
it to safely get the module (check for null), bail out or log and return if
missing, then safely obtain audioDeviceModule from the module (check its
type/null) before calling WebRtcAudioTrackHelper.setAudioOutputAttributes(adm,
audioAttributes); ensure you remove the !! and handle both missing WebRTCModule
and missing/invalid audioDeviceModule gracefully.
In
@packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/callmanager/StreamInCallManagerModule.kt:
- Around line 83-92: The error log in setEnableStereoAudioOutput incorrectly
references "setAudioRole()" due to a copy-paste mistake; update the log call
inside AudioDeviceManager.runInAudioThread (where it checks
audioManagerActivated) to use "setEnableStereoAudioOutput():" (include TAG and
keep same log level) so the message matches the actual method name and context.
- Around line 118-120: The stop() method clears audioManagerActivated
unconditionally which is asymmetric with start() where audioManagerActivated =
true is set only inside reactApplicationContext.currentActivity?.let; update
stop() so the audioManagerActivated = false assignment is moved inside the
reactApplicationContext.currentActivity?.let block that calls
mAudioDeviceManager.stop(it), or alternatively add an explicit log and retain
the flag only when no activity is present; ensure you modify the stop()
implementation (referencing stop(), audioManagerActivated,
mAudioDeviceManager.stop, and reactApplicationContext.currentActivity?.let) so
the flag change mirrors start() behavior or clearly logs skipped cleanup.
- Around line 94-97: The public ReactMethod setup() currently calls
mAudioDeviceManager.setup() directly and must be run on the audio thread for
thread-safety; wrap the body of setup() in AudioDeviceManager.runInAudioThread
(i.e., call mAudioDeviceManager.runInAudioThread { mAudioDeviceManager.setup()
}) so it executes on the audio thread like start() and other configuration
methods (which also guard access to shared state such as callAudioRole and
mAudioManager.mode) and avoid races with setAudioRole().
In @packages/react-native-sdk/ios/StreamInCallManager.swift:
- Around line 215-223: The setForceSpeakerphoneOn(enable:) implementation calls
AVAudioSession.sharedInstance().setActive(true) without RTCAudioSession locking,
causing a race with WebRTC; modify setForceSpeakerphoneOn to run on
audioSessionQueue and use
RTCAudioSession.sharedInstance().lockForConfiguration() /
unlockForConfiguration() around the operation, call
AVAudioSession.sharedInstance().overrideOutputAudioPort(enable ? .speaker :
.none) inside that locked section, and then call session.setActive(true) on the
RTCAudioSession instance, with defer-based unlocking and the existing error
handling preserved.
- Around line 81-132: The setup() method has two fixes: add a safe guard in
getAudioDeviceModule() to avoid a forced cast crash by checking that
self.bridge.module(forName: "WebRTCModule") is a WebRTCModule and, if not, fail
fast with a clear message like "WebRTCModule is required but not registered with
the bridge" before returning audioDeviceModule; and turn the TODO about failing
with .allowBluetoothA2DP (stereo Bluetooth causing OSStatus -50) into an
explicit documented limitation (create an issue/track it and update the comment
near allowBluetoothA2DP to reference that issue and note that iOS WebRTC rejects
.allowBluetoothA2DP for stereo routing).
In @packages/react-native-sdk/src/index.ts:
- Line 19: registerSDKGlobals() is being called unconditionally which exposes
streamRNVideoSDK (that depends on native modules) on web; wrap the
registerSDKGlobals() invocation in a Platform.OS !== 'web' guard to match the
existing registerGlobals() pattern so the global is only registered on native
platforms (use the same conditional style used around registerGlobals()).
In @sample-apps/react-native/dogfood/ios/AppDelegate.swift:
- Around line 150-154: The WebRTC verbose logging is being enabled
unconditionally; wrap the assignment of
WebRTCModuleOptions.sharedInstance().loggingSeverity = .verbose in a DEBUG-only
conditional so verbose logs are only enabled in dev builds: locate the
WebRTCModuleOptions.sharedInstance() usage (and existing
enableMultitaskingCameraAccess setting) in AppDelegate.swift and enclose the
loggingSeverity = .verbose line inside an #if DEBUG / #endif block, leaving
enableMultitaskingCameraAccess unchanged.
🧹 Nitpick comments (3)
packages/react-native-sdk/src/modules/call-manager/CallManager.ts (1)
114-119:setup()should be platform-guarded and version-guarded (new public API).This is now part of the public surface; calling it on unsupported platforms (or with older native builds) should fail with a clear error rather than “undefined is not a function”.
Proposed fix
setup = (): void => { - NativeManager.setup(); + invariant( + Platform.OS === 'ios' || Platform.OS === 'android', + 'Supported only on iOS/Android', + ); + invariant( + typeof NativeManager.setup === 'function', + 'StreamInCallManager.setup is not available. Ensure native modules are updated.', + ); + NativeManager.setup(); };packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/audio/utils/AudioFocusUtil.kt (1)
50-67: Good refactoring to eliminate code duplication.The extraction of
getAudioAttributesand the centralizedsetupmethod successfully eliminates duplication while maintaining the same logic flow. TherequestFocusmethod now properly delegates to the shared helpers.packages/react-native-sdk/src/utils/internal/registerSDKGlobals.ts (1)
1-26: Consider error handling in facade methods.The facade methods delegate directly to
callManagerwithout error handling. IfcallManager.setup(),.start(), or.stop()throw errors, they will propagate to the Call class in the client package, which may not have appropriate error handling (as noted in the Call.ts review).♻️ Add error boundary to facade
const streamRNVideoSDKGlobals: StreamRNVideoSDKGlobals = { callManager: { setup: () => { - callManager.setup(); + try { + callManager.setup(); + } catch (err) { + console.warn('[StreamVideoSDK] Failed to setup call manager', err); + } }, start: () => { - callManager.start(); + try { + callManager.start(); + } catch (err) { + console.warn('[StreamVideoSDK] Failed to start call manager', err); + } }, stop: () => { - callManager.stop(); + try { + callManager.stop(); + } catch (err) { + console.warn('[StreamVideoSDK] Failed to stop call manager', err); + } }, }, };Note: The duplicate global declaration at lines 4-6 is redundant since it's already declared in
@stream-io/video-client/src/types.ts. Consider removing it to avoid potential conflicts.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
⛔ Files ignored due to path filters (7)
packages/audio-filters-web/src/krispai/dist/krispsdk.es5.jsis excluded by!**/dist/**packages/audio-filters-web/src/krispai/dist/krispsdk.jsis excluded by!**/dist/**packages/audio-filters-web/src/krispai/dist/krispsdk.mjsis excluded by!**/dist/**packages/audio-filters-web/src/krispai/dist/usermedia.jsis excluded by!**/dist/**packages/audio-filters-web/src/krispai/dist/usermedia.mjsis excluded by!**/dist/**sample-apps/react-native/dogfood/ios/Podfile.lockis excluded by!**/*.lockyarn.lockis excluded by!**/yarn.lock,!**/*.lock
📒 Files selected for processing (16)
packages/client/src/Call.tspackages/client/src/types.tspackages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/audio/AudioDeviceManager.ktpackages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/audio/utils/AudioFocusUtil.ktpackages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/audio/utils/AudioSetupStoreUtil.ktpackages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/callmanager/StreamInCallManagerModule.ktpackages/react-native-sdk/ios/StreamInCallManager.mpackages/react-native-sdk/ios/StreamInCallManager.swiftpackages/react-native-sdk/src/index.tspackages/react-native-sdk/src/modules/call-manager/CallManager.tspackages/react-native-sdk/src/modules/call-manager/native-module.d.tspackages/react-native-sdk/src/modules/call-manager/types.tspackages/react-native-sdk/src/utils/internal/registerSDKGlobals.tssample-apps/react-native/dogfood/ios/AppDelegate.swiftsample-apps/react-native/dogfood/package.jsonsample-apps/react-native/dogfood/src/screens/LiveStream/ViewLiveStream.tsx
💤 Files with no reviewable changes (1)
- packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/audio/utils/AudioSetupStoreUtil.kt
🧰 Additional context used
📓 Path-based instructions (14)
packages/react-native-sdk/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (packages/react-native-sdk/CLAUDE.md)
packages/react-native-sdk/src/**/*.{ts,tsx}: Use React hooks from@stream-io/video-react-bindings(viauseCall()anduseCallStateHooks()) instead of directly accessing RxJS observables from@stream-io/video-client
Use Platform.OS checks and conditional imports to gate platform-specific code (iOS, Android, web) and prevent importing platform-specific libraries on unsupported platforms
Files:
packages/react-native-sdk/src/modules/call-manager/types.tspackages/react-native-sdk/src/utils/internal/registerSDKGlobals.tspackages/react-native-sdk/src/modules/call-manager/CallManager.tspackages/react-native-sdk/src/modules/call-manager/native-module.d.tspackages/react-native-sdk/src/index.ts
packages/react-native-sdk/src/modules/**/*.ts
📄 CodeRabbit inference engine (packages/react-native-sdk/CLAUDE.md)
Structure native module TypeScript interfaces in
src/modules/to match the exact signatures of their native implementations (Android Kotlin and iOS Swift), including parameter types and return types
Files:
packages/react-native-sdk/src/modules/call-manager/types.tspackages/react-native-sdk/src/modules/call-manager/CallManager.tspackages/react-native-sdk/src/modules/call-manager/native-module.d.ts
packages/{client,react-sdk,react-native-sdk}/src/**/*.ts?(x)
📄 CodeRabbit inference engine (AGENTS.md)
packages/{client,react-sdk,react-native-sdk}/src/**/*.ts?(x): Use TypeScript for all source code in packages/client, packages/react-sdk, and packages/react-native-sdk
Mark deprecated APIs with @deprecated JSDoc, including rationale and replacement guidance
Throw descriptive errors or return typed error results consistently with existing patterns in public APIs
Gate internal debug logging behind an environment flag; no console noise in production builds
Never leak credentials or user data in error messages or logs
Check instance IDs and timestamps before state updates to avoid race conditions in async operations
Make public API surfaces explicit with TypeScript types and interfaces
Files:
packages/react-native-sdk/src/modules/call-manager/types.tspackages/react-native-sdk/src/utils/internal/registerSDKGlobals.tspackages/client/src/types.tspackages/client/src/Call.tspackages/react-native-sdk/src/modules/call-manager/CallManager.tspackages/react-native-sdk/src/modules/call-manager/native-module.d.tspackages/react-native-sdk/src/index.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx,js,jsx}: Use camelCase for function and property names
Narrowly scope eslint-disable comments with inline explanatory comments and rationale
Files:
packages/react-native-sdk/src/modules/call-manager/types.tspackages/react-native-sdk/src/utils/internal/registerSDKGlobals.tssample-apps/react-native/dogfood/src/screens/LiveStream/ViewLiveStream.tsxpackages/client/src/types.tspackages/client/src/Call.tspackages/react-native-sdk/src/modules/call-manager/CallManager.tspackages/react-native-sdk/src/modules/call-manager/native-module.d.tspackages/react-native-sdk/src/index.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use PascalCase for component and type names
Files:
packages/react-native-sdk/src/modules/call-manager/types.tspackages/react-native-sdk/src/utils/internal/registerSDKGlobals.tssample-apps/react-native/dogfood/src/screens/LiveStream/ViewLiveStream.tsxpackages/client/src/types.tspackages/client/src/Call.tspackages/react-native-sdk/src/modules/call-manager/CallManager.tspackages/react-native-sdk/src/modules/call-manager/native-module.d.tspackages/react-native-sdk/src/index.ts
**/*.{sh,js,ts}
📄 CodeRabbit inference engine (AGENTS.md)
Make scripts error on missing critical environment variables
Files:
packages/react-native-sdk/src/modules/call-manager/types.tspackages/react-native-sdk/src/utils/internal/registerSDKGlobals.tspackages/client/src/types.tspackages/client/src/Call.tspackages/react-native-sdk/src/modules/call-manager/CallManager.tspackages/react-native-sdk/src/modules/call-manager/native-module.d.tspackages/react-native-sdk/src/index.ts
packages/react-native-sdk/src/utils/**/*.ts
📄 CodeRabbit inference engine (packages/react-native-sdk/CLAUDE.md)
Implement React Native native modules using
NativeModulesAPI and always wrap calls in try-catch blocks to handle promise rejection from native code
Files:
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.ts
sample-apps/**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use placeholder keys (e.g., YOUR_STREAM_KEY) in code examples and sample apps, never real credentials
Files:
sample-apps/react-native/dogfood/src/screens/LiveStream/ViewLiveStream.tsx
packages/client/**/*.ts
📄 CodeRabbit inference engine (packages/client/CLAUDE.md)
packages/client/**/*.ts: Implement scoped loggers with configurable log levels (debug, info, warn, error) for each component for easier debugging
Use helper functions isReactNative(), isSafari(), isFirefox(), isMobileSafari() to detect platform differences and apply browser/platform-specific quirks
Files:
packages/client/src/types.tspackages/client/src/Call.ts
packages/client/src/**/*.ts?(x)
📄 CodeRabbit inference engine (AGENTS.md)
Avoid unguarded web-only APIs in shared code between React and React Native
Files:
packages/client/src/types.tspackages/client/src/Call.ts
packages/client/**/Call.ts
📄 CodeRabbit inference engine (packages/client/CLAUDE.md)
packages/client/**/Call.ts: Use withoutConcurrency for serial execution in join/leave operations and critical state changes; use withCancellation for device toggling and switching operations
Use PermissionsContext.canPublish(trackType) and hasPermission() methods to gate operations based on user permissions before publishing or performing actions
Use distinct tag symbols for different concurrency operations to ensure operations with same tag run serially while different tags run in parallel; use settled(tag) to wait for completion
Files:
packages/client/src/Call.ts
packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/**/*.kt
📄 CodeRabbit inference engine (packages/react-native-sdk/CLAUDE.md)
When adding Android native functionality, create Kotlin file in
android/src/main/java/com/streamvideo/reactnative/, use@ReactMethodannotation, register inStreamVideoReactNativePackage.kt, and call from TypeScript viaNativeModules.StreamVideoReactNative
Files:
packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/audio/utils/AudioFocusUtil.ktpackages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/callmanager/StreamInCallManagerModule.ktpackages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/audio/AudioDeviceManager.kt
packages/react-native-sdk/ios/**/*.swift
📄 CodeRabbit inference engine (packages/react-native-sdk/CLAUDE.md)
When adding iOS native functionality, create Swift file in
ios/, create Objective-C bridge withRCT_EXTERN_METHOD, expose viaStreamVideoReactNative.swift, and call from TypeScript viaNativeModules.StreamVideoReactNative
Files:
packages/react-native-sdk/ios/StreamInCallManager.swift
packages/react-native-sdk/src/index.ts
📄 CodeRabbit inference engine (packages/react-native-sdk/CLAUDE.md)
packages/react-native-sdk/src/index.ts: Always callregisterGlobals()from@stream-io/react-native-webrtcbefore using WebRTC on non-web platforms in the React Native SDK entry point
Export all public SDK APIs (components, hooks, providers, types) from the mainsrc/index.tsfile, with re-exports from client and bindings packages and SDK-specific implementations
Files:
packages/react-native-sdk/src/index.ts
🧠 Learnings (40)
📓 Common learnings
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/client/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:06.761Z
Learning: Applies to packages/client/**/rtc/helpers/sdp.ts : Use removeCodecsExcept() helper for codec selection and enableStereo() for high-quality audio in music/DJ applications
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/providers/StreamCall/**/*.tsx : Disable local video in background on iOS to save battery, but maintain audio connection for ongoing calls using AVAudioSession background modes
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/index.ts : Always call `registerGlobals()` from `stream-io/react-native-webrtc` before using WebRTC on non-web platforms in the React Native SDK entry point
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/hooks/**/*.ts : Handle iOS backgrounding by disabling video tracks to prevent battery drain when `AppState` changes to 'background', while keeping audio enabled
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/providers/StreamVideo.tsx : Use `StreamVideoRN.configure()` static method to set up SDK configuration including foreground service, push notification providers, and notification callbacks before mounting StreamVideo provider
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: For cross-package changes, rebuild in dependency order: client → bindings → react-native-sdk, then test in sample app to verify integration
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/ios/**/*.swift : When adding iOS native functionality, create Swift file in `ios/`, create Objective-C bridge with `RCT_EXTERN_METHOD`, expose via `StreamVideoReactNative.swift`, and call from TypeScript via `NativeModules.StreamVideoReactNative`
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/hooks/**/*.ts : Implement Android foreground service via `useAndroidKeepCallAliveEffect()` hook to prevent call termination when app is backgrounded, with automatic permission handling for POST_NOTIFICATIONS (Android 13+)
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T11:08:35.312Z
Learning: Test React Native changes on both iOS and Android for native module or platform-specific UI changes
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/**/*.{ts,tsx} : Use React hooks from `stream-io/video-react-bindings` (via `useCall()` and `useCallStateHooks()`) instead of directly accessing RxJS observables from `stream-io/video-client`
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/providers/StreamCall/**/*.tsx : Disable local video in background on iOS to save battery, but maintain audio connection for ongoing calls using AVAudioSession background modes
Applied to files:
packages/react-native-sdk/src/modules/call-manager/types.tssample-apps/react-native/dogfood/src/screens/LiveStream/ViewLiveStream.tsxpackages/client/src/types.tspackages/client/src/Call.tspackages/react-native-sdk/ios/StreamInCallManager.mpackages/react-native-sdk/src/modules/call-manager/CallManager.tspackages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/audio/utils/AudioFocusUtil.ktpackages/react-native-sdk/src/modules/call-manager/native-module.d.tspackages/react-native-sdk/ios/StreamInCallManager.swiftpackages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/callmanager/StreamInCallManagerModule.ktpackages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/audio/AudioDeviceManager.kt
📚 Learning: 2026-01-09T11:08:06.856Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:08:06.856Z
Learning: Applies to packages/react-sdk/**/*.{ts,tsx} : Never play local participant audio; always check !participant.isLocalParticipant before rendering Audio components
Applied to files:
packages/react-native-sdk/src/modules/call-manager/types.tssample-apps/react-native/dogfood/src/screens/LiveStream/ViewLiveStream.tsx
📚 Learning: 2026-01-09T11:07:06.761Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/client/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:06.761Z
Learning: Applies to packages/client/**/rtc/helpers/sdp.ts : Use removeCodecsExcept() helper for codec selection and enableStereo() for high-quality audio in music/DJ applications
Applied to files:
packages/react-native-sdk/src/modules/call-manager/types.tssample-apps/react-native/dogfood/src/screens/LiveStream/ViewLiveStream.tsxpackages/react-native-sdk/src/modules/call-manager/CallManager.tspackages/react-native-sdk/ios/StreamInCallManager.swift
📚 Learning: 2026-01-09T11:08:06.856Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:08:06.856Z
Learning: Applies to packages/react-sdk/src/core/components/{ParticipantView,Video,Audio}/**/*.{ts,tsx} : Use callback refs pattern to support both internal state and external user refs for media elements
Applied to files:
packages/react-native-sdk/src/modules/call-manager/types.tssample-apps/react-native/dogfood/src/screens/LiveStream/ViewLiveStream.tsxpackages/client/src/Call.ts
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/index.ts : Always call `registerGlobals()` from `stream-io/react-native-webrtc` before using WebRTC on non-web platforms in the React Native SDK entry point
Applied to files:
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.tspackages/client/src/types.tspackages/client/src/Call.tspackages/react-native-sdk/src/modules/call-manager/CallManager.tssample-apps/react-native/dogfood/package.jsonsample-apps/react-native/dogfood/ios/AppDelegate.swiftpackages/react-native-sdk/src/index.ts
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/ios/**/*.swift : When adding iOS native functionality, create Swift file in `ios/`, create Objective-C bridge with `RCT_EXTERN_METHOD`, expose via `StreamVideoReactNative.swift`, and call from TypeScript via `NativeModules.StreamVideoReactNative`
Applied to files:
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.tspackages/client/src/types.tspackages/react-native-sdk/ios/StreamInCallManager.mpackages/react-native-sdk/src/modules/call-manager/native-module.d.tssample-apps/react-native/dogfood/package.jsonpackages/react-native-sdk/ios/StreamInCallManager.swiftpackages/react-native-sdk/src/index.ts
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/index.ts : Export all public SDK APIs (components, hooks, providers, types) from the main `src/index.ts` file, with re-exports from client and bindings packages and SDK-specific implementations
Applied to files:
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.tspackages/client/src/types.tspackages/react-native-sdk/src/index.ts
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/version.ts : Store generated version information in `src/version.ts` (auto-generated via `yarn copy-version` script) and export for SDK consumers
Applied to files:
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.tssample-apps/react-native/dogfood/package.jsonpackages/react-native-sdk/src/index.ts
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/**/*.kt : When adding Android native functionality, create Kotlin file in `android/src/main/java/com/streamvideo/reactnative/`, use `ReactMethod` annotation, register in `StreamVideoReactNativePackage.kt`, and call from TypeScript via `NativeModules.StreamVideoReactNative`
Applied to files:
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.tspackages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/audio/utils/AudioFocusUtil.ktpackages/react-native-sdk/src/modules/call-manager/native-module.d.tspackages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/callmanager/StreamInCallManagerModule.ktpackages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/audio/AudioDeviceManager.kt
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/**/*.{ts,tsx} : Use React hooks from `stream-io/video-react-bindings` (via `useCall()` and `useCallStateHooks()`) instead of directly accessing RxJS observables from `stream-io/video-client`
Applied to files:
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.tssample-apps/react-native/dogfood/src/screens/LiveStream/ViewLiveStream.tsxpackages/client/src/types.tspackages/client/src/Call.tspackages/react-native-sdk/src/modules/call-manager/native-module.d.tssample-apps/react-native/dogfood/package.jsonpackages/react-native-sdk/src/index.ts
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/providers/StreamVideo.tsx : Use `StreamVideoRN.configure()` static method to set up SDK configuration including foreground service, push notification providers, and notification callbacks before mounting StreamVideo provider
Applied to files:
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.tssample-apps/react-native/dogfood/src/screens/LiveStream/ViewLiveStream.tsxpackages/client/src/types.tspackages/client/src/Call.tspackages/react-native-sdk/src/index.ts
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/modules/**/*.ts : Structure native module TypeScript interfaces in `src/modules/` to match the exact signatures of their native implementations (Android Kotlin and iOS Swift), including parameter types and return types
Applied to files:
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.tspackages/react-native-sdk/src/modules/call-manager/native-module.d.tspackages/react-native-sdk/src/index.ts
📚 Learning: 2026-01-09T11:08:35.312Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T11:08:35.312Z
Learning: Applies to packages/{client,react-sdk,react-native-sdk}/src/**/*.ts?(x) : Make public API surfaces explicit with TypeScript types and interfaces
Applied to files:
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.tspackages/client/src/types.tspackages/react-native-sdk/src/modules/call-manager/native-module.d.tspackages/react-native-sdk/src/index.ts
📚 Learning: 2026-01-09T11:08:06.856Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:08:06.856Z
Learning: Applies to packages/react-sdk/**/*.{ts,tsx} : Always use hooks from stream-io/video-react-bindings via useCallStateHooks() factory pattern instead of importing individual hooks directly
Applied to files:
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.tspackages/client/src/types.tspackages/client/src/Call.ts
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/**/*.tsx : Always request media permissions via `usePermissionRequest()` hook before calling device enable methods like `call.camera.enable()` or `call.microphone.enable()`
Applied to files:
sample-apps/react-native/dogfood/src/screens/LiveStream/ViewLiveStream.tsxpackages/react-native-sdk/src/modules/call-manager/CallManager.tspackages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/audio/utils/AudioFocusUtil.ktpackages/react-native-sdk/src/modules/call-manager/native-module.d.tspackages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/callmanager/StreamInCallManagerModule.ktpackages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/audio/AudioDeviceManager.kt
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/hooks/**/*.ts : Handle iOS backgrounding by disabling video tracks to prevent battery drain when `AppState` changes to 'background', while keeping audio enabled
Applied to files:
sample-apps/react-native/dogfood/src/screens/LiveStream/ViewLiveStream.tsx
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/providers/**/*.tsx : Use the renderless component pattern (component that returns null) for side-effect-only child components within `StreamCall` and `StreamVideo` providers to keep logic separated and testable
Applied to files:
sample-apps/react-native/dogfood/src/screens/LiveStream/ViewLiveStream.tsx
📚 Learning: 2026-01-09T11:08:35.312Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T11:08:35.312Z
Learning: Applies to packages/{client,react-sdk,react-native-sdk}/src/**/*.ts?(x) : Throw descriptive errors or return typed error results consistently with existing patterns in public APIs
Applied to files:
packages/client/src/types.ts
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/theme/**/*.ts : Apply deep partial theme overrides using the `DeepPartial<Theme>` type when customizing StreamVideo provider theme to maintain type safety
Applied to files:
packages/client/src/types.ts
📚 Learning: 2026-01-09T11:08:06.856Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:08:06.856Z
Learning: Applies to packages/react-sdk/src/**/*.{ts,tsx} : Translations are provided via i18next in bindings; use useI18n() hook and support translationsOverrides prop in StreamVideo
Applied to files:
packages/client/src/types.ts
📚 Learning: 2026-01-09T11:07:06.761Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/client/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:06.761Z
Learning: Applies to packages/client/**/Call.ts : Use withoutConcurrency for serial execution in join/leave operations and critical state changes; use withCancellation for device toggling and switching operations
Applied to files:
packages/client/src/Call.ts
📚 Learning: 2026-01-09T11:07:06.761Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/client/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:06.761Z
Learning: Applies to packages/client/**/*{CallState,stateStore}.ts : Use RxJS observables (BehaviorSubject) for state management and always update state through .next(), never mutate state directly
Applied to files:
packages/client/src/Call.ts
📚 Learning: 2026-01-09T11:07:06.761Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/client/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:06.761Z
Learning: Applies to packages/client/**/callEventHandlers.ts : Register Coordinator event handlers in callEventHandlers.ts to wire up backend events to state updates; distinguish from SFU events using isSfuEvent()
Applied to files:
packages/client/src/Call.ts
📚 Learning: 2026-01-09T11:07:06.761Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/client/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:06.761Z
Learning: Applies to packages/client/**/{CallState,Subscriber}.ts : Use sessionId as the unique identifier for participants, not userId; use trackLookupPrefix for track lookups combining participant ID and track type
Applied to files:
packages/client/src/Call.ts
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/hooks/push/**/*.ts : Use RxJS subjects from `src/utils/push/internal/rxSubjects.ts` (like `pushTappedIncomingCallCId$`, `pushAcceptedIncomingCallCId$`, `pushRejectedIncomingCallCId$`, `voipPushNotificationCallCId$`) to bridge native push events to React hooks
Applied to files:
packages/client/src/Call.ts
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/hooks/**/*.ts : When adding new React Native hooks, use `useCall()` and `useCallStateHooks()` from bindings layer, handle platform-specific logic with `Platform.OS`, add tests in `__tests__/hooks/`, and export from `src/hooks/index.ts`
Applied to files:
packages/react-native-sdk/src/modules/call-manager/CallManager.tspackages/react-native-sdk/src/modules/call-manager/native-module.d.tspackages/react-native-sdk/src/index.ts
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/hooks/**/*.ts : Implement Android foreground service via `useAndroidKeepCallAliveEffect()` hook to prevent call termination when app is backgrounded, with automatic permission handling for POST_NOTIFICATIONS (Android 13+)
Applied to files:
packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/audio/utils/AudioFocusUtil.ktpackages/react-native-sdk/src/modules/call-manager/native-module.d.ts
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/expo-config-plugin/src/**/*.ts : Configure Expo config plugins in `expo-config-plugin/` with platform-specific modifiers for Android manifest, iOS Info.plist, and app delegate modifications
Applied to files:
packages/react-native-sdk/src/modules/call-manager/native-module.d.ts
📚 Learning: 2026-01-09T11:08:06.856Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:08:06.856Z
Learning: Test changes that affect multiple packages in dependency order: client → bindings → react-sdk
Applied to files:
sample-apps/react-native/dogfood/package.json
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: For cross-package changes, rebuild in dependency order: client → bindings → react-native-sdk, then test in sample app to verify integration
Applied to files:
sample-apps/react-native/dogfood/package.json
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Use the dogfood sample app (`sample-apps/react-native/dogfood/`) for manual testing of all SDK features and integration with push notifications before release
Applied to files:
sample-apps/react-native/dogfood/package.json
📚 Learning: 2026-01-09T11:08:35.312Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T11:08:35.312Z
Learning: Applies to packages/client/src/**/*.ts?(x) : Avoid unguarded web-only APIs in shared code between React and React Native
Applied to files:
sample-apps/react-native/dogfood/package.json
📚 Learning: 2026-01-09T11:08:35.312Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T11:08:35.312Z
Learning: Applies to packages/{client,react-sdk,react-native-sdk}/src/**/*.ts?(x) : Mark deprecated APIs with deprecated JSDoc, including rationale and replacement guidance
Applied to files:
sample-apps/react-native/dogfood/package.json
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Use monorepo build commands (`yarn build:react-native:deps`, `yarn build:react-native:sdk`) to ensure proper dependency ordering when working with workspace packages
Applied to files:
sample-apps/react-native/dogfood/package.json
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/**/*.{ts,tsx} : Use Platform.OS checks and conditional imports to gate platform-specific code (iOS, Android, web) and prevent importing platform-specific libraries on unsupported platforms
Applied to files:
sample-apps/react-native/dogfood/package.jsonpackages/react-native-sdk/src/index.ts
📚 Learning: 2026-01-09T11:08:06.856Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:08:06.856Z
Learning: Applies to packages/react-sdk/src/components/**/*.{ts,tsx} : All public components should be exported from src/components/index.ts and have their own directory with index.ts
Applied to files:
packages/react-native-sdk/src/index.ts
📚 Learning: 2026-01-09T11:08:35.312Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T11:08:35.312Z
Learning: Applies to packages/{client,react-sdk,react-native-sdk}/src/**/*.ts?(x) : Gate internal debug logging behind an environment flag; no console noise in production builds
Applied to files:
packages/react-native-sdk/src/index.ts
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/utils/**/*.ts : Implement React Native native modules using `NativeModules` API and always wrap calls in try-catch blocks to handle promise rejection from native code
Applied to files:
packages/react-native-sdk/src/index.ts
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/utils/push/**/*.ts : Safely detect optional library availability using helper functions like `getFirebaseMessagingLibNoThrow()` and `getExpoNotificationsLib()` before accessing optional peer dependencies
Applied to files:
packages/react-native-sdk/src/index.ts
🧬 Code graph analysis (5)
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.ts (2)
packages/client/src/types.ts (1)
StreamRNVideoSDKGlobals(333-350)packages/react-native-sdk/src/modules/call-manager/index.ts (1)
callManager(5-5)
sample-apps/react-native/dogfood/src/screens/LiveStream/ViewLiveStream.tsx (1)
packages/react-native-sdk/src/modules/call-manager/index.ts (1)
callManager(5-5)
packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/audio/utils/AudioFocusUtil.kt (2)
packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/audio/AudioDeviceManager.kt (1)
setup(116-125)packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/callmanager/StreamInCallManagerModule.kt (1)
setup(94-97)
packages/react-native-sdk/src/index.ts (2)
packages/react-native-sdk/__mocks__/@stream-io/react-native-webrtc.tsx (1)
registerGlobals(19-19)packages/react-native-sdk/src/utils/internal/registerSDKGlobals.ts (1)
registerSDKGlobals(22-26)
packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/audio/AudioDeviceManager.kt (2)
packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/audio/utils/AudioFocusUtil.kt (2)
setup(37-41)setup(43-48)packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/callmanager/StreamInCallManagerModule.kt (1)
setup(94-97)
🪛 SwiftLint (0.57.0)
packages/react-native-sdk/ios/StreamInCallManager.swift
[Warning] 95-95: TODOs should be resolved (for stereo we should disallow ...)
(todo)
[Error] 423-423: Force casts should be avoided
(force_cast)
🔇 Additional comments (21)
sample-apps/react-native/dogfood/src/screens/LiveStream/ViewLiveStream.tsx (1)
45-50: Confirm callManager lifecycle ownership (singleton start/stop).Given
callManageris a singleton, unmounting this screen callscallManager.stop()unconditionally; please confirm this can’t tear down audio for another active call/session in the app.packages/react-native-sdk/ios/StreamInCallManager.m (1)
10-13: iOS native selector parity confirmed—implementations match across all layers.The
setEnableStereoAudioOutput:(BOOL)enableandsetupextern declarations correctly map to their Swift@objcimplementations and TypeScript interface definitions. No signature mismatches or runtime failures expected.packages/react-native-sdk/src/modules/call-manager/types.ts (1)
18-26: LGTM! Clean API addition for stereo audio configuration.The optional
enableStereoAudioOutputfield is well-placed in the listener branch of the union type, with clear naming and appropriate typing for enabling high-quality stereo audio output.packages/client/src/Call.ts (2)
347-350: Verify error handling for callManager.setup().The call to
globalThis.streamRNVideoSDK?.callManager.setup()happens early in the setup flow, which is correct. However, if the native setup can throw errors (e.g., permission issues, audio system initialization failures), those exceptions would propagate and potentially prevent the Call from initializing.Consider wrapping this in a try-catch block if native setup can fail:
🛡️ Defensive error handling
- globalThis.streamRNVideoSDK?.callManager.setup(); + try { + globalThis.streamRNVideoSDK?.callManager.setup(); + } catch (err) { + this.logger.warn('Failed to setup native call manager', err); + }
596-666: Lifecycle hook placement looks correct.The
callManager.stop()call is appropriately placed after core cleanup (stats, subscribers, publisher, SFU client) and before device disposal, ensuring the native audio session is properly deactivated after the call infrastructure is torn down.Consistent with the setup() comment, consider adding error handling if native stop can fail.
packages/client/src/types.ts (1)
333-354: LGTM! Clean global API surface for React Native SDK integration.The
StreamRNVideoSDKGlobalstype provides a clear, minimal interface for lifecycle management with well-documented methods. The global declaration follows TypeScript conventions and maintains type safety while allowing the Call class to optionally integrate with platform-specific audio management.As per coding guidelines, this makes the public API surface explicit with TypeScript types.
packages/react-native-sdk/src/modules/call-manager/native-module.d.ts (2)
54-57: LGTM! Interface matches native implementations.The
setup()method is correctly typed and documented. The signature aligns with the Android (StreamInCallManagerModule.kt) and iOS (StreamInCallManager.swift) native implementations.Based on learnings and coding guidelines.
74-78: LGTM! Stereo audio control interface is well-defined.The
setEnableStereoAudioOutput()method signature and documentation are clear. The boolean parameter type matches the native implementations on both platforms.Based on learnings and coding guidelines.
packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/audio/AudioDeviceManager.kt (4)
92-92: LGTM! Stereo flag is properly initialized.The
enableStereoproperty is correctly declared with a safe default value offalse. It's accessed from the audio thread via the ReactMethod wrapper in StreamInCallManagerModule.
129-129: LGTM! Clean lifecycle separation.The
setup()call at the start of the audio thread execution properly centralizes audio configuration before activation. This separates concerns:setup()configures the session,start()activates it.
147-160: Verify Activity null handling coordination.The
stop()method now requires a non-nullActivityparameter to resetvolumeControlStream(line 158). In StreamInCallManagerModule.kt (lines 118-120),stop()is only called ifcurrentActivityis non-null. Ensure this coordination is intentional and that skippingstop()when activity is null doesn't cause audio session leaks or resource cleanup issues.As per coding guidelines: Check instance IDs and timestamps before state updates to avoid race conditions in async operations.
116-125: Use theenableStereoproperty in thesetup()method to match iOS implementation.The
enableStereoproperty is set viasetEnableStereoAudioOutput()but never used in thesetup()method. iOS explicitly callssetStereoPlayoutPreference(true)when stereo is enabled, but the Android implementation leaves the property unused. Either the Android setup should apply stereo configuration (similar to iOS), or the property should be removed if stereo is handled solely at the SDP level. Verify whether Android needs an equivalent tosetStereoPlayoutPreference()or confirm that stereo output is controlled only through SDP negotiation.⛔ Skipped due to learnings
Learnt from: CR Repo: GetStream/stream-video-js PR: 0 File: packages/client/CLAUDE.md:0-0 Timestamp: 2026-01-09T11:07:06.761Z Learning: Applies to packages/client/**/rtc/helpers/sdp.ts : Use removeCodecsExcept() helper for codec selection and enableStereo() for high-quality audio in music/DJ applicationsLearnt from: CR Repo: GetStream/stream-video-js PR: 0 File: packages/react-native-sdk/CLAUDE.md:0-0 Timestamp: 2026-01-09T11:07:38.535Z Learning: Applies to packages/react-native-sdk/src/providers/StreamCall/**/*.tsx : Disable local video in background on iOS to save battery, but maintain audio connection for ongoing calls using AVAudioSession background modesLearnt from: CR Repo: GetStream/stream-video-js PR: 0 File: packages/react-sdk/CLAUDE.md:0-0 Timestamp: 2026-01-09T11:08:06.856Z Learning: Applies to packages/react-sdk/**/*.{ts,tsx} : Never play local participant audio; always check !participant.isLocalParticipant before rendering Audio componentsLearnt from: CR Repo: GetStream/stream-video-js PR: 0 File: packages/react-native-sdk/CLAUDE.md:0-0 Timestamp: 2026-01-09T11:07:38.535Z Learning: Applies to packages/react-native-sdk/src/**/*.tsx : Always request media permissions via `usePermissionRequest()` hook before calling device enable methods like `call.camera.enable()` or `call.microphone.enable()`packages/react-native-sdk/src/index.ts (1)
32-32: LGTM! Call manager module is properly exported.The export follows the project pattern of exposing public SDK APIs from the main entry point. This allows SDK consumers to access the call manager functionality.
Based on coding guidelines: Export all public SDK APIs from the main
src/index.tsfile.packages/react-native-sdk/ios/StreamInCallManager.swift (8)
27-27: LGTM! Stereo flag is properly encapsulated.The
enableStereoprivate property with a default offalseprovides safe state management, controlled exclusively through the publicsetEnableStereoAudioOutput()method.
37-37: LGTM! Debounce work item is properly declared.The optional
stereoRefreshWorkItemprovides clean debouncing for stereo refresh operations. It's properly cancelled during cleanup instop()(lines 184-186).
70-79: LGTM! Stereo control is thread-safe and properly guarded.The
setEnableStereoAudioOutput()implementation correctly:
- Dispatches on
audioSessionQueuefor thread safety- Guards against changes after audio manager activation
- Mirrors the Android implementation pattern
140-163: LGTM! Clean lifecycle flow with proper error handling.The
setup()integration follows the correct sequence: configure → update UI → activate. TheRTCAudioSessionlocking/unlocking pattern withdeferensures proper cleanup even on error paths.
184-186: LGTM! Proper cleanup of debounced work.Cancelling and nilling the
stereoRefreshWorkItemprevents execution of stale stereo refresh operations after the call manager stops.DispatchWorkItem.cancel()is thread-safe, so calling it outside the audio queue is acceptable.
233-269: LGTM! Enhanced logging aids debugging.The expanded
logAudioState()provides comprehensive diagnostics by including both native AVAudioSession state and WebRTC's RTCAudioSession wrapper state. This will be valuable for debugging stereo output and audio routing issues.
347-376: LGTM! Proper debouncing for stereo refresh.The debounced stereo refresh pattern is well-implemented:
- Cancels pending refreshes to avoid redundant work
- Uses
[weak self]to prevent retain cycles- 2-second delay allows the audio route to stabilize
- Only triggers on
.routeConfigurationChange, not all route changesThe
refreshStereoPlayoutState()call ensures stereo output remains correctly configured when audio devices change (e.g., connecting/disconnecting headphones or bluetooth).
438-459: LGTM! Comprehensive route change reason mapping.The
routeChangeReasonDescription()helper provides clear, human-readable strings for allAVAudioSession.RouteChangeReasoncases. The@unknown defaultcase gracefully handles future enum additions.
...t-native-sdk/android/src/main/java/com/streamvideo/reactnative/audio/utils/AudioFocusUtil.kt
Show resolved
Hide resolved
...k/android/src/main/java/com/streamvideo/reactnative/callmanager/StreamInCallManagerModule.kt
Show resolved
Hide resolved
...k/android/src/main/java/com/streamvideo/reactnative/callmanager/StreamInCallManagerModule.kt
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In @packages/react-native-sdk/src/utils/internal/registerSDKGlobals.ts:
- Line 5: The StreamInCallManagerNativeModule assignment and subsequent uses
(e.g., any calls to StreamInCallManagerNativeModule methods between the const
declaration and the code around lines 13-18) must guard against the native
module being undefined and wrap calls in try-catch: first check that
StreamInCallManagerNativeModule is not null/undefined before invoking its
methods and log an error or no-op if missing, and for every call to
StreamInCallManagerNativeModule.* wrap the invocation in a try-catch to handle
and log exceptions rather than allowing a crash.
🧹 Nitpick comments (1)
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.ts (1)
19-24: Add error handling for callManager lifecycle methods.If
callManager.start()orcallManager.stop()throw, the error will propagate and potentially break the join/leave flow inCall.ts.♻️ Proposed defensive wrapping
start: () => { + try { callManager.start(); + } catch (error) { + console.warn('Failed to start call manager', error); + } }, stop: () => { + try { callManager.stop(); + } catch (error) { + console.warn('Failed to stop call manager', error); + } },
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (5)
packages/client/src/Call.tspackages/client/src/devices/SpeakerManager.tspackages/client/src/types.tspackages/react-native-sdk/src/modules/call-manager/CallManager.tspackages/react-native-sdk/src/utils/internal/registerSDKGlobals.ts
💤 Files with no reviewable changes (1)
- packages/client/src/devices/SpeakerManager.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- packages/react-native-sdk/src/modules/call-manager/CallManager.ts
- packages/client/src/types.ts
🧰 Additional context used
📓 Path-based instructions (9)
packages/react-native-sdk/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (packages/react-native-sdk/CLAUDE.md)
packages/react-native-sdk/src/**/*.{ts,tsx}: Use React hooks from@stream-io/video-react-bindings(viauseCall()anduseCallStateHooks()) instead of directly accessing RxJS observables from@stream-io/video-client
Use Platform.OS checks and conditional imports to gate platform-specific code (iOS, Android, web) and prevent importing platform-specific libraries on unsupported platforms
Files:
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.ts
packages/react-native-sdk/src/utils/**/*.ts
📄 CodeRabbit inference engine (packages/react-native-sdk/CLAUDE.md)
Implement React Native native modules using
NativeModulesAPI and always wrap calls in try-catch blocks to handle promise rejection from native code
Files:
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.ts
packages/{client,react-sdk,react-native-sdk}/src/**/*.ts?(x)
📄 CodeRabbit inference engine (AGENTS.md)
packages/{client,react-sdk,react-native-sdk}/src/**/*.ts?(x): Use TypeScript for all source code in packages/client, packages/react-sdk, and packages/react-native-sdk
Mark deprecated APIs with @deprecated JSDoc, including rationale and replacement guidance
Throw descriptive errors or return typed error results consistently with existing patterns in public APIs
Gate internal debug logging behind an environment flag; no console noise in production builds
Never leak credentials or user data in error messages or logs
Check instance IDs and timestamps before state updates to avoid race conditions in async operations
Make public API surfaces explicit with TypeScript types and interfaces
Files:
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.tspackages/client/src/Call.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx,js,jsx}: Use camelCase for function and property names
Narrowly scope eslint-disable comments with inline explanatory comments and rationale
Files:
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.tspackages/client/src/Call.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use PascalCase for component and type names
Files:
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.tspackages/client/src/Call.ts
**/*.{sh,js,ts}
📄 CodeRabbit inference engine (AGENTS.md)
Make scripts error on missing critical environment variables
Files:
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.tspackages/client/src/Call.ts
packages/client/**/Call.ts
📄 CodeRabbit inference engine (packages/client/CLAUDE.md)
packages/client/**/Call.ts: Use withoutConcurrency for serial execution in join/leave operations and critical state changes; use withCancellation for device toggling and switching operations
Use PermissionsContext.canPublish(trackType) and hasPermission() methods to gate operations based on user permissions before publishing or performing actions
Use distinct tag symbols for different concurrency operations to ensure operations with same tag run serially while different tags run in parallel; use settled(tag) to wait for completion
Files:
packages/client/src/Call.ts
packages/client/**/*.ts
📄 CodeRabbit inference engine (packages/client/CLAUDE.md)
packages/client/**/*.ts: Implement scoped loggers with configurable log levels (debug, info, warn, error) for each component for easier debugging
Use helper functions isReactNative(), isSafari(), isFirefox(), isMobileSafari() to detect platform differences and apply browser/platform-specific quirks
Files:
packages/client/src/Call.ts
packages/client/src/**/*.ts?(x)
📄 CodeRabbit inference engine (AGENTS.md)
Avoid unguarded web-only APIs in shared code between React and React Native
Files:
packages/client/src/Call.ts
🧠 Learnings (20)
📓 Common learnings
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/providers/StreamVideo.tsx : Use `StreamVideoRN.configure()` static method to set up SDK configuration including foreground service, push notification providers, and notification callbacks before mounting StreamVideo provider
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/providers/StreamCall/**/*.tsx : Disable local video in background on iOS to save battery, but maintain audio connection for ongoing calls using AVAudioSession background modes
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/ios/**/*.swift : When adding iOS native functionality, create Swift file in `ios/`, create Objective-C bridge with `RCT_EXTERN_METHOD`, expose via `StreamVideoReactNative.swift`, and call from TypeScript via `NativeModules.StreamVideoReactNative`
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/index.ts : Always call `registerGlobals()` from `stream-io/react-native-webrtc` before using WebRTC on non-web platforms in the React Native SDK entry point
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/**/*.kt : When adding Android native functionality, create Kotlin file in `android/src/main/java/com/streamvideo/reactnative/`, use `ReactMethod` annotation, register in `StreamVideoReactNativePackage.kt`, and call from TypeScript via `NativeModules.StreamVideoReactNative`
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/**/*.{ts,tsx} : Use React hooks from `stream-io/video-react-bindings` (via `useCall()` and `useCallStateHooks()`) instead of directly accessing RxJS observables from `stream-io/video-client`
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Do not instantiate multiple `StreamVideo` providers at the same hierarchy level; use only one provider at app root and nest `StreamCall` providers under it
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/index.ts : Always call `registerGlobals()` from `stream-io/react-native-webrtc` before using WebRTC on non-web platforms in the React Native SDK entry point
Applied to files:
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.tspackages/client/src/Call.ts
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/ios/**/*.swift : When adding iOS native functionality, create Swift file in `ios/`, create Objective-C bridge with `RCT_EXTERN_METHOD`, expose via `StreamVideoReactNative.swift`, and call from TypeScript via `NativeModules.StreamVideoReactNative`
Applied to files:
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.ts
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/index.ts : Export all public SDK APIs (components, hooks, providers, types) from the main `src/index.ts` file, with re-exports from client and bindings packages and SDK-specific implementations
Applied to files:
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.ts
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/version.ts : Store generated version information in `src/version.ts` (auto-generated via `yarn copy-version` script) and export for SDK consumers
Applied to files:
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.ts
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/**/*.kt : When adding Android native functionality, create Kotlin file in `android/src/main/java/com/streamvideo/reactnative/`, use `ReactMethod` annotation, register in `StreamVideoReactNativePackage.kt`, and call from TypeScript via `NativeModules.StreamVideoReactNative`
Applied to files:
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.ts
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/**/*.{ts,tsx} : Use React hooks from `stream-io/video-react-bindings` (via `useCall()` and `useCallStateHooks()`) instead of directly accessing RxJS observables from `stream-io/video-client`
Applied to files:
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.tspackages/client/src/Call.ts
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/providers/StreamVideo.tsx : Use `StreamVideoRN.configure()` static method to set up SDK configuration including foreground service, push notification providers, and notification callbacks before mounting StreamVideo provider
Applied to files:
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.tspackages/client/src/Call.ts
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/modules/**/*.ts : Structure native module TypeScript interfaces in `src/modules/` to match the exact signatures of their native implementations (Android Kotlin and iOS Swift), including parameter types and return types
Applied to files:
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.ts
📚 Learning: 2026-01-09T11:08:35.312Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T11:08:35.312Z
Learning: Applies to packages/{client,react-sdk,react-native-sdk}/src/**/*.ts?(x) : Make public API surfaces explicit with TypeScript types and interfaces
Applied to files:
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.ts
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/theme/**/*.ts : Apply deep partial theme overrides using the `DeepPartial<Theme>` type when customizing StreamVideo provider theme to maintain type safety
Applied to files:
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.ts
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/providers/StreamCall/**/*.tsx : Disable local video in background on iOS to save battery, but maintain audio connection for ongoing calls using AVAudioSession background modes
Applied to files:
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.tspackages/client/src/Call.ts
📚 Learning: 2026-01-09T11:07:06.761Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/client/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:06.761Z
Learning: Applies to packages/client/**/Call.ts : Use withoutConcurrency for serial execution in join/leave operations and critical state changes; use withCancellation for device toggling and switching operations
Applied to files:
packages/client/src/Call.ts
📚 Learning: 2026-01-09T11:07:06.761Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/client/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:06.761Z
Learning: Applies to packages/client/**/*{CallState,stateStore}.ts : Use RxJS observables (BehaviorSubject) for state management and always update state through .next(), never mutate state directly
Applied to files:
packages/client/src/Call.ts
📚 Learning: 2026-01-09T11:07:06.761Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/client/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:06.761Z
Learning: Applies to packages/client/**/callEventHandlers.ts : Register Coordinator event handlers in callEventHandlers.ts to wire up backend events to state updates; distinguish from SFU events using isSfuEvent()
Applied to files:
packages/client/src/Call.ts
📚 Learning: 2026-01-09T11:07:06.761Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/client/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:06.761Z
Learning: Applies to packages/client/**/{CallState,Subscriber}.ts : Use sessionId as the unique identifier for participants, not userId; use trackLookupPrefix for track lookups combining participant ID and track type
Applied to files:
packages/client/src/Call.ts
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/utils/push/setupIosVoipPushEvents.ts : Implement VoIP push display logic to call `CallKeep.displayIncomingCall()` within 30 seconds of receiving VoIP push notification on iOS, or the system will terminate the app
Applied to files:
packages/client/src/Call.ts
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/hooks/**/*.ts : Handle iOS backgrounding by disabling video tracks to prevent battery drain when `AppState` changes to 'background', while keeping audio enabled
Applied to files:
packages/client/src/Call.ts
📚 Learning: 2026-01-09T11:08:06.856Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:08:06.856Z
Learning: Applies to packages/react-sdk/src/hooks/usePersistedDevicePreferences.ts : Handle device selection state by checking if devices are available before use; use usePersistedDevicePreferences at app level
Applied to files:
packages/client/src/Call.ts
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/**/*.tsx : Always request media permissions via `usePermissionRequest()` hook before calling device enable methods like `call.camera.enable()` or `call.microphone.enable()`
Applied to files:
packages/client/src/Call.ts
🧬 Code graph analysis (2)
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.ts (2)
packages/client/src/types.ts (1)
StreamRNVideoSDKGlobals(334-355)packages/react-native-sdk/src/modules/call-manager/index.ts (1)
callManager(5-5)
packages/client/src/Call.ts (2)
packages/client/src/store/CallState.ts (1)
settings(938-940)packages/client/src/devices/ScreenShareState.ts (1)
settings(59-61)
🔇 Additional comments (4)
packages/client/src/Call.ts (3)
664-665: LGTM! Call manager cleanup is properly placed in the leave flow.The
stop()call is correctly positioned after call state cleanup and before device disposal, ensuring the audio session is deactivated before devices are released.
1114-1114: LGTM! Call manager start is properly gated.The
start()call is correctly placed after device configuration succeeds and protected by thedeviceSettingsAppliedOnceguard, preventing repeated activations on reconnections.
2657-2659: LGTM! Audio session setup precedes device configuration.The
setup()call correctly configures the native audio session with backend settings before applying camera/microphone device configuration. The optional chaining ensures this is a no-op on non-RN platforms.packages/react-native-sdk/src/utils/internal/registerSDKGlobals.ts (1)
28-31: LGTM! Global registration pattern is appropriate.The guard against re-registration (
if (!global.streamRNVideoSDK)) ensures the globals are only set once, preventing potential issues with multiple registrations.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In @packages/react-native-sdk/src/modules/call-manager/CallManager.ts:
- Around line 103-105: The stereo flag is only set true but never cleared, so
update CallManager (around the block that checks config?.audioRole ===
'listener') to explicitly call NativeManager.setEnableStereoAudioOutput(false)
when starting in listener role without enableStereoAudioOutput; alternatively
ensure the native modules' stop() implementations reset their stored stereo
state. Specifically modify the logic that currently only calls
setEnableStereoAudioOutput(true) to call false when
config.enableStereoAudioOutput is falsy, or add resets in the native stop()
methods to clear the instance stereo flag.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (7)
packages/react-native-sdk/src/index.tspackages/react-native-sdk/src/modules/call-manager/CallManager.tssample-apps/react-native/dogfood/ios/AppDelegate.swiftsample-apps/react-native/dogfood/src/components/ActiveCall.tsxsample-apps/react-native/dogfood/src/navigators/Call.tsxsample-apps/react-native/dogfood/src/screens/AudioRoom/Room.tsxsample-apps/react-native/dogfood/src/screens/LiveStream/HostLiveStream.tsx
💤 Files with no reviewable changes (4)
- sample-apps/react-native/dogfood/src/navigators/Call.tsx
- sample-apps/react-native/dogfood/src/components/ActiveCall.tsx
- sample-apps/react-native/dogfood/src/screens/LiveStream/HostLiveStream.tsx
- sample-apps/react-native/dogfood/src/screens/AudioRoom/Room.tsx
🚧 Files skipped from review as they are similar to previous changes (2)
- sample-apps/react-native/dogfood/ios/AppDelegate.swift
- packages/react-native-sdk/src/index.ts
🧰 Additional context used
📓 Path-based instructions (6)
packages/react-native-sdk/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (packages/react-native-sdk/CLAUDE.md)
packages/react-native-sdk/src/**/*.{ts,tsx}: Use React hooks from@stream-io/video-react-bindings(viauseCall()anduseCallStateHooks()) instead of directly accessing RxJS observables from@stream-io/video-client
Use Platform.OS checks and conditional imports to gate platform-specific code (iOS, Android, web) and prevent importing platform-specific libraries on unsupported platforms
Files:
packages/react-native-sdk/src/modules/call-manager/CallManager.ts
packages/react-native-sdk/src/modules/**/*.ts
📄 CodeRabbit inference engine (packages/react-native-sdk/CLAUDE.md)
Structure native module TypeScript interfaces in
src/modules/to match the exact signatures of their native implementations (Android Kotlin and iOS Swift), including parameter types and return types
Files:
packages/react-native-sdk/src/modules/call-manager/CallManager.ts
packages/{client,react-sdk,react-native-sdk}/src/**/*.ts?(x)
📄 CodeRabbit inference engine (AGENTS.md)
packages/{client,react-sdk,react-native-sdk}/src/**/*.ts?(x): Use TypeScript for all source code in packages/client, packages/react-sdk, and packages/react-native-sdk
Mark deprecated APIs with @deprecated JSDoc, including rationale and replacement guidance
Throw descriptive errors or return typed error results consistently with existing patterns in public APIs
Gate internal debug logging behind an environment flag; no console noise in production builds
Never leak credentials or user data in error messages or logs
Check instance IDs and timestamps before state updates to avoid race conditions in async operations
Make public API surfaces explicit with TypeScript types and interfaces
Files:
packages/react-native-sdk/src/modules/call-manager/CallManager.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx,js,jsx}: Use camelCase for function and property names
Narrowly scope eslint-disable comments with inline explanatory comments and rationale
Files:
packages/react-native-sdk/src/modules/call-manager/CallManager.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use PascalCase for component and type names
Files:
packages/react-native-sdk/src/modules/call-manager/CallManager.ts
**/*.{sh,js,ts}
📄 CodeRabbit inference engine (AGENTS.md)
Make scripts error on missing critical environment variables
Files:
packages/react-native-sdk/src/modules/call-manager/CallManager.ts
🧠 Learnings (4)
📓 Common learnings
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/client/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:06.761Z
Learning: Applies to packages/client/**/rtc/helpers/sdp.ts : Use removeCodecsExcept() helper for codec selection and enableStereo() for high-quality audio in music/DJ applications
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/providers/StreamCall/**/*.tsx : Disable local video in background on iOS to save battery, but maintain audio connection for ongoing calls using AVAudioSession background modes
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/providers/StreamVideo.tsx : Use `StreamVideoRN.configure()` static method to set up SDK configuration including foreground service, push notification providers, and notification callbacks before mounting StreamVideo provider
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Do not instantiate multiple `StreamVideo` providers at the same hierarchy level; use only one provider at app root and nest `StreamCall` providers under it
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/providers/StreamCall/**/*.tsx : Disable local video in background on iOS to save battery, but maintain audio connection for ongoing calls using AVAudioSession background modes
Applied to files:
packages/react-native-sdk/src/modules/call-manager/CallManager.ts
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/**/*.tsx : Always request media permissions via `usePermissionRequest()` hook before calling device enable methods like `call.camera.enable()` or `call.microphone.enable()`
Applied to files:
packages/react-native-sdk/src/modules/call-manager/CallManager.ts
📚 Learning: 2026-01-09T11:07:06.761Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/client/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:06.761Z
Learning: Applies to packages/client/**/rtc/helpers/sdp.ts : Use removeCodecsExcept() helper for codec selection and enableStereo() for high-quality audio in music/DJ applications
Applied to files:
packages/react-native-sdk/src/modules/call-manager/CallManager.ts
🧬 Code graph analysis (1)
packages/react-native-sdk/src/modules/call-manager/CallManager.ts (1)
packages/react-native-sdk/src/modules/call-manager/types.ts (1)
StreamInCallManagerConfig(18-26)
🔇 Additional comments (1)
packages/react-native-sdk/src/modules/call-manager/CallManager.ts (1)
94-95: LGTM!The JSDoc correctly documents the new
enableStereoAudioOutputparameter and its scope limitation to the listener audio role, matching the discriminated union type definition.
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/callmanager/StreamInCallManagerModule.kt (1)
115-128: State inconsistency whencurrentActivityis null.If
currentActivityis null whenstop()is called, theaudioManagerActivatedflag remainstruewhile microphone mute and screen flags are still reset. This leaves the module in an inconsistent state where:
- The audio manager isn't actually stopped
audioManagerActivatedstaystrue, preventing futurestart()calls- Side effects (mute/screen flags) are still cleared
Suggested fix: Move flag reset outside the let block or handle the null case
@ReactMethod fun stop() { AudioDeviceManager.runInAudioThread { if (audioManagerActivated) { Log.d(TAG, "stop() mAudioDeviceManager") - reactApplicationContext.currentActivity?.let { - mAudioDeviceManager.stop(it) - audioManagerActivated = false + val activity = reactApplicationContext.currentActivity + if (activity != null) { + mAudioDeviceManager.stop(activity) + } else { + Log.w(TAG, "stop(): currentActivity is null, stopping without activity context") + // Consider: mAudioDeviceManager.stopWithoutActivity() or similar } + audioManagerActivated = false setMicrophoneMute(false) setKeepScreenOn(false) } } }packages/react-native-sdk/ios/StreamInCallManager.swift (1)
352-381: Debounce logic is sound, butstereoRefreshWorkItemaccess needs synchronization.The 2-second debounce for
routeConfigurationChangeis a reasonable approach to let iOS audio routing settle. However,stereoRefreshWorkItemis accessed directly in this notification callback (which may fire on arbitrary threads) without synchronization. This relates to the race condition noted instop().Consider wrapping the debounce logic in
audioSessionQueue.async:🔧 Proposed fix
if reason == .routeConfigurationChange { - // Cancel any pending debounced refresh - stereoRefreshWorkItem?.cancel() - // Create a new debounced work item - let workItem = DispatchWorkItem { [weak self] in - self?.getAudioDeviceModule().refreshStereoPlayoutState() - self?.log("Executed debounced refreshStereoPlayoutState") - } - stereoRefreshWorkItem = workItem - // Schedule the work item after 2 seconds - audioSessionQueue.asyncAfter(deadline: .now() + 2.0, execute: workItem) + audioSessionQueue.async { [weak self] in + guard let self = self else { return } + // Cancel any pending debounced refresh + self.stereoRefreshWorkItem?.cancel() + // Create a new debounced work item + let workItem = DispatchWorkItem { [weak self] in + self?.getAudioDeviceModule().refreshStereoPlayoutState() + self?.log("Executed debounced refreshStereoPlayoutState") + } + self.stereoRefreshWorkItem = workItem + // Schedule the work item after 2 seconds + self.audioSessionQueue.asyncAfter(deadline: .now() + 2.0, execute: workItem) + } }
🤖 Fix all issues with AI agents
In
@packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/callmanager/StreamInCallManagerModule.kt:
- Around line 94-99: The setup() method is missing the audioManagerActivated
guard used by other configuration methods; update
StreamInCallManagerModule.setup to check the audioManagerActivated flag before
calling AudioDeviceManager.runInAudioThread and return early if activation has
already occurred (matching the pattern used in setAudioRole,
setDefaultAudioDeviceEndpointType, setEnableStereoAudioOutput); perform the
guard in setup(), then call mAudioDeviceManager.setup() inside
AudioDeviceManager.runInAudioThread only when not activated to ensure consistent
API behavior.
In @packages/react-native-sdk/ios/StreamInCallManager.swift:
- Around line 184-186: Access to stereoRefreshWorkItem is not synchronized here
and can race with handleAudioRouteChange which uses audioSessionQueue; move the
two lines that cancel and nil out stereoRefreshWorkItem so they run inside the
same audioSessionQueue.async block used for audio session state changes (i.e.,
perform stereoRefreshWorkItem?.cancel() and stereoRefreshWorkItem = nil within
audioSessionQueue.async) to ensure thread-safe access consistent with
handleAudioRouteChange.
- Around line 81-132: setup() reads shared mutable properties (callAudioRole,
defaultAudioDevice, enableStereo) without synchronization while setters
(setAudioRole(), setDefaultAudioDeviceEndpointType(),
setEnableStereoAudioOutput()) modify them on audioSessionQueue, creating a data
race; to fix, wrap the entire body of setup() in audioSessionQueue.sync { ... }
so all reads occur on the same serial queue used by the setters, preserving
current behavior and thread-safety when accessing callAudioRole,
defaultAudioDevice, enableStereo and when configuring RTCAudioSession.
🧹 Nitpick comments (2)
packages/react-native-sdk/ios/StreamInCallManager.swift (2)
95-97: Address or track the TODO comment.The static analysis tool flagged this TODO. The comment indicates that stereo mode should disallow BluetoothHFP and only allow BluetoothA2DP (matching iOS native SDK behavior), but this currently fails with OSStatus error -50.
Consider either resolving this limitation or creating a tracking issue if it's a known platform constraint.
Would you like me to open an issue to track investigating the BluetoothA2DP-only option for stereo mode?
427-437: Consider whetherfatalErroris appropriate for an SDK.Using
fatalErrorwill crash the host app if the bridge or WebRTCModule is unavailable. While this guards against programming errors (the module should always be registered), crashing may be undesirable for a library SDK.If this is intentional as a precondition check (the SDK cannot function without WebRTCModule), this is acceptable. Otherwise, consider returning an optional or using a softer failure mode with logging.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/callmanager/StreamInCallManagerModule.ktpackages/react-native-sdk/ios/StreamInCallManager.swift
🧰 Additional context used
📓 Path-based instructions (2)
packages/react-native-sdk/ios/**/*.swift
📄 CodeRabbit inference engine (packages/react-native-sdk/CLAUDE.md)
When adding iOS native functionality, create Swift file in
ios/, create Objective-C bridge withRCT_EXTERN_METHOD, expose viaStreamVideoReactNative.swift, and call from TypeScript viaNativeModules.StreamVideoReactNative
Files:
packages/react-native-sdk/ios/StreamInCallManager.swift
packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/**/*.kt
📄 CodeRabbit inference engine (packages/react-native-sdk/CLAUDE.md)
When adding Android native functionality, create Kotlin file in
android/src/main/java/com/streamvideo/reactnative/, use@ReactMethodannotation, register inStreamVideoReactNativePackage.kt, and call from TypeScript viaNativeModules.StreamVideoReactNative
Files:
packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/callmanager/StreamInCallManagerModule.kt
🧠 Learnings (8)
📓 Common learnings
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/providers/StreamVideo.tsx : Use `StreamVideoRN.configure()` static method to set up SDK configuration including foreground service, push notification providers, and notification callbacks before mounting StreamVideo provider
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/providers/StreamCall/**/*.tsx : Disable local video in background on iOS to save battery, but maintain audio connection for ongoing calls using AVAudioSession background modes
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Do not instantiate multiple `StreamVideo` providers at the same hierarchy level; use only one provider at app root and nest `StreamCall` providers under it
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/**/*.{ts,tsx} : Use React hooks from `stream-io/video-react-bindings` (via `useCall()` and `useCallStateHooks()`) instead of directly accessing RxJS observables from `stream-io/video-client`
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/providers/StreamCall/**/*.tsx : Disable local video in background on iOS to save battery, but maintain audio connection for ongoing calls using AVAudioSession background modes
Applied to files:
packages/react-native-sdk/ios/StreamInCallManager.swiftpackages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/callmanager/StreamInCallManagerModule.kt
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/ios/**/*.swift : When adding iOS native functionality, create Swift file in `ios/`, create Objective-C bridge with `RCT_EXTERN_METHOD`, expose via `StreamVideoReactNative.swift`, and call from TypeScript via `NativeModules.StreamVideoReactNative`
Applied to files:
packages/react-native-sdk/ios/StreamInCallManager.swift
📚 Learning: 2026-01-09T11:07:06.761Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/client/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:06.761Z
Learning: Applies to packages/client/**/rtc/helpers/sdp.ts : Use removeCodecsExcept() helper for codec selection and enableStereo() for high-quality audio in music/DJ applications
Applied to files:
packages/react-native-sdk/ios/StreamInCallManager.swift
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/index.ts : Always call `registerGlobals()` from `stream-io/react-native-webrtc` before using WebRTC on non-web platforms in the React Native SDK entry point
Applied to files:
packages/react-native-sdk/ios/StreamInCallManager.swiftpackages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/callmanager/StreamInCallManagerModule.kt
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/**/*.tsx : Always request media permissions via `usePermissionRequest()` hook before calling device enable methods like `call.camera.enable()` or `call.microphone.enable()`
Applied to files:
packages/react-native-sdk/ios/StreamInCallManager.swiftpackages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/callmanager/StreamInCallManagerModule.kt
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/**/*.kt : When adding Android native functionality, create Kotlin file in `android/src/main/java/com/streamvideo/reactnative/`, use `ReactMethod` annotation, register in `StreamVideoReactNativePackage.kt`, and call from TypeScript via `NativeModules.StreamVideoReactNative`
Applied to files:
packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/callmanager/StreamInCallManagerModule.kt
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/hooks/**/*.ts : Handle iOS backgrounding by disabling video tracks to prevent battery drain when `AppState` changes to 'background', while keeping audio enabled
Applied to files:
packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/callmanager/StreamInCallManagerModule.kt
🪛 SwiftLint (0.57.0)
packages/react-native-sdk/ios/StreamInCallManager.swift
[Warning] 95-95: TODOs should be resolved (for stereo we should disallow ...)
(todo)
🔇 Additional comments (10)
packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/callmanager/StreamInCallManagerModule.kt (5)
83-92: LGTM!The implementation correctly follows the established pattern of other configuration methods (
setAudioRole,setDefaultAudioDeviceEndpointType): runs on the audio thread, guards against modification after activation, and logs clearly on invalid state.
144-157: LGTM - Good defensive programming with role validation.The addition of audio thread execution and role validation is appropriate. Speakerphone control is only relevant for
Communicatorrole.Minor note: For enum comparison in Kotlin,
!=(structural equality) is more idiomatic than!==(referential equality), though both work correctly for enum constants.
177-192: LGTM!Consistent implementation with
setForceSpeakerphoneOn(): audio thread execution and role validation for Communicator-only functionality.
194-199: LGTM!Wrapping in
runInAudioThreadensures thread-safe audio operations.
201-206: LGTM!Symmetric implementation with
muteAudioOutput(), ensuring thread-safe execution.packages/react-native-sdk/ios/StreamInCallManager.swift (5)
69-79: LGTM!The method follows the established pattern of other configuration setters in this class, using the
audioSessionQueuefor thread-safe property access and preventing changes after activation.
134-164: LGTM!The
start()method properly callssetup()within theaudioSessionQueue.asyncblock ensuring thread safety. The RTCAudioSession lock/unlock pattern withdeferis correctly implemented.
213-229: LGTM!Good improvement to use
audioSessionQueue.asyncwith[weak self]for thread safety and proper RTCAudioSession locking pattern.
237-274: LGTM!The enhanced logging provides valuable diagnostic information including ADM and RTCAudioSession state. Good use of the lock pattern for safe access.
450-471: LGTM!Clean helper for logging with good use of
@unknown defaultfor forward compatibility with future iOS SDK additions.
...k/android/src/main/java/com/streamvideo/reactnative/callmanager/StreamInCallManagerModule.kt
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
packages/react-native-sdk/ios/StreamInCallManager.swift (2)
95-100: Track the TODO for stereo Bluetooth configuration.The TODO indicates that for proper stereo playback, BluetoothHFP should be disallowed in favor of BluetoothA2DP (as done in the iOS native SDK), but this currently fails with
OSStatus error -50. Consider creating an issue to track this limitation, as it may affect stereo quality over Bluetooth headsets.Would you like me to open an issue to track this Bluetooth stereo configuration limitation?
213-229: Consider whethersetActive(true)is necessary.The session should already be active when
setForceSpeakerphoneOnis called during a call. TheoverrideOutputAudioPortcall alone should be sufficient. If keepingsetActive(true)as a safeguard, consider that it could potentially interfere with other audio apps if the session wasn't actually active.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
packages/react-native-sdk/ios/StreamInCallManager.swift
🧰 Additional context used
📓 Path-based instructions (1)
packages/react-native-sdk/ios/**/*.swift
📄 CodeRabbit inference engine (packages/react-native-sdk/CLAUDE.md)
When adding iOS native functionality, create Swift file in
ios/, create Objective-C bridge withRCT_EXTERN_METHOD, expose viaStreamVideoReactNative.swift, and call from TypeScript viaNativeModules.StreamVideoReactNative
Files:
packages/react-native-sdk/ios/StreamInCallManager.swift
🧠 Learnings (11)
📓 Common learnings
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/providers/StreamCall/**/*.tsx : Disable local video in background on iOS to save battery, but maintain audio connection for ongoing calls using AVAudioSession background modes
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/providers/StreamVideo.tsx : Use `StreamVideoRN.configure()` static method to set up SDK configuration including foreground service, push notification providers, and notification callbacks before mounting StreamVideo provider
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Do not instantiate multiple `StreamVideo` providers at the same hierarchy level; use only one provider at app root and nest `StreamCall` providers under it
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/**/*.{ts,tsx} : Use React hooks from `stream-io/video-react-bindings` (via `useCall()` and `useCallStateHooks()`) instead of directly accessing RxJS observables from `stream-io/video-client`
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/ios/**/*.swift : When adding iOS native functionality, create Swift file in `ios/`, create Objective-C bridge with `RCT_EXTERN_METHOD`, expose via `StreamVideoReactNative.swift`, and call from TypeScript via `NativeModules.StreamVideoReactNative`
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/providers/StreamCall/**/*.tsx : Disable local video in background on iOS to save battery, but maintain audio connection for ongoing calls using AVAudioSession background modes
Applied to files:
packages/react-native-sdk/ios/StreamInCallManager.swift
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/ios/**/*.swift : When adding iOS native functionality, create Swift file in `ios/`, create Objective-C bridge with `RCT_EXTERN_METHOD`, expose via `StreamVideoReactNative.swift`, and call from TypeScript via `NativeModules.StreamVideoReactNative`
Applied to files:
packages/react-native-sdk/ios/StreamInCallManager.swift
📚 Learning: 2026-01-09T11:07:06.761Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/client/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:06.761Z
Learning: Applies to packages/client/**/rtc/helpers/sdp.ts : Use removeCodecsExcept() helper for codec selection and enableStereo() for high-quality audio in music/DJ applications
Applied to files:
packages/react-native-sdk/ios/StreamInCallManager.swift
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/**/*.tsx : Always request media permissions via `usePermissionRequest()` hook before calling device enable methods like `call.camera.enable()` or `call.microphone.enable()`
Applied to files:
packages/react-native-sdk/ios/StreamInCallManager.swift
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/index.ts : Always call `registerGlobals()` from `stream-io/react-native-webrtc` before using WebRTC on non-web platforms in the React Native SDK entry point
Applied to files:
packages/react-native-sdk/ios/StreamInCallManager.swift
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/**/*.{ts,tsx} : Use React hooks from `stream-io/video-react-bindings` (via `useCall()` and `useCallStateHooks()`) instead of directly accessing RxJS observables from `stream-io/video-client`
Applied to files:
packages/react-native-sdk/ios/StreamInCallManager.swift
📚 Learning: 2026-01-09T11:08:35.312Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T11:08:35.312Z
Learning: Applies to packages/{client,react-sdk,react-native-sdk}/src/**/*.ts?(x) : Check instance IDs and timestamps before state updates to avoid race conditions in async operations
Applied to files:
packages/react-native-sdk/ios/StreamInCallManager.swift
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/providers/StreamVideo.tsx : Use `StreamVideoRN.configure()` static method to set up SDK configuration including foreground service, push notification providers, and notification callbacks before mounting StreamVideo provider
Applied to files:
packages/react-native-sdk/ios/StreamInCallManager.swift
📚 Learning: 2026-01-09T11:08:06.856Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:08:06.856Z
Learning: Applies to packages/react-sdk/**/{Video,Audio,ParticipantView}/**/*.{ts,tsx} : Use useLayoutEffect instead of useEffect for video element binding to call.bindVideoElement() to ensure timing before paint
Applied to files:
packages/react-native-sdk/ios/StreamInCallManager.swift
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/hooks/**/*.ts : Handle iOS backgrounding by disabling video tracks to prevent battery drain when `AppState` changes to 'background', while keeping audio enabled
Applied to files:
packages/react-native-sdk/ios/StreamInCallManager.swift
🪛 SwiftLint (0.57.0)
packages/react-native-sdk/ios/StreamInCallManager.swift
[Warning] 95-95: TODOs should be resolved (for stereo we should disallow ...)
(todo)
🔇 Additional comments (6)
packages/react-native-sdk/ios/StreamInCallManager.swift (6)
69-79: LGTM!The
setEnableStereoAudioOutputmethod follows the established pattern of other configuration setters, with proper thread safety viaaudioSessionQueue.asyncand appropriate guard against changes after activation.
134-164: LGTM!The
start()method properly:
- Guards against double activation
- Calls
setup()within the queue context- Uses the lock/unlock pattern with
deferfor RTCAudioSession- Handles UI operations (wake lock, route observer) on the main thread
166-196: LGTM!Good cleanup pattern - cancelling the pending
stereoRefreshWorkItembefore clearingaudioManagerActivatedprevents stale callbacks from executing after the session is stopped.
352-381: LGTM!The debouncing pattern for
refreshStereoPlayoutStateis well-implemented:
- Cancels any pending work before scheduling new work
- Uses
[weak self]to prevent retain cycles- 2-second delay provides stability for rapid route changes
427-437: LGTM!Using
fatalErroris appropriate here since a missing bridge or WebRTCModule represents a fatal integration error that cannot be recovered from. The error messages are clear enough to help developers identify the issue.
450-471: LGTM!Good use of
@unknown defaultto handle potential futureRouteChangeReasoncases added in newer iOS versions.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In @packages/react-native-sdk/ios/StreamInCallManager.swift:
- Around line 428-438: Change getAudioDeviceModule() to return an optional
AudioDeviceModule? instead of crashing: remove the fatalError calls, log a clear
error when bridge is nil or WebRTCModule isn’t found (using NSLog/os_log or the
project logger), and return nil; return webrtcModule.audioDeviceModule as an
optional when present. Update all callers (e.g., sites that call
getAudioDeviceModule().reset()) to safely unwrap or use optional chaining like
getAudioDeviceModule()?.reset() so the code fails gracefully during teardown or
missing-module edge cases.
🧹 Nitpick comments (2)
packages/react-native-sdk/ios/StreamInCallManager.swift (2)
84-86: Consider using regular optionals or direct initialization.Implicitly unwrapped optionals (
!) can be avoided here since both branches of the conditional fully initialize these variables. A cleaner approach would use direct assignment.♻️ Suggested refactor
- let intendedCategory: AVAudioSession.Category! - let intendedMode: AVAudioSession.Mode! - let intendedOptions: AVAudioSession.CategoryOptions! - - let adm = getAudioDeviceModule() - adm.reset() - - if (callAudioRole == .listener) { + let adm = getAudioDeviceModule() + adm.reset() + + let intendedCategory: AVAudioSession.Category + let intendedMode: AVAudioSession.Mode + let intendedOptions: AVAudioSession.CategoryOptions + + if callAudioRole == .listener {
363-374: Consider extracting debounce delay as a named constant.The 2.0 second debounce delay for stereo state refresh is a magic number. Extracting it as a named constant would improve readability and make it easier to tune.
♻️ Suggested refactor
Add a constant at the class level:
private let stereoRefreshDebounceInterval: TimeInterval = 2.0Then update line 373:
- audioSessionQueue.asyncAfter(deadline: .now() + 2.0, execute: workItem) + audioSessionQueue.asyncAfter(deadline: .now() + stereoRefreshDebounceInterval, execute: workItem)
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
packages/react-native-sdk/ios/StreamInCallManager.swift
🧰 Additional context used
📓 Path-based instructions (1)
packages/react-native-sdk/ios/**/*.swift
📄 CodeRabbit inference engine (packages/react-native-sdk/CLAUDE.md)
When adding iOS native functionality, create Swift file in
ios/, create Objective-C bridge withRCT_EXTERN_METHOD, expose viaStreamVideoReactNative.swift, and call from TypeScript viaNativeModules.StreamVideoReactNative
Files:
packages/react-native-sdk/ios/StreamInCallManager.swift
🧠 Learnings (6)
📓 Common learnings
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/providers/StreamCall/**/*.tsx : Disable local video in background on iOS to save battery, but maintain audio connection for ongoing calls using AVAudioSession background modes
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/providers/StreamVideo.tsx : Use `StreamVideoRN.configure()` static method to set up SDK configuration including foreground service, push notification providers, and notification callbacks before mounting StreamVideo provider
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Do not instantiate multiple `StreamVideo` providers at the same hierarchy level; use only one provider at app root and nest `StreamCall` providers under it
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/providers/StreamCall/**/*.tsx : Disable local video in background on iOS to save battery, but maintain audio connection for ongoing calls using AVAudioSession background modes
Applied to files:
packages/react-native-sdk/ios/StreamInCallManager.swift
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/ios/**/*.swift : When adding iOS native functionality, create Swift file in `ios/`, create Objective-C bridge with `RCT_EXTERN_METHOD`, expose via `StreamVideoReactNative.swift`, and call from TypeScript via `NativeModules.StreamVideoReactNative`
Applied to files:
packages/react-native-sdk/ios/StreamInCallManager.swift
📚 Learning: 2026-01-09T11:07:06.761Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/client/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:06.761Z
Learning: Applies to packages/client/**/rtc/helpers/sdp.ts : Use removeCodecsExcept() helper for codec selection and enableStereo() for high-quality audio in music/DJ applications
Applied to files:
packages/react-native-sdk/ios/StreamInCallManager.swift
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/**/*.tsx : Always request media permissions via `usePermissionRequest()` hook before calling device enable methods like `call.camera.enable()` or `call.microphone.enable()`
Applied to files:
packages/react-native-sdk/ios/StreamInCallManager.swift
📚 Learning: 2026-01-09T11:08:35.312Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T11:08:35.312Z
Learning: Applies to packages/{client,react-sdk,react-native-sdk}/src/**/*.ts?(x) : Check instance IDs and timestamps before state updates to avoid race conditions in async operations
Applied to files:
packages/react-native-sdk/ios/StreamInCallManager.swift
🪛 SwiftLint (0.57.0)
packages/react-native-sdk/ios/StreamInCallManager.swift
[Warning] 96-96: TODOs should be resolved (for stereo we should disallow ...)
(todo)
🔇 Additional comments (7)
packages/react-native-sdk/ios/StreamInCallManager.swift (7)
27-37: LGTM!The new private properties for stereo state management are well-scoped with appropriate default values.
69-79: LGTM!The method follows the established pattern for configuration setters, correctly guarding against changes after activation and dispatching to the serial queue for thread safety.
96-98: Acknowledged limitation: Stereo Bluetooth A2DP configuration.The TODO documents a known limitation where enabling
.allowBluetoothA2DPwithout.allowBluetoothHFPfails with OSStatus error -50. This matches the iOS native SDK behavior limitation. Consider linking to a tracking issue if one exists.
135-165: LGTM!The
start()method correctly delegates setup configuration before activation. SinceaudioSessionQueueis a serial queue, the async block fromsetup()is guaranteed to complete before this method's async block executes. The wake lock and observer registration appropriately run on the main thread.
167-197: LGTM!The
stop()method properly cleans up by canceling pending debounced work items, deactivating the audio session, and disabling wake lock and proximity monitoring on the main thread.
214-230: Verify locking strategy for speaker override.The method locks
RTCAudioSessionbut then operates directly onAVAudioSession.sharedInstance(). While this may be intentional to prevent conflicts with WebRTC's audio session management, consider whether the port override should go throughRTCAudioSessionfor consistency with the rest of the refactored code.
451-472: LGTM!The helper method provides a clean, exhaustive mapping of route change reasons with proper handling of unknown future values via
@unknown default.
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In @packages/react-native-sdk/src/utils/internal/registerSDKGlobals.ts:
- Around line 10-25: streamRNVideoSDKGlobals.callManager methods currently call
native APIs directly and can throw if the native module is missing; wrap each
native call in try-catch blocks inside callManager.setup, callManager.start, and
callManager.stop (i.e., around
StreamInCallManagerNativeModule.setDefaultAudioDeviceEndpointType, .setup,
.start, and .stop) and handle errors by logging them (console.warn or existing
logger) and failing gracefully instead of letting exceptions propagate.
- Line 4: The code directly assigns NativeModules.StreamInCallManager to
StreamInCallManagerNativeModule which can be undefined on some platforms; add a
defensive guard where StreamInCallManagerNativeModule is declared/used: check
NativeModules.StreamInCallManager for truthiness, and if absent assign a safe
no-op stub object (implementing the same method names used elsewhere) or set the
variable to undefined and early-return from callers; also emit a console.warn or
logger warning when the native module is missing so we fail gracefully instead
of causing runtime crashes (refer to the StreamInCallManagerNativeModule symbol
and any functions that call its methods).
🧹 Nitpick comments (5)
packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/audio/AudioDeviceManager.kt (1)
116-125: Consider documenting thread-safety requirements.The
setup()function is public and modifies audio manager state. While current callers correctly invoke it withinrunInAudioThread, the function itself doesn't enforce this. Consider adding a KDoc comment indicating this must be called on the audio thread, or adding an assertion to catch misuse during development./** * Initializes audio mode and focus settings based on the current [callAudioRole]. * Must be called on the audio thread (via [runInAudioThread]). */ fun setup() {packages/react-native-sdk/ios/StreamInCallManager.swift (4)
84-86: Consider using explicit non-optional types with direct initialization.Using implicitly unwrapped optionals (
!) works here since all branches assign values, but it's more idiomatic Swift to initialize variables directly within a switch/if expression or declare them as non-optional with immediate assignment.♻️ Suggested refactor
- let intendedCategory: AVAudioSession.Category! - let intendedMode: AVAudioSession.Mode! - let intendedOptions: AVAudioSession.CategoryOptions! - - let adm = getAudioDeviceModule() - adm.reset() - - if (callAudioRole == .listener) { + let adm = getAudioDeviceModule() + adm.reset() + + let intendedCategory: AVAudioSession.Category + let intendedMode: AVAudioSession.Mode + let intendedOptions: AVAudioSession.CategoryOptions + + if callAudioRole == .listener {
96-98: Track the TODO for stereo Bluetooth A2DP support.The static analysis flagged this TODO comment. The comment indicates a known limitation where enabling Bluetooth A2DP for stereo playback (matching iOS native SDK behavior) fails with
OSStatus error -50. This should be tracked in your issue tracker to ensure it's not forgotten.Would you like me to help open an issue to track this Bluetooth A2DP limitation for stereo audio?
366-379: Consider dispatchinglogAudioState()to the audio session queue.The
logAudioState()call at line 379 acquires the RTCAudioSession lock synchronously from whatever thread delivers the route change notification. While likely fine in practice, dispatching it toaudioSessionQueuewould be more consistent with the rest of the audio session management in this class.♻️ Optional refactor
- logAudioState() - + audioSessionQueue.async { [weak self] in + self?.logAudioState() + }
431-441: Consider graceful handling instead offatalError.While missing
RCTBridgeorWebRTCModuleare genuine programming errors, usingfatalErrorwill crash the app. This could occur during edge cases like app teardown. Consider returning an optional or logging an error and handling the failure gracefully.♻️ Suggested approach with optional return
- private func getAudioDeviceModule() -> AudioDeviceModule { - guard let bridge = self.bridge else { - fatalError("StreamInCallManager: RCTBridge is not available yet.") - } - - guard let webrtcModule = bridge.module(forName: "WebRTCModule") as? WebRTCModule else { - fatalError("WebRTCModule is required but not registered with the bridge") - } - - return webrtcModule.audioDeviceModule - } + private func getAudioDeviceModule() -> AudioDeviceModule? { + guard let bridge = self.bridge else { + log("Error: RCTBridge is not available") + return nil + } + + guard let webrtcModule = bridge.module(forName: "WebRTCModule") as? WebRTCModule else { + log("Error: WebRTCModule is not registered with the bridge") + return nil + } + + return webrtcModule.audioDeviceModule + }Then update call sites to use optional chaining (e.g.,
getAudioDeviceModule()?.reset()).
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (3)
packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/audio/AudioDeviceManager.ktpackages/react-native-sdk/ios/StreamInCallManager.swiftpackages/react-native-sdk/src/utils/internal/registerSDKGlobals.ts
🧰 Additional context used
📓 Path-based instructions (8)
packages/react-native-sdk/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (packages/react-native-sdk/CLAUDE.md)
packages/react-native-sdk/src/**/*.{ts,tsx}: Use React hooks from@stream-io/video-react-bindings(viauseCall()anduseCallStateHooks()) instead of directly accessing RxJS observables from@stream-io/video-client
Use Platform.OS checks and conditional imports to gate platform-specific code (iOS, Android, web) and prevent importing platform-specific libraries on unsupported platforms
Files:
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.ts
packages/react-native-sdk/src/utils/**/*.ts
📄 CodeRabbit inference engine (packages/react-native-sdk/CLAUDE.md)
Implement React Native native modules using
NativeModulesAPI and always wrap calls in try-catch blocks to handle promise rejection from native code
Files:
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.ts
packages/{client,react-sdk,react-native-sdk}/src/**/*.ts?(x)
📄 CodeRabbit inference engine (AGENTS.md)
packages/{client,react-sdk,react-native-sdk}/src/**/*.ts?(x): Use TypeScript for all source code in packages/client, packages/react-sdk, and packages/react-native-sdk
Mark deprecated APIs with @deprecated JSDoc, including rationale and replacement guidance
Throw descriptive errors or return typed error results consistently with existing patterns in public APIs
Gate internal debug logging behind an environment flag; no console noise in production builds
Never leak credentials or user data in error messages or logs
Check instance IDs and timestamps before state updates to avoid race conditions in async operations
Make public API surfaces explicit with TypeScript types and interfaces
Files:
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx,js,jsx}: Use camelCase for function and property names
Narrowly scope eslint-disable comments with inline explanatory comments and rationale
Files:
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use PascalCase for component and type names
Files:
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.ts
**/*.{sh,js,ts}
📄 CodeRabbit inference engine (AGENTS.md)
Make scripts error on missing critical environment variables
Files:
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.ts
packages/react-native-sdk/ios/**/*.swift
📄 CodeRabbit inference engine (packages/react-native-sdk/CLAUDE.md)
When adding iOS native functionality, create Swift file in
ios/, create Objective-C bridge withRCT_EXTERN_METHOD, expose viaStreamVideoReactNative.swift, and call from TypeScript viaNativeModules.StreamVideoReactNative
Files:
packages/react-native-sdk/ios/StreamInCallManager.swift
packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/**/*.kt
📄 CodeRabbit inference engine (packages/react-native-sdk/CLAUDE.md)
When adding Android native functionality, create Kotlin file in
android/src/main/java/com/streamvideo/reactnative/, use@ReactMethodannotation, register inStreamVideoReactNativePackage.kt, and call from TypeScript viaNativeModules.StreamVideoReactNative
Files:
packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/audio/AudioDeviceManager.kt
🧠 Learnings (20)
📓 Common learnings
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/providers/StreamCall/**/*.tsx : Disable local video in background on iOS to save battery, but maintain audio connection for ongoing calls using AVAudioSession background modes
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/providers/StreamVideo.tsx : Use `StreamVideoRN.configure()` static method to set up SDK configuration including foreground service, push notification providers, and notification callbacks before mounting StreamVideo provider
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Do not instantiate multiple `StreamVideo` providers at the same hierarchy level; use only one provider at app root and nest `StreamCall` providers under it
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/**/*.{ts,tsx} : Use React hooks from `stream-io/video-react-bindings` (via `useCall()` and `useCallStateHooks()`) instead of directly accessing RxJS observables from `stream-io/video-client`
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/ios/**/*.swift : When adding iOS native functionality, create Swift file in `ios/`, create Objective-C bridge with `RCT_EXTERN_METHOD`, expose via `StreamVideoReactNative.swift`, and call from TypeScript via `NativeModules.StreamVideoReactNative`
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/index.ts : Always call `registerGlobals()` from `stream-io/react-native-webrtc` before using WebRTC on non-web platforms in the React Native SDK entry point
Applied to files:
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.tspackages/react-native-sdk/ios/StreamInCallManager.swift
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/ios/**/*.swift : When adding iOS native functionality, create Swift file in `ios/`, create Objective-C bridge with `RCT_EXTERN_METHOD`, expose via `StreamVideoReactNative.swift`, and call from TypeScript via `NativeModules.StreamVideoReactNative`
Applied to files:
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.tspackages/react-native-sdk/ios/StreamInCallManager.swift
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/index.ts : Export all public SDK APIs (components, hooks, providers, types) from the main `src/index.ts` file, with re-exports from client and bindings packages and SDK-specific implementations
Applied to files:
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.ts
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/version.ts : Store generated version information in `src/version.ts` (auto-generated via `yarn copy-version` script) and export for SDK consumers
Applied to files:
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.ts
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/**/*.kt : When adding Android native functionality, create Kotlin file in `android/src/main/java/com/streamvideo/reactnative/`, use `ReactMethod` annotation, register in `StreamVideoReactNativePackage.kt`, and call from TypeScript via `NativeModules.StreamVideoReactNative`
Applied to files:
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.tspackages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/audio/AudioDeviceManager.kt
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/**/*.{ts,tsx} : Use React hooks from `stream-io/video-react-bindings` (via `useCall()` and `useCallStateHooks()`) instead of directly accessing RxJS observables from `stream-io/video-client`
Applied to files:
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.ts
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/modules/**/*.ts : Structure native module TypeScript interfaces in `src/modules/` to match the exact signatures of their native implementations (Android Kotlin and iOS Swift), including parameter types and return types
Applied to files:
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.ts
📚 Learning: 2026-01-09T11:08:35.312Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T11:08:35.312Z
Learning: Applies to packages/{client,react-sdk,react-native-sdk}/src/**/*.ts?(x) : Make public API surfaces explicit with TypeScript types and interfaces
Applied to files:
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.ts
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/providers/StreamVideo.tsx : Use `StreamVideoRN.configure()` static method to set up SDK configuration including foreground service, push notification providers, and notification callbacks before mounting StreamVideo provider
Applied to files:
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.ts
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/utils/**/*.ts : Implement React Native native modules using `NativeModules` API and always wrap calls in try-catch blocks to handle promise rejection from native code
Applied to files:
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.tspackages/react-native-sdk/ios/StreamInCallManager.swift
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/**/*.tsx : Always request media permissions via `usePermissionRequest()` hook before calling device enable methods like `call.camera.enable()` or `call.microphone.enable()`
Applied to files:
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.tspackages/react-native-sdk/ios/StreamInCallManager.swiftpackages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/audio/AudioDeviceManager.kt
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/providers/**/*.tsx : Use the renderless component pattern (component that returns null) for side-effect-only child components within `StreamCall` and `StreamVideo` providers to keep logic separated and testable
Applied to files:
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.ts
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/hooks/**/*.ts : When adding new React Native hooks, use `useCall()` and `useCallStateHooks()` from bindings layer, handle platform-specific logic with `Platform.OS`, add tests in `__tests__/hooks/`, and export from `src/hooks/index.ts`
Applied to files:
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.ts
📚 Learning: 2026-01-09T11:07:06.761Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/client/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:06.761Z
Learning: Applies to packages/client/**/StreamVideoClient.ts : Implement SafePromise pattern for long-lived promises that may reject to prevent unhandled rejection errors; use checkPending() to check if promise is still pending
Applied to files:
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.ts
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/providers/StreamCall/**/*.tsx : Disable local video in background on iOS to save battery, but maintain audio connection for ongoing calls using AVAudioSession background modes
Applied to files:
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.tspackages/react-native-sdk/ios/StreamInCallManager.swiftpackages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/audio/AudioDeviceManager.kt
📚 Learning: 2026-01-09T11:08:35.312Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T11:08:35.312Z
Learning: Applies to packages/client/src/**/*.ts?(x) : Avoid unguarded web-only APIs in shared code between React and React Native
Applied to files:
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.ts
📚 Learning: 2026-01-09T11:08:06.856Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:08:06.856Z
Learning: Applies to packages/react-sdk/**/*.{ts,tsx} : Always use hooks from stream-io/video-react-bindings via useCallStateHooks() factory pattern instead of importing individual hooks directly
Applied to files:
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.ts
📚 Learning: 2026-01-09T11:07:06.761Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/client/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:06.761Z
Learning: Applies to packages/client/**/rtc/helpers/sdp.ts : Use removeCodecsExcept() helper for codec selection and enableStereo() for high-quality audio in music/DJ applications
Applied to files:
packages/react-native-sdk/ios/StreamInCallManager.swiftpackages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/audio/AudioDeviceManager.kt
📚 Learning: 2026-01-09T11:08:35.312Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T11:08:35.312Z
Learning: Applies to packages/{client,react-sdk,react-native-sdk}/src/**/*.ts?(x) : Check instance IDs and timestamps before state updates to avoid race conditions in async operations
Applied to files:
packages/react-native-sdk/ios/StreamInCallManager.swift
🧬 Code graph analysis (3)
packages/react-native-sdk/src/utils/internal/registerSDKGlobals.ts (1)
packages/client/src/types.ts (1)
StreamRNVideoSDKGlobals(334-355)
packages/react-native-sdk/ios/StreamInCallManager.swift (2)
packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/callmanager/StreamInCallManagerModule.kt (2)
setEnableStereoAudioOutput(83-92)setup(94-99)packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/audio/AudioDeviceManager.kt (1)
setup(116-125)
packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/audio/AudioDeviceManager.kt (3)
packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/callmanager/StreamInCallManagerModule.kt (1)
setup(94-99)packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/audio/utils/AudioFocusUtil.kt (2)
setup(37-41)setup(43-48)packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/audio/BluetoothManager.kt (1)
updateAudioDeviceState(78-81)
🪛 SwiftLint (0.57.0)
packages/react-native-sdk/ios/StreamInCallManager.swift
[Warning] 96-96: TODOs should be resolved (for stereo we should disallow ...)
(todo)
🔇 Additional comments (11)
packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/audio/AudioDeviceManager.kt (3)
92-93: LGTM!The new
enableStereoproperty follows the existing pattern for configuration properties in this class. It's reset tofalseinstop(), ensuring clean state between calls.
127-144: LGTM!The refactoring to call
setup()at the start ofstart()centralizes audio initialization logic and keeps it properly within the audio thread context.
147-164: Verify conditional state reset behavior.The state resets at lines 155-157 (
callAudioRole,enableStereo,defaultAudioDevice) only occur whencallAudioRole == CallAudioRole.Communicator. This means:
- If stopping a non-Communicator call, these values retain their previous state.
If a non-Communicator call could have modified
enableStereoordefaultAudioDevice, those values won't be reset. Verify this is the intended behavior, or consider moving the resets outside the conditional block:Suggested fix if resets should apply to all call types
fun stop(activity: Activity) { runInAudioThread { if (callAudioRole == CallAudioRole.Communicator) { if (Build.VERSION.SDK_INT >= 31) { mAudioManager.clearCommunicationDevice() } else { mAudioManager.setSpeakerphoneOn(false) } - callAudioRole = CallAudioRole.Communicator - enableStereo = false - defaultAudioDevice = AudioDeviceEndpoint.TYPE_SPEAKER bluetoothManager.stop() proximityManager.stop() } + // Reset to defaults for all call types + callAudioRole = CallAudioRole.Communicator + enableStereo = false + defaultAudioDevice = AudioDeviceEndpoint.TYPE_SPEAKER activity.volumeControlStream = AudioManager.USE_DEFAULT_STREAM_TYPE audioFocusUtil.abandonFocus() } }packages/react-native-sdk/src/utils/internal/registerSDKGlobals.ts (2)
6-8: LGTM!The global declaration correctly extends the global namespace with proper TypeScript typing, allowing
globalThis.streamRNVideoSDKto be accessed safely with the appropriate type.
27-31: LGTM!The idempotent registration pattern correctly prevents overwriting an existing global, which is important if multiple SDK instances or re-initializations occur.
packages/react-native-sdk/ios/StreamInCallManager.swift (6)
70-79: LGTM!The implementation correctly mirrors the Android pattern, preventing stereo configuration changes after activation and using the audio session queue for thread safety.
135-165: LGTM!The
start()method correctly chainssetup()and activation on the same serial queue, ensuring proper ordering. The RTCAudioSession lock/unlock pattern withdeferis correctly implemented.
183-189: LGTM!Good defensive cleanup: cancelling the pending debounced work item and resetting all configuration state to defaults ensures a clean slate for the next call.
226-228: Verify necessity ofsetActive(true)after output port override.The
setActive(true)call afteroverrideOutputAudioPortmay be unnecessary if the session is already active (fromstart()). This could also cause unexpected behavior if called when the audio session is intentionally inactive.Consider whether this activation is needed, or if the port override alone is sufficient when the session is already active.
241-278: LGTM!The enhanced logging now includes both AVAudioSession and RTCAudioSession state, which will be valuable for debugging stereo and audio routing issues. The locking pattern is correctly applied.
454-475: LGTM!Clean helper implementation with proper
@unknown defaulthandling for future iOS versions.
There was a problem hiding this comment.
Actionable comments posted: 0
🧹 Nitpick comments (1)
sample-apps/react-native/dogfood/ios/AppDelegate.swift (1)
152-155: LGTM! Debug-only logging is correctly gated.The
#if DEBUGblock properly ensures verbose WebRTC logs are only active in debug builds, preventing console noise in production. Based on learnings, this aligns with the guideline to gate internal debug logging behind an environment flag.Minor nit: The comment at lines 132-133 ("Uncomment the next line to enable verbose WebRTC logs") is now outdated since verbose logging is already enabled automatically in DEBUG builds. Consider removing or updating that comment to avoid confusion.
🧹 Optional cleanup
- // Uncomment the next line to enable verbose WebRTC logs - // WebRTCModuleOptions.sharedInstance().loggingSeverity = .verbose - let localizedAppName = Bundle.main.localizedInfoDictionary?["CFBundleDisplayName"] as? String
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
sample-apps/react-native/dogfood/ios/AppDelegate.swift
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/providers/StreamCall/**/*.tsx : Disable local video in background on iOS to save battery, but maintain audio connection for ongoing calls using AVAudioSession background modes
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/providers/StreamVideo.tsx : Use `StreamVideoRN.configure()` static method to set up SDK configuration including foreground service, push notification providers, and notification callbacks before mounting StreamVideo provider
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/index.ts : Always call `registerGlobals()` from `stream-io/react-native-webrtc` before using WebRTC on non-web platforms in the React Native SDK entry point
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/**/*.{ts,tsx} : Use React hooks from `stream-io/video-react-bindings` (via `useCall()` and `useCallStateHooks()`) instead of directly accessing RxJS observables from `stream-io/video-client`
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/ios/**/*.swift : When adding iOS native functionality, create Swift file in `ios/`, create Objective-C bridge with `RCT_EXTERN_METHOD`, expose via `StreamVideoReactNative.swift`, and call from TypeScript via `NativeModules.StreamVideoReactNative`
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Do not instantiate multiple `StreamVideo` providers at the same hierarchy level; use only one provider at app root and nest `StreamCall` providers under it
📚 Learning: 2026-01-09T11:08:35.312Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-09T11:08:35.312Z
Learning: Applies to packages/{client,react-sdk,react-native-sdk}/src/**/*.ts?(x) : Gate internal debug logging behind an environment flag; no console noise in production builds
Applied to files:
sample-apps/react-native/dogfood/ios/AppDelegate.swift
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Use the dogfood sample app (`sample-apps/react-native/dogfood/`) for manual testing of all SDK features and integration with push notifications before release
Applied to files:
sample-apps/react-native/dogfood/ios/AppDelegate.swift
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/expo-config-plugin/src/**/*.ts : Configure Expo config plugins in `expo-config-plugin/` with platform-specific modifiers for Android manifest, iOS Info.plist, and app delegate modifications
Applied to files:
sample-apps/react-native/dogfood/ios/AppDelegate.swift
📚 Learning: 2026-01-09T11:07:38.535Z
Learnt from: CR
Repo: GetStream/stream-video-js PR: 0
File: packages/react-native-sdk/CLAUDE.md:0-0
Timestamp: 2026-01-09T11:07:38.535Z
Learning: Applies to packages/react-native-sdk/src/index.ts : Always call `registerGlobals()` from `stream-io/react-native-webrtc` before using WebRTC on non-web platforms in the React Native SDK entry point
Applied to files:
sample-apps/react-native/dogfood/ios/AppDelegate.swift
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@packages/react-native-sdk/ios/StreamInCallManager.swift`:
- Around line 379-390: The notification handler accesses and mutates
stereoRefreshWorkItem off-thread which races with stop() that touches the same
property on audioSessionQueue; wrap the cancel, creation/assignment of
stereoRefreshWorkItem and scheduling into audioSessionQueue (use
audioSessionQueue.async or asyncIfNeeded) so all reads/writes to
stereoRefreshWorkItem happen on audioSessionQueue, keep the DispatchWorkItem
closure body unchanged (calling
getAudioDeviceModule().refreshStereoPlayoutState() and log) and ensure you
capture self weakly as before.
🧹 Nitpick comments (2)
packages/react-native-sdk/ios/StreamInCallManager.swift (2)
84-86: Remove implicitly unwrapped optionals.These variables are assigned in all code paths, so IUOs are unnecessary and can mask potential issues.
♻️ Suggested fix
- let intendedCategory: AVAudioSession.Category! - let intendedMode: AVAudioSession.Mode! - let intendedOptions: AVAudioSession.CategoryOptions! + let intendedCategory: AVAudioSession.Category + let intendedMode: AVAudioSession.Mode + let intendedOptions: AVAudioSession.CategoryOptions
98-100: Consider creating an issue to track the Bluetooth A2DP limitation.The TODO indicates that stereo playback with Bluetooth A2DP-only mode fails with
OSStatus error -50. This is a known iOS limitation worth tracking externally.Would you like me to open an issue to track resolving this Bluetooth A2DP stereo limitation?
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@packages/react-native-sdk/ios/StreamInCallManager.swift`:
- Around line 111-113: The TODO about stereo/Bluetooth handling in
StreamInCallManager.swift must be resolved or tracked: implement the stereo
behavior by setting intendedOptions = self.enableStereo ? [.allowBluetoothA2DP]
: [] where intendedOptions is used to configure the AVAudioSession (e.g., when
calling setCategory/setMode/setActive), and add a fallback path that catches the
OSStatus -50 error and retries with [.allowBluetoothHFP] (log the error).
Alternatively, if you don't implement now, replace the TODO with a TODO/FIXME
that includes a link/ID to a new issue and add a swiftlint:disable comment so
SwiftLint stops failing; reference the enableStereo property and the
intendedOptions assignment so reviewers can find the change.
♻️ Duplicate comments (4)
packages/react-native-sdk/ios/StreamInCallManager.swift (3)
243-255: Use RTCAudioSession activation to keep WebRTC state consistent.Calling
AVAudioSession.setActive(true)bypasses RTCAudioSession’s activation bookkeeping (activationCount,isActive). Usesession.setActive(true)under the RTC lock instead.🔧 Proposed fix
do { try avAudioSession.overrideOutputAudioPort(enable ? .speaker : .none) - try avAudioSession.setActive(true) + try session.setActive(true) } catch { self?.log("Error setting speakerphone: \(error)") }RTCAudioSession iOS setActive vs AVAudioSession setActive when using WebRTC
382-402: Race onstereoRefreshWorkItemoutsideaudioSessionQueue.This reintroduces the earlier race: the notification handler mutates
stereoRefreshWorkItemoff-queue whilestop()also touches it on the queue. Please move cancel/assign/schedule intoaudioSessionQueue.async.🔒 Proposed fix
if reason == .routeConfigurationChange { - // Cancel any pending debounced refresh - stereoRefreshWorkItem?.cancel() - // Create a new debounced work item - let workItem = DispatchWorkItem { [weak self] in - self?.getAudioDeviceModule().refreshStereoPlayoutState() - self?.log("Executed debounced refreshStereoPlayoutState") - } - stereoRefreshWorkItem = workItem - // Schedule the work item after debounce interval - audioSessionQueue.asyncAfter(deadline: .now() + Constants.stereoRefreshDebounceSeconds, execute: workItem) + audioSessionQueue.async { [weak self] in + guard let self = self else { return } + // Cancel any pending debounced refresh + self.stereoRefreshWorkItem?.cancel() + // Create a new debounced work item + let workItem = DispatchWorkItem { [weak self] in + self?.getAudioDeviceModule().refreshStereoPlayoutState() + self?.log("Executed debounced refreshStereoPlayoutState") + } + self.stereoRefreshWorkItem = workItem + // Schedule the work item after debounce interval + self.audioSessionQueue.asyncAfter( + deadline: .now() + Constants.stereoRefreshDebounceSeconds, + execute: workItem + ) + } }
457-466: AvoidfatalErrorin teardown/bridge edge cases.A missing bridge/module during teardown will crash. Consider returning an optional and logging, then safely no-op at call sites.
🧯 Proposed approach
- private func getAudioDeviceModule() -> AudioDeviceModule { - guard let bridge = self.bridge else { - fatalError("StreamInCallManager: RCTBridge is not available yet.") - } - - guard let webrtcModule = bridge.module(forName: "WebRTCModule") as? WebRTCModule else { - fatalError("WebRTCModule is required but not registered with the bridge") - } - - return webrtcModule.audioDeviceModule - } + private func getAudioDeviceModule() -> AudioDeviceModule? { + guard let bridge = self.bridge else { + log("StreamInCallManager: RCTBridge is not available yet.") + return nil + } + + guard let webrtcModule = bridge.module(forName: "WebRTCModule") as? WebRTCModule else { + log("WebRTCModule is required but not registered with the bridge") + return nil + } + + return webrtcModule.audioDeviceModule + }Then update call sites (e.g.,
getAudioDeviceModule()?.reset()).packages/react-native-sdk/src/utils/internal/registerSDKGlobals.ts (1)
6-19: Wrap native module calls in try/catch to prevent native exceptions from crashing JS.Even if the module is bundled, native calls can still throw (e.g., audio session failures). The utils guidelines require try/catch for native module calls; consider routing errors through the existing logger (or gating console output with
__DEV__) to avoid production noise.🛠️ Suggested defensive handling
const streamRNVideoSDKGlobals: StreamRNVideoSDKGlobals = { callManager: { setup: ({ default_device }) => { - StreamInCallManagerNativeModule.setDefaultAudioDeviceEndpointType( - default_device, - ); - StreamInCallManagerNativeModule.setup(); + try { + StreamInCallManagerNativeModule.setDefaultAudioDeviceEndpointType( + default_device, + ); + StreamInCallManagerNativeModule.setup(); + } catch (error) { + if (__DEV__) { + console.warn( + '[StreamVideoRN] Failed to setup in-call manager:', + error, + ); + } + } }, start: () => { - StreamInCallManagerNativeModule.start(); + try { + StreamInCallManagerNativeModule.start(); + } catch (error) { + if (__DEV__) { + console.warn('[StreamVideoRN] Failed to start in-call manager:', error); + } + } }, stop: () => { - StreamInCallManagerNativeModule.stop(); + try { + StreamInCallManagerNativeModule.stop(); + } catch (error) { + if (__DEV__) { + console.warn('[StreamVideoRN] Failed to stop in-call manager:', error); + } + } }, }, };As per coding guidelines.
Run this to review existing native-module error-handling patterns in utils and align with the preferred logger:
#!/bin/bash rg -n "NativeModules" packages/react-native-sdk/src/utils
🧹 Nitpick comments (1)
packages/react-native-sdk/ios/StreamInCallManager.swift (1)
97-100: Prefer non-optional audio session config variables.These values are always assigned in each branch; IUOs add avoidable runtime risk and deviate from idiomatic Swift. Consider plain
letfor compile-time safety.♻️ Proposed change
- let intendedCategory: AVAudioSession.Category! - let intendedMode: AVAudioSession.Mode! - let intendedOptions: AVAudioSession.CategoryOptions! + let intendedCategory: AVAudioSession.Category + let intendedMode: AVAudioSession.Mode + let intendedOptions: AVAudioSession.CategoryOptions
# Conflicts: # packages/client/src/types.ts # sample-apps/react-native/dogfood/ios/Podfile.lock
Run cancel/create/schedule on audioSessionQueue to avoid races with stop.
💡 Overview
Apart from stereo audio support, we also ensure that from video client we make sure that the audio session always configured before joining a call and then audio session configuration is deactivated after leaving the call
depends on: GetStream/react-native-webrtc#23
📝 Implementation notes
🎫 Ticket: https://linear.app/stream/issue/RN-332
📑 Docs: https://github.com/GetStream/docs-content/pull/899
Summary by CodeRabbit
New Features
Improvements
Chores
✏️ Tip: You can customize this high-level summary in your review settings.