Artist’s rendition of one of the YMC Mobile team members

RADIOUS, our Walkie Talkie app, relies on Apple’s Multipeer Connectivity Framework which came to iOS with iOS 7 last year.

It allows nearby iOS devices to communicate with each other in a peer to peer fashion. Depending on what connectivity options are available, it uses infrastructure Wi-Fi networks (iPhone 4 and up), peer to peer Wi-Fi (iPhone 5/iPad 4th Gen and up), and Bluetooth (iPhone 4S/iPad 2 and up). This means if peer to peer Wi-Fi or Bluetooth is available, it works even without a common Wi-Fi network between the peers. And it requires no Internet connection at all which is useful in cases where a connection to the Internet is not available or very expensive (e.g. when the user is abroad).

There is an overview of how to use Multipeer Connectivity on NSHipster, if you’re not familiar with using the framework.

This sounds very useful – if it only would work properly. As with Game Kit and Game Center, you can tell that Multipeer Connectivity Framework is not something Apple is using themselves.

In the process of creating a custom version of RADIOUS for a customer to support more than two clients and to handle reconnection if one client drops out, we hit several major bugs in the Framework. We’d like to share the biggest roadblocks and how to get around them (if possible). If you’re also running into those problems, please do also file a bug report with Apple. The issue numbers are noted in this post and its contents are on Open Radar.

Noticing that someone is gone

This sounds like an easy task, but unfortunately, the framework doesn’t always notify you when a peer is gone. To implement reconnection however, we do need to know.

The problem is two fold:

  • When browsing for nearby peers, we need to know when a peer is out of range so we can remove it from the UI as a candidate to connect to. Also, when the peer was previously connected, we need to see a disappearance and reappearance to know that we can initiate the connection again.
  • When connected, we need to know when a peer drops out

Problem 1: Detecting when a peer is not nearby

Before a connection can be established, we need to know what peers are nearby. Each peer that can accept connections runs an advertiser and peers looking to connect to someone run a service browser.

The browser can either feed a UI where the user can initiate connections or the app can handle those events programmatically.

Usually, when a peer is out of range or has stopped its advertiser, -[MCNearbyServiceBrowserDelegate browser:lostPeer:] is called so you can react to it accordingly. This works in most cases but unfortunately it’s only “in most cases”. If the iOS simulator is browsing, the peer always sticks around when it drops out of the network. Even completely re-instantiating the peer browser does not help.

Not noticing that a peer got out of range and then back in again is not a big problem for the peer browser UI since the connection will actually succeed when the user tells the app to connect.

But for automated actions, it is a problem since you can’t be sure when the other peer is back in range.

