This Frustrating iOS Safari MediaStream Bug (and fix)

Kyron
3 min readDec 23, 2018

--

Intro

I recently just finished working for a company that provided an embeddable web-chat system with a voice-chat feature. The product was broken on iOS when I first joined the team, due to numerous issues with iOS Safari, including one native bug. Here’s how I fixed it.

How our App Worked

The way our application was designed was to create a new window (popup) using ​window.open(); ​and start a basic WebRTC call within the child window. This is required for persistence. Enabling the visitor (User A) to continue to browse the website without the call being interrupted. This was both a user-experience enhancement, and a requirement due to our third-party SIP provider.

The first issue was that, we were dynamically creating a video/audio element with the autoplay attribute — which doesn’t work on iOS — The autoplay attribute is respected only on page loading. Simple enough solution — we just run .play() and the stream will play. Sweet, that was easy. Now our whole solution is working!… Wait.

The Problem

This, for some reason only works for one call. Any subsequent calls will result in the agent (User B) not being able hear the visitor (User A). The only solution being to completely reset the browser.

This is very strange as it everything appears fine. I couldn’t find anything particularly out of the ordinary while debugging.

You can pull up Dev Tools for iOS devices from Safari on MacOS

The Solution

After testing in multiple different environments, I’d deducted that the issue was with closing the popup window (containing the WebRTC call). For some reason, it appears that Safari fails to tear down the local MediaStream properly, which results in a non-functioning input stream. The only fix being to hard reset the browser.

Thankfully, I found a way to get the browser to tear down the popup gracefully and not screw up further calls.

Solution

I eventually stumbled upon a fix — if we refreshed, or changed the location of the child window (which ends our call as mentioned before), then the MediaStream will teardown properly and you can then close the window.

There are multiple ways to do this, however here is my minimal approach, which you can demo on CodeSandbox.io

This will result in a clean teardown of the WebRTC connection, and the agent (User B) will be able to hear the visitor (User A) in any subsequent calls. The caveat being that if the visitor (User A) were to manually close the popup before our hacky little workaround were to execute, then this would result in the same bug, and they would need to restart the browser.

Hopefully Apple fixes this in a future update. This is still an issue now as of iOS 12.

--

--