I'm learning the basics of MultipeerConnectivity
framework. I was trying to just send some data between two simulators, so I set up a small project and I've implemented the MCBrowserViewControllerDelegate
, MCNearbyServiceAdvertiserDelegate
and MCSessionDelegate
protocols. I added three buttons, with a corresponding action each - names are pretty self-explanatory I guess:
advertiseButton
, with aadvertise()
associated method;joinButton
, with ajoin()
associated method;sendDataButton
, with asendData()
associated method.
With my code, I was actually able to connect two simulator devices, since I get a "Connected" output printed in the console, however after a few seconds from the connection I get a bunch of errors logged in the console:
[GCKSession] Not in connected state, so giving up for participant [1935E9EC] on channel [0].
[GCKSession] Not in connected state, so giving up for participant [1935E9EC] on channel [1].
[GCKSession] Not in connected state, so giving up for participant [1935E9EC] on channel [2].
[GCKSession] Not in connected state, so giving up for participant [1935E9EC] on channel [3].
I might be wrong, but I guess it means that the connection was lost.
This happens every time, so it might be some implementation in my code done wrong. My first question is: why does it happen and how can I fix it?
Moreover, if I tap on the sendData
button on one simulator no data is received on the other simulator device, the didReceive data
callback of MCSessionDelegate
is never called. I'm almost sure it is due to the above log, however this occurs also if I press the button after the connection and slightly prior of the warning.
So, basically, what am I doing wrong in my code?
I'm using XCode 14.3, testing on simulators iPhone 14 and iPhone 14 Pro.
EDIT: just tested on real devices, same issue.
Here is the full sample project, just copy-paste it, add privacy settings in Info.plist
if needed, then build&run:
import UIKit
import MultipeerConnectivity
final class ViewController: UIViewController {
// MARK: - Connectivity
private var peerID: MCPeerID!
private var session: MCSession!
private var nearbyServiceAdvertiser: MCNearbyServiceAdvertiser!
// MARK: - Buttons
private lazy var advertiseButton: UIButton = {
let button = UIButton()
button.addTarget(self, action: #selector(advertise), for: .touchUpInside)
button.setTitle("Advertise", for: .normal)
button.setTitleColor(.blue, for: .normal)
button.titleLabel?.font = UIFont(name: "Arial", size: 24)
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
private lazy var joinButton: UIButton = {
let button = UIButton()
button.addTarget(self, action: #selector(join), for: .touchUpInside)
button.setTitle("Join", for: .normal)
button.setTitleColor(.blue, for: .normal)
button.titleLabel?.font = UIFont(name: "Arial", size: 24)
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
private lazy var sendDataButton: UIButton = {
let button = UIButton()
button.addTarget(self, action: #selector(sendData), for: .touchUpInside)
button.setTitle("Send Data", for: .normal)
button.setTitleColor(.blue, for: .normal)
button.titleLabel?.font = UIFont(name: "Arial", size: 24)
button.translatesAutoresizingMaskIntoConstraints = false
return button
}()
// MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
view.addSubview(advertiseButton)
view.addSubview(joinButton)
view.addSubview(sendDataButton)
peerID = MCPeerID(displayName: UIDevice.current.name)
session = MCSession(peer: peerID, securityIdentity: nil, encryptionPreference: .required)
session.delegate = self
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
advertiseButton.frame = CGRect(x: 0, y: 500, width: view.frame.size.width, height: 50)
joinButton.frame = CGRect(x: 0, y: 600, width: view.frame.size.width, height: 50)
sendDataButton.frame = CGRect(x: 0, y: 700, width: view.frame.size.width, height: 50)
}
// MARK: - Selectors
@objc private func advertise() {
nearbyServiceAdvertiser = MCNearbyServiceAdvertiser(peer: peerID, discoveryInfo: nil, serviceType: "test")
nearbyServiceAdvertiser.delegate = self
nearbyServiceAdvertiser.startAdvertisingPeer()
}
@objc private func join() {
let browser = MCBrowserViewController(serviceType: "test", session: session)
browser.delegate = self
present(browser, animated: true)
}
@objc private func sendData() {
let stringToSend: String = "randomstring"
if let data = stringToSend.data(using: .utf8) {
try? session.send(data, toPeers: session.connectedPeers, with: .reliable)
} else {
print("error converting data from string")
}
}
}
// MARK: - MCSessionDelegate
extension ViewController: MCSessionDelegate {
func session(_ session: MCSession, peer peerID: MCPeerID, didChange state: MCSessionState) {
switch state {
case .notConnected:
print("Not connected: \(peerID.displayName).")
case .connecting:
print("Connecting: \(peerID.displayName).")
case .connected:
print("Connected: \(peerID.displayName).")
@unknown default:
fatalError()
}
}
func session(_ session: MCSession, didReceive data: Data, fromPeer peerID: MCPeerID) {
if let receivedString = String(data: data, encoding: .utf8) {
print(receivedString)
} else {
print("error converting string from data")
}
}
func session(_ session: MCSession, didReceive stream: InputStream, withName streamName: String, fromPeer peerID: MCPeerID) {
// no operations
}
func session(_ session: MCSession, didStartReceivingResourceWithName resourceName: String, fromPeer peerID: MCPeerID, with progress: Progress) {
// no operations
}
func session(_ session: MCSession, didFinishReceivingResourceWithName resourceName: String, fromPeer peerID: MCPeerID, at localURL: URL?, withError error: Error?) {
// no operations
}
}
// MARK: - MCBrowserViewControllerDelegate
extension ViewController: MCBrowserViewControllerDelegate {
func browserViewControllerDidFinish(_ browserViewController: MCBrowserViewController) {
browserViewController.dismiss(animated: true)
}
func browserViewControllerWasCancelled(_ browserViewController: MCBrowserViewController) {
browserViewController.dismiss(animated: true)
}
}
// MARK: - MCNearbyServiceAdvertiserDelegate
extension ViewController: MCNearbyServiceAdvertiserDelegate {
func advertiser(_ advertiser: MCNearbyServiceAdvertiser, didReceiveInvitationFromPeer peerID: MCPeerID, withContext context: Data?, invitationHandler: @escaping (Bool, MCSession?) -> Void) {
invitationHandler(true, session)
}
}