Getting Started with Multipeer Connectivity in Swift

Getting Started with Multipeer Connectivity in Swift

Learn the basics of the Multipeer Connectivity framework.

When we need to establish a connection between nearby devices without relying on an internet connection, we can use the Multipeer Connectivity framework.

This framework allows us to discover services advertised by other devices and exchange information in various ways through peer-to-peer connections using Wi-Fi, Bluetooth, or peer discovery without an internet connection.

For implementing this functionality within your app, you need to configure some permissions in the Info.plist:

Each device in a session process needs a unique identity, represented by an MCPeerID. A name visible to other peers in displayName will return something like "Gabriel's iPhone."

To ensure that only devices using the same service connect, use serviceID, a unique identifier for the service that peers advertise or search for. It must be a maximum of 15 characters and contain only lowercase letters, numbers, and hyphens.

private let peerID = MCPeerID(displayName: UIDevice.current.name)

private let serviceID = "demo-start"

The MCSession class enables and manages communication between all peers in a multipeer connectivity session. So, of the required parameters, securityIdentity: nil means we are not using custom certificates, and encryptionPreference: .required ensures that all transmitted data is encrypted. Complement with the MCSessionDelegate protocol to monitor state changes and receive data.

private var session: MCSession
session = MCSession(peer: peerID, securityIdentity: nil, encryptionPreference: .required)
session.delegate = self

To provide a user interface for handling invitations from your app, use MCAdvertiserAssistant. It displays prompts to automatically accept or reject connections.

The application will require the discoveryInfo parameter, which may contain additional data about the peer, such as device type or app version.

assistant = MCAdvertiserAssistant(serviceType: serviceID, discoveryInfo: nil, session: session)
assistant?.start()

The MCBrowserViewController class is the essential interface for browsing and connecting to advertising peers. It displays nearby devices and allows the user to invite them to a session. This displays a list of nearby devices advertising the same service ID. In addition, the MCBrowserViewControllerDelegate delegate handles screen cancellation events.

let browser = MCBrowserViewController(serviceType: serviceID, session: session)
browser.delegate = self

There are also some mandatory protocols that monitor events within the session. For example, didChange state is useful for knowing when a peer connects, disconnects, or is trying to connect. Other mandatory methods that may remain empty because they are not initially needed are didReceive data, didReceive stream, didStartReceivingResourceWithName, and didFinishReceivingResourceWithName.

    func session(_ session: MCSession, peer peerID: MCPeerID, didChange state: MCSessionState) {}
    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?) {}

Finally, to allow the user to actually end or cancel the search, use properties of the MCBrowserViewControllerDelegate protocol that control user interaction with the browser.

func browserViewControllerDidFinish(_ browserViewController: MCBrowserViewController)
func browserViewControllerWasCancelled(_ browserViewController: MCBrowserViewController)

This is the main point to get started with the Multipeer Connectivity framework, enough for two nearby devices to find and connect. I also leave a suggestion to cover all the code presented:

import Foundation
import MultipeerConnectivity

class PeerHandler: NSObject, ObservableObject {
    private let serviceID = "demo-start"
    private let peerID = MCPeerID(displayName: UIDevice.current.name)
    
    private var session: MCSession
    private var assistant: MCAdvertiserAssistant?
    
    override init() {
        session = MCSession(peer: peerID, securityIdentity: nil, encryptionPreference: .required)
        super.init()
        session.delegate = self
    }
    
    func startHosting() {
        assistant = MCAdvertiserAssistant(serviceType: serviceID, discoveryInfo: nil, session: session)
        assistant?.start()
    }
    
    func makeBrowser() -> MCBrowserViewController {
        let browser = MCBrowserViewController(serviceType: serviceID, session: session)
        browser.delegate = self
        return browser
    }
}
extension PeerHandler: MCSessionDelegate {
    func session(_ session: MCSession, peer peerID: MCPeerID, didChange state: MCSessionState) {}
    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?) {}
}
extension PeerHandler: MCBrowserViewControllerDelegate {
    func browserViewControllerDidFinish(_ browserViewController: MCBrowserViewController) {
        browserViewController.dismiss(animated: true)
    }
    func browserViewControllerWasCancelled(_ browserViewController: MCBrowserViewController) {
        browserViewController.dismiss(animated: true)
    }
}