Skip to content

Commit 501145c

Browse files
authored
fix: fixed dict and array enumeration for clone method (#26)
### The Problem The clone method on iOS has two mutation-during-enumeration bugs — a classic Objective-C pitfall that causes crashes: 1. Dictionary mutation during enumeration — The code iterates over self.localStreams directly (for ... in self.localStreams) while the loop body can modify the dictionary (e.g., adding tracks to a stream can trigger side effects). In Objective-C, mutating a collection while enumerating it throws an NSInvalidArgumentException. 2. Array mutation during enumeration — Similarly, stream.audioTracks and stream.videoTracks are iterated directly, but addAudioTrack: / addVideoTrack: calls inside the loop can mutate the underlying array. ### The Fix (two changes, applied to both audio and video track cloning) Before: for (... in self.localStreams) After: for (... in [self.localStreams allKeys]) Why: Iterates over a snapshot of the dictionary keys, so mutations to the dictionary during the loop are safe. ──────────────────────────────────────── Before: for (... in stream.audioTracks) / stream.videoTracks After: for (... in [stream.audioTracks copy]) / [stream.videoTracks copy] Why: Iterates over a copy of the array, so adding tracks to the stream mid-loop won't crash. ──────────────────────────────────────── Before: (no nil check) After: if (stream == nil) continue; Why: Adds a defensive nil guard in case a stream was removed between getting the keys and looking it up. ### Impact This is a crash fix for the iOS track cloning path. Without it, cloning a media stream track could intermittently crash the app with a "collection was mutated while being enumerated" exception — a race condition that's hard to reproduce but common under load. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Improved reliability of media stream handling to prevent potential issues during track operations. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent d46511f commit 501145c

File tree

1 file changed

+6
-4
lines changed

1 file changed

+6
-4
lines changed

ios/RCTWebRTC/WebRTCModule+RTCMediaStream.m

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -519,9 +519,10 @@ - (void)removeLocalVideoTrackDimensionDetection:(RTCVideoTrack *)videoTrack {
519519
RTCAudioTrack *audioTrack = [self.peerConnectionFactory audioTrackWithTrackId:trackUUID];
520520
audioTrack.isEnabled = originalTrack.isEnabled;
521521
[self.localTracks setObject:audioTrack forKey:trackUUID];
522-
for (NSString* streamId in self.localStreams) {
522+
for (NSString* streamId in [self.localStreams allKeys]) {
523523
RTCMediaStream* stream = [self.localStreams objectForKey:streamId];
524-
for (RTCAudioTrack* track in stream.audioTracks) {
524+
if (stream == nil) continue;
525+
for (RTCAudioTrack* track in [stream.audioTracks copy]) {
525526
if ([trackID isEqualToString:track.trackId]) {
526527
[stream addAudioTrack:audioTrack];
527528
}
@@ -537,9 +538,10 @@ - (void)removeLocalVideoTrackDimensionDetection:(RTCVideoTrack *)videoTrack {
537538
[self addLocalVideoTrackDimensionDetection:videoTrack];
538539

539540
[self.localTracks setObject:videoTrack forKey:trackUUID];
540-
for (NSString* streamId in self.localStreams) {
541+
for (NSString* streamId in [self.localStreams allKeys]) {
541542
RTCMediaStream* stream = [self.localStreams objectForKey:streamId];
542-
for (RTCVideoTrack* track in stream.videoTracks) {
543+
if (stream == nil) continue;
544+
for (RTCVideoTrack* track in [stream.videoTracks copy]) {
543545
if ([trackID isEqualToString:track.trackId]) {
544546
[stream addVideoTrack:videoTrack];
545547
}

0 commit comments

Comments
 (0)