(Filed as rdar://17574895).

Workaround: None so far.

Problem 2: Detecting when a peer lost connection

When the connection to a peer is established or lost, the session calls -[MCSessionDelegate session:peer:didChangeState:] on its delegate. Except when it doesn’t. Sometimes, we get the notification much later, in the order of half a minute. Sometimes, we don’t get any notification when a peer drops out at all.

(Filed as rdar://17640200)

Workaround: We implemented a heartbeat which regularly sends a message to each connected peer and expects an answer. After a certain amount of consecutive heartbeat failures, we mark the peer as disconnected. Works great with two peers, as you can just disconnect from the session when the heartbeat fails. Unfortunately, for sessions with more than two peers, this is only moderately useful, as a peer can’t be manually removed from the session (filed as rdar://17640200). To work around this, we create a session for each pair of peers. More on that a bit further down.

Reconnecting after connection Loss

“That should be easy”, we thought. “Just send out an invitation automatically when the dropped out peer appears again. The other peer would just automatically accept the invitation when it’s reconnecting.”

As discussed above, the “when the dropped out peer appears again” is not detected reliably. But in the cases we can detect the condition and react accordingly, we still hit some brick walls.

Problem 3: Reconnecting in a session with more than two peers

One of the biggest problems we hit was that a peer re-joining a session doesn’t re-establish connection to all peers. To illustrate the problem, we have a session with three peers connected to each other:

A session with three peers, “A”, “B” and “C” connected

Now let’s say peer “C” goes out of range:

Peer “C” is now disconnected

Now peer “C” get back into range and peer “A” connects to to peer “C”. We expect that all peers would get connected to each other again, but the actual result is something like this:

Peers “B” and “C” can’t talk to each other

Peer “A” can talk to peers “B” and “C”, but peers “B” and “C” is only connected to peer “A”.

The funny thing is though, peers “B” and “C” each get -[MCSessionDelegate session:peer:didChangeState:] called for the not connected peer withMCSessionStateConnected, i.e. the framework tells peer “B” that peer “C” is now connected (and vice versa). But in reality, all attempts of peers “B” and “C” talking to each other fails with a “Peer is not connected” error message.

(Filed as rdar://17533347)

Workaround: Create a session for each connected peer. In the above example, peer “A” would have a session with only peer “B” and another session with only peer “C” in it. The downside of this solution is that it prevents one of Multipeer Connectivity’s flagship features: If a peer is not directly reachable, the data can not be relayed by another peer anymore.

Problem 4: Being invited after a connection loss

Sometimes, -[MCNearbyServiceAdvertiserDelegate advertiser:didReceiveInvitationFromPeer:(…)] doesn’t get called on the advertiser’s delegate. The retry mechanism on the inviter’s side didn’t help as all subsequent invitations also didn’t come through.

(Filed as rdar://17637621)

Workaround: Re-instantiate the browser and advertiser. Just stopping and starting them again is not sufficient, only creating new instances helped in our case. We do this each time a peer changes to the disconnected state.

Conclusion

Multipeer Connectivity has many great uses, but unfortunately it’s still in its infancy and not up to the quality levels of Apple’s more mature and often used frameworks. It was a ridiculous amount of extra work we needed to put into a seemingly simple feature such as automatic reconnection. Unfortunately, we’re not the only ones with problems.

We are considering cleaning up and extracting our session management code into a library and releasing it. Before we do that, we need to ship first though.

7 Kommentare

  1. I was wondering if you ever considered changing the page layout of
    your website? Its very well written; I love what youve
    got to say. But maybe you could a little more in the way of content so people could connect
    with it better. Youve got an awful lot of text for only having 1 or 2 pictures.

    Maybe you could space it out better?

    Antworten
  2. A wonderful & well researched article. Just wondering how is it today after almost one year from when you notice these issues?
    Thanks

     

    Antworten
  3. Hello, my app sometimes experiences crashes when I try to re-initiate the service browser or the service advertiser. Do you have any insights on this? Thank you.

    Antworten
  4. What about slow transferring speeds? I completely understand that BlueTooth can be way slower than WiFi but even using MultiPeer over WiFi only it’s extremely slow. Same file, on the same network transfers really fast when using let’s say a Web Server. I’ve searched a lot regarding this issue and found only that many others are experiencing the same.

    At the end I simply run a Web Server over WiFi for transferring big files. Here however I found another issue. When server runs and BlueTooth is on one side of the connection the sever stops responding or at least drops speed. It appears that there is some interference between BT and WiFi as if I turn BT off all works fine. And the problem is caused by the MultiPeer simply because if I stop everything from this framework (browsing, advertising etc.) while running the web server BlueTooth is no longer slowing or stopping the server or causing any problems.

    Still trying to find solution as the framework is nice.

    Antworten
  5. Awesome!

    I’m struggling with really unstable MC myself.

    Will try out the mentioned workarounds.

    Antworten
    • I’m glad to be of help.

      Some of the bugs are fixed on iOS 8 so if you’re not planning on supporting iOS 7, you might not need those hacks.

      Antworten

Einen Kommentar abschicken

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind markiert *