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)
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]()
Combine multiple outgoing NISessions with multiple NINearbyPeerConfiguration objects when data is received using Bonjour
 
 
Q