Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multipeer Connectivity not working after Xcode 11 update

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.

like image 229
Muhammed Gül Avatar asked Oct 25 '19 18:10

Muhammed Gül


4 Answers

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()
like image 67
Muhammed Gül Avatar answered Nov 18 '22 23:11

Muhammed Gül


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

like image 39
Nico Tasche Avatar answered Nov 18 '22 21:11

Nico Tasche


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

like image 3
Shan Avatar answered Nov 18 '22 23:11

Shan


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
like image 3
ChuckZHB Avatar answered Nov 18 '22 21:11

ChuckZHB