Hi.
I have three problems implementing the MultipeerConnectivity framework.
1) I am trying to connect three players to one MCSession. Every action that one peer does should be repeated by other peers. If I connect like this:
A (iPad device) -> C (Simulator)
B (iPhone device) -> C
all actions performed by A and B are received by in C. However, when A is advertising and I connect B and C to A, whatever I do on B is not repeated on C (my Simulator). How to ensure that messages are sent all directions (three-way or four way)?
2) Whenever one peer leaves the session, I am trying to reconnect the same peer to the same MCSession, but it never does. Why is this?
3) If I disconnect peer A that was originally browsing and to which other peers connected, peer B starts crashing with the following message: "message sent to deallocated instance <MCPEERID A>" whenever I do any actions on it. Is there a way to preserve the session and other peers if they joined the peer that left?
This is my class ConnectionManager class
import Foundation
import MultipeerConnectivity
protocol ConnectionManagerDelegate
{
func foundPeer()
func lostPeer(peerID: String)
func invitationWasReceived(fromPeer: String)
func connectedWithPeer(peerID: MCPeerID)
}
class ConnectionManager : NSObject, MCSessionDelegate, MCNearbyServiceBrowserDelegate, MCNearbyServiceAdvertiserDelegate, StreamDelegate
{
var delegate: ConnectionManagerDelegate?
private let CONNECTION_SERVICE_TYPE = "play-service"
public var session: MCSession!
public var peer: MCPeerID!
public var browser: MCNearbyServiceBrowser!
public var advertiser: MCNearbyServiceAdvertiser!
public var foundPeers = [MCPeerID]()
public var invitationHandler: ((Bool, MCSession?)->Void)!
var outputStream:OutputStream?
override init()
{
super.init()
peer = MCPeerID(displayName: UIDevice.current.name)
session = MCSession(peer: peer, securityIdentity: nil, encryptionPreference: MCEncryptionPreference.optional)
session.delegate = self
browser = MCNearbyServiceBrowser(peer: peer, serviceType: CONNECTION_SERVICE_TYPE)
browser.delegate = self
advertiser = MCNearbyServiceAdvertiser(peer: peer,discoveryInfo: nil,serviceType: CONNECTION_SERVICE_TYPE)
advertiser.delegate = self
}
deinit
{
browser.stopBrowsingForPeers()
advertiser.stopAdvertisingPeer()
}
func browser(_ browser: MCNearbyServiceBrowser,
foundPeer peerID: MCPeerID,
withDiscoveryInfo info: [String : String]?)
{
foundPeers.append(peerID)
delegate?.foundPeer()
}
func browser(_ browser: MCNearbyServiceBrowser, lostPeer peerID: MCPeerID)
{
for (index, aPeer) in foundPeers.enumerated()
{
if aPeer == peerID
{
foundPeers.remove(at: index)
break
}
}
delegate?.lostPeer(peerID: peerID.displayName)
}
func browser(_ browser: MCNearbyServiceBrowser,
didNotStartBrowsingForPeers error: Error)
{
print("DID NOT START BROWSING FOR PEERS",error.localizedDescription)
}
func advertiser(_ advertiser: MCNearbyServiceAdvertiser,
didReceiveInvitationFromPeer peerID: MCPeerID,
withContext context: Data?,
invitationHandler: @escaping (Bool, MCSession?) -> Void)
{
self.invitationHandler = invitationHandler
delegate?.invitationWasReceived(fromPeer: peerID.displayName)
}
func advertiser(_ advertiser: MCNearbyServiceAdvertiser,
didNotStartAdvertisingPeer error: Error)
{
print("DID NOT START ADEVRTISING FOR PEERS",error.localizedDescription)
}
func session(_ session: MCSession,
peer peerID: MCPeerID,
didChange state: MCSessionState)
{
switch state
{
case MCSessionState.connected:
delegate?.connectedWithPeer(peerID: peerID)
startSession(peer: peerID)
case MCSessionState.connecting:
print("Connecting to session: \(session)")
case MCSessionState.notConnected:
session.disconnect()
print("NOT connected to session: \(session)")
default:
print("Did not connect to session: \(session)")
stopSession()
}
}
func session(_ session: MCSession,
didReceiveCertificate certificate: [Any]?,
fromPeer peerID: MCPeerID,
certificateHandler: @escaping (Bool) -> Void)
{
certificateHandler(true)
}
func startSession(peer peerID: MCPeerID)
{
do
{
if outputStream == nil
{
try outputStream = self.session.startStream(withName: "stream", toPeer: peerID)
}
if let outputStream = outputStream
{
outputStream.delegate = self
outputStream.schedule(in: RunLoop.main, forMode:RunLoopMode.defaultRunLoopMode)
outputStream.open()
}
}
catch let error
{
print("ERROR STARTING STREAM IN SESSION: \(error)")
}
}
func stream(_ aStream: Stream, handle eventCode: Stream.Event)
{
switch(eventCode)
{
case Stream.Event.hasBytesAvailable:
print("--STREAM EVENT HAS BYTES AVAILABLE--")
processInput(from: aStream)
break
case Stream.Event.hasSpaceAvailable:
print("--STREAM EVENT HAS SPACE AVAILABLE--")
break
case Stream.Event.endEncountered:
print("--STREAM EVENT END ENCOUNTERED--")
break
case Stream.Event.errorOccurred:
print("--STREAM EVENT ERROR OCCURRED--")
break
default:
print("--STREAM EVENT DEFAULT--")
break
}
}
func stopSession()
{
closeConnection()
}
func closeConnection()
{
if(self.outputStream != nil)
{
self.outputStream?.remove(from: RunLoop.current, forMode: RunLoopMode.defaultRunLoopMode)
self.outputStream?.close()
self.outputStream = nil;
}
if(session != nil)
{
session.disconnect()
}
}
func sendDataToPeer()
{
if session.connectedPeers.count > 0
{
let message = "This is a string test"
let data = NSKeyedArchiver.archivedData(withRootObject: message) //this is a NSData instance. Could be any NSData, as a serialized object, for example.
if let outputStream = outputStream
{
_ = data.withUnsafeBytes { outputStream.write($0, maxLength: data.count) }
}
}
}
func processInput(from aStream: Stream)
{
let input = aStream as! InputStream
var buffer = [UInt8](repeating: 0, count: 1024) //allocate a buffer. The size of the buffer will depended on the size of the data you are sending.
let bytesRead = input.read(&buffer, maxLength:buffer.count) // Read buffer
if (bytesRead>0)
{
let dataString = NSData(bytes: &buffer, length: bytesRead)
let message = NSKeyedUnarchiver.unarchiveObject(with: dataString as Data) as! String //deserializing the NSData
print("STREAM: ",message)
}
else if (bytesRead < 0)
{
print("Input stream has less than 0 bytes")
// Close connection
//stopSession() TO DO!!! Disabled for now. Should the session continue?
}
else if bytesRead == 0
{
print("Input stream has 0 bytes")
}
}
func session(_ session: MCSession,
didReceive stream: InputStream,
withName streamName: String,
fromPeer peerID: MCPeerID)
{
if streamName == "stream"
{
stream.delegate = self
stream.schedule(in: RunLoop.main, forMode: RunLoopMode.defaultRunLoopMode)
stream.open()
}
}
}Thank you!