Multipeer Connectivity not working after Xcode 11 update
Asked Answered
W

5

6

I am trying to build a basic application to send messages between nearby iOS devices with Multipeer Connectivity Framework. I have tried many tutorials but it seems in Xcode 11 browsing nearby devices and accepting requests does not work as it did before.

Here is my view controller & delegate methods:

import UIKit
import MultipeerConnectivity

class ViewController: UIViewController,MCSessionDelegate,MCBrowserViewControllerDelegate {

var peerID: MCPeerID?
var session: MCSession?

override func viewDidLoad() {
   super.viewDidLoad()
   peerID = MCPeerID(displayName: UIDevice.current.name)
   session = MCSession(peer: peerID!, securityIdentity: nil, encryptionPreference: .none)
   session!.delegate = self
}

func session(_ session: MCSession, peer peerID: MCPeerID, didChange state: MCSessionState) {
    switch state {
    case .connected: print("Connected to \(peerID.displayName)")
    case .connecting: print("Connecting: \(peerID.displayName)")
    case .notConnected: print("Not Connected: \(peerID.displayName)")
    default: print("")
    }
}

func session(_ session: MCSession, didReceive data: Data, fromPeer peerID: MCPeerID) {

}

func session(_ session: MCSession, didReceive stream: InputStream, withName streamName: String, fromPeer peerID: MCPeerID) {

}

func session(_ session: MCSession, didStartReceivingResourceWithName resourceName: String, fromPeer peerID: MCPeerID, with progress: Progress) {

}

func session(_ session: MCSession, didFinishReceivingResourceWithName resourceName: String, fromPeer peerID: MCPeerID, at localURL: URL?, withError error: Error?) {

}

func browserViewControllerDidFinish(_ browserViewController: MCBrowserViewController) {
    browserViewController.dismiss(animated: true, completion: nil)
}

func browserViewControllerWasCancelled(_ browserViewController: MCBrowserViewController) {
    browserViewController.dismiss(animated: true, completion: nil)
}

}

I've added 2 buttons in the UI, one for hosting and the other one for joining. Here are the methods:

@IBAction func hostBtnTapped(_ sender: Any) {
    hostSession()
}

@IBAction func joinBtnTapped(_ sender: Any) {
    joinSession()
}

They call:

func hostSession() {
    let advertiser = MCAdvertiserAssistant(serviceType: "mg-testing", discoveryInfo: nil, session: session!)
    advertiser.start()
}
func joinSession() {
    let browser = MCBrowserViewController(serviceType: "mg-testing", session: session!)
    browser.delegate = self
    self.present(browser, animated: true, completion: nil)
}

When I compile & run it, everything looks fine. I click "Host" in my first device and enter "Join" mode in my second device (the MCBrowserViewController shows up), but the joining device never detects the hosting device. There is no output in console and there are no errors. The joining device's "Searching..." indicator turns forever without any nearby devices showing up. What might be causing this? How can I solve it? I'm using Xcode 11.0 and iOS 12 & 13.

Wakayama answered 25/10, 2019 at 18:7 Comment(0)
W
11

It seems MCAdvertiserAssistant and MCBrowserViewController are not updated for latest versions of iOS & Swift, hence they're not working properly.

I solved it by using MCNearbyServiceAdvertiser instead of MCAdvertiserAssistant and MCNearbyServiceBrowser instead of MCBrowserViewController. Note that by using these classes, you'll need to perform basic operations yourself such as listing founded devices, showing and handling invitation alert.. etc.

You can use these classes as below.

Definition:

 var advertiser: MCNearbyServiceAdvertiser!
 var browser: MCNearbyServiceBrowser!

Initialization:

advertiser = MCNearbyServiceAdvertiser(peer: peerID, discoveryInfo: nil, serviceType: "my-test")
browser = MCNearbyServiceBrowser(peer: peerID, serviceType: "my-test")

Assigning delegates & starting:

advertiser.delegate = self
advertiser.startAdvertisingPeer()
browser.delegate = self
browser.startBrowsingForPeers()
Wakayama answered 26/10, 2019 at 21:48 Comment(3)
Yes the problem of MCAdvertiserAssistant exists in the latest Xcode 11.5. Using MCNearbyServiceAdvertiser fixes my problem. Thx!Rayburn
Did you submit a bug report for this?Tlaxcala
use this link to see YouTube vid to sort this problem out with Xcode 12 plus. It worked for me youtube.com/watch?v=WemXz8yp9CYJerryjerrybuild
D
9

Based on the answer of my predecessor, the biggest problem seems to be the MCAdvertiserAssistant. MCBrowserViewController works fine fore me.

If you have code that should run based on MCAdvertiserAssistant and MCBrowserViewController, try to replace the MCAdvertiserAssistant with MCNearbyServiceAdvertiser.

So this is everything you need, if you for example work on Project 25 in the 100 days of Swift by Paul Hudson. (Which I just did when I had to find a solution for the problem)

var advertiser: MCNearbyServiceAdvertiser!
advertiser = MCNearbyServiceAdvertiser(peer: peerID, discoveryInfo: nil, serviceType: "my-test")
advertiser.delegate = self
advertiser.startAdvertisingPeer()

Add the MCNearbyServiceAdvertiserDelegate to your protocol list and implement the delegate method:

func advertiser(_ advertiser: MCNearbyServiceAdvertiser, didReceiveInvitationFromPeer peerID: MCPeerID, withContext context: Data?, invitationHandler: @escaping (Bool, MCSession?) -> Void) {
        invitationHandler(true, mcSession)
    }

This would just accept every connection but it is enough too so that it should work.

For further investigation: It seems to be a problem with the new Xcode template and the new UIApplicationSceneManifest in the info.plist and the new SceneDelegate for the multi-windows support.

When I changed it to the old info plist and AppDelegate system MCAdvertiserAssistant works just fine even with a new Xcode and Swift.

ps. Based on latest Xcode 11.2 Beta 2

Darnel answered 27/10, 2019 at 22:1 Comment(3)
The last section about the manifest saved me from a headache!Bagpipes
It also seems important to retain a pointer to var advertiser (don't declare inside a func).Turbofan
Your great solution works for me. I'm using the latest Xcode 11.5, and latest simulator and iOS version for my iPhone 7.Rayburn
P
3

This issue is caused by the UISceneDelegate. It should be good to go after you opt out of the UISceneDelegate and rebuild your project.

For more information about how to opt out of the UISceneDelegate, please take a look at the post: Xcode 11 - Opt out of UISceneDelegate/SwiftUI on iOS 13

Paludal answered 14/1, 2020 at 15:31 Comment(0)
C
3

For Xcode 12 and iOS 14:

Come from hackingwithSwift project 25 too, it takes me 2 hours to compare the code:( Eventually, I think it is some issue with Xcode version or iOS version.

To fix it, first replace MCAdvertiserAssistant with MCNearbyServiceAdvertiser.

Change startHosting method as below:

    func startHosting(action: UIAlertAction) {
        advertiser = MCNearbyServiceAdvertiser(peer: peerID, discoveryInfo: nil, serviceType: "hws-project25")
        advertiser.delegate = self
        advertiser.startAdvertisingPeer()
    }

And implement this new method: I add an alertViewController to show the grant prompt because it will not appear when we do above changes.

    func advertiser(_ advertiser: MCNearbyServiceAdvertiser, didReceiveInvitationFromPeer peerID: MCPeerID, withContext context: Data?, invitationHandler: @escaping (Bool, MCSession?) -> Void) {
        let ac = UIAlertController(title: "Project25", message: "'\(peerID.displayName)' wants to connect", preferredStyle: .alert)
        ac.addAction(UIAlertAction(title: "Accept", style: .default, handler: { [weak self] _ in
            invitationHandler(true, self?.mcSession)
        }))
        ac.addAction(UIAlertAction(title: "Decline", style: .cancel, handler: { _ in
            invitationHandler(false, nil)
        }))
        present(ac, animated: true)
    }

One more thing:

For iOS 14, You also need to add this 2 properties in info.plist if you want to use multipeer connection.

  1. Privacy - Local Network Usage Description: "your message"
  2. Bonjour services: _yourServiceTypeName._tcp
Coherent answered 25/12, 2020 at 14:30 Comment(2)
I am unable to search device while joining session. Note: I have started Host from real device and joining from simulator.Bradski
Bonjour services: _yourServiceTypeName _.tcp" , i think this one is in the correct format. Ignore the space between _yourServiceTypeName and _.tcpParticia
Y
0

MCAdvertiserAssistant and MCBrowserViewController are not deprecated and still work fine, even in iOS 16 (Xcode 14). However, as described in the other answer, two entries need to be added in info.plist:

  1. Privacy - Local Network Usage Description: yourMessage
  2. Bonjour services: _yourServiceTypeName._tcp

Without these entries, you will not see the second device in the connectivity browser.

Yellowthroat answered 3/10, 2022 at 14:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.