Combine multiple outgoing NISessions with multiple NINearbyPeerConfiguration objects when data is received using Bonjour

Because of the 8 peer limit I'm using NWListener, NWBrowser, NWConnection, and Bonjour for up to 20 peer-to-peer concurrent connections.

I followed this answer and this answer which are both from Apple engineers.

The first one said to create multiple concurrent NISessions:

All NISessions are peer-to-peer and as a result, creating multiple NISession objects is necessary to have multiple concurrent sessions.

One approach is to create a dictionary between an identifier for the peer (i.e. a user identifier provided by your app MyAppUserID) and the NISession objects while also keeping track of the NIDiscoveryToken for each peer identifier: 

var sessions = [MyAppUserID: NISession]()
var peerTokensMapping = [NIDiscoveryToken: MyAppUserID]()

And the second answer said to perform interactions with multiple iPhones:

Create an NISession for each peer you would like to interact with. For example, if you are interacting with two peers, create 2 * NISession objects.

Each NISession will have a unique NIDiscoveryToken associated with it. Share discovery token #1 with peer #1, and share discovery token #2 with peer #2.

When you receive discovery tokens from peers #1 and #2, create 2 * NINearbyPeerConfiguration objects and use them to run sessions #1 and #2, respectively.

The problem is when sending out a NIDiscoveryToken via NWConnection, I can't find a way to link to the NISession from the sent out data to the token that is received from the incoming data after I initialize a NINearbyPeerConfiguration object:

eg.

User object which is sent and received when other devices are discovered
Code Block
class User: NSObject, NSSecureCoding {
var uid: String?
var peerToken: NIDiscoveryToken? = nil
init(uid: String, peerToken: NIDiscoveryToken) {...}
// ... encoder for uid and peerToken
// ... decoder for uid and peerToken
}


Send current user's NIDiscoveryToken data via NWBrowser and NWConnection and save it to the
peerTokensMapping dictionary and save the session to the sessionsArr

Code Block
let currentUserId = "qwerty"
var sessionsArr = [NISession]()
var sessions = [String: NISession]()
var peerTokensMapping = [NIDiscoveryToken: String]()
func sendDataWhenNewDeviceIsDiscovered() {
let session = NISession()
session.delegate = self
guard let myToken = session.discoveryToken else { return }
let user = User(uid: currentUserId, peerToken: myToken)
guard let outgoingData = try? NSKeyedArchiver.archivedData(withRootObject: user,
requiringSecureCoding: true)
else { return }
let message = NWProtocolWebSocket.Metadata(opcode: .text)
let context = NWConnection.ContentContext(identifier: "send", metadata: [message])
connection.send(content: outgoingData, contentContext: context, isComplete: true, completion: .contentProcessed({ [weak self](error) in
       
     if let error = error { return }
self?.sessionsArr.append(session)
self?.peerTokensMapping[myToken] = self!.currentUserId
     print("Successfully Sent")
}))
}


Receive other user's NIDiscoveryToken data via NWListener and NWConnection.

Code Block
connection.receive(minimumIncompleteLength: 1, maximumLength: 65535) {
[weak self](data, context, isComplete, error) in
if let err = error { return }
if let data = data, !data.isEmpty {
self?.decodeReceived(data)
}
}
func decodeReceived(_ data: Data) {
guard let incomingData = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? User
else { return }
guard let uid = incomingData.uid, let peerToken = incomingData.peerToken
else { return }
let config = NINearbyPeerConfiguration(peerToken: peerToken)
/* not sure what to do here to get the session that was created when the data was sent out */
for (key,_) in peerTokensMapping {
if key.??? == peerToken.??? {
session.run(config)
self.sessions[currentUserId] = session
break
}
}
/* or try this but this is the other user's peerToken so this will never work */
for session in self.sessionsArr {
if session.discoveryToken ?? "" == peerToken {
session.run(config)
self.sessions[currentUserId] = session
break
}
}
}


Once the NINearbyPeerConfiguration is initialized how do I connect the incoming peerToken with the correct one that was sent out above that is currently inside the peerTokensMapping dict or the sessionsArr so that I can
get the session and call session.run(config)

Replies

I couldn't add these because the Edit button disappeared.

1- For clarity sendDataWhenNewDeviceIsDiscovered() {...} will run every time a new device is discovered but there will only be 20 sessions at a time. This won't stop it from running but the incomingData will never be added if the sessions are at 20.

2- In place of:
var sessions = [MyAppUserID: NISession]()

I used:
var sessions = [String: NISession]()

The problem is because the current user's userId is always the key, it will only have one key the will write always over its value
self.sessions[currentUserId] = session

I wasn't sure what was supposed to happen here. Did you mean:
var sessions = [NISession: MyAppUserID]()
or
var sessions = [NISession: String]()
  • Hi there...

    I am also trying to connect with multiple device using Nearby Interaction, and my code is working on simulator but not working in real device, did you resolve you issue

    Thank you

Add a Comment