I found this answer - https://stackoverflow.com/a/51319158/4833705 on Stack Overflow. The work around is whatever view controller that is using CallKit you simply make sure that it doesn't get initialized inside that view controller if the country is China. Like this
var callObserver: CXCallObserver?
override func viewDidLoad() {
super.viewDidLoad()
if Locale.current.regionCode != "CN" ||
Locale.current.regionCode != "CHN" { // only init for countries that aren't China
callObserver = CXCallObserver()
}
}
Post
Replies
Boosts
Views
Activity
Yes, TCP and Websocket, here is the send message:
let info = // some String info
guard let data = try? NSKeyedArchiver.archivedData(withRootObject: info, requiringSecureCoding: true) else {
return
}
let message = NWProtocolWebSocket.Metadata(opcode: .text)
let context = NWConnection.ContentContext(identifier: "send", metadata: [message])
connection.send(content: data, contentContext: context, isComplete: true, completion: .contentProcessed({ (error) in
if let error = error { print("\(error.localizedDescription)"); return }
print("Success")
}))
The NWListener, NWBrowser, and NWConnection all are initialized with let tcpOptions = NWProtocolTCP.Options() inside their NWParameters.
Yes, they both have the same exact code. Btw neither device receives the echo but they both receive that first message.
Here is startOutgoingConnection, actually here is PeerConnection because it is 1 class that has 2 initializers, one for outgoing and one for incoming. I put break points everywhere but nada on that 2nd incoming message.
protocol PeerConnectionDelegate: class {
func sendOutgoing(_ connection: NWConnection)
func receivedIncoming(_ connection: NWConnection)
}
class PeerConnection: Hashable {
static func == (lhs: PeerConnection, rhs: PeerConnection) - Bool {
return lhs.connection?.endpoint.debugDescription == rhs.connection?.endpoint.debugDescription
}
func hash(into hasher: inout Hasher) {
guard let connection = connection else { return }
hasher.combine(connection.endpoint.debugDescription)
}
weak var delegate: PeerConnectionDelegate?
public var connection: NWConnection?
// Outgoing
init(endPoint: NWEndpoint, delegate: PeerConnectionDelegate) {
self.delegate = delegate
let tcpOptions = NWProtocolTCP.Options()
tcpOptions.enableKeepalive = true
tcpOptions.keepaliveIdle = 2
let parameters = NWParameters(tls: nil, tcp: tcpOptions)
parameters.includePeerToPeer = true
parameters.allowLocalEndpointReuse = true
connection = NWConnection(to: endPoint, using: parameters)
startOutgoingConnection()
}
// Incoming
init(connection: NWConnection, delegate: PeerConnectionDelegate) {
self.delegate = delegate
self.connection = connection
startIncomingConnection()
}
func startIncomingConnection() {
guard let connection = connection else { return }
connection.stateUpdateHandler = { [weak self](nwConnectionState) in
switch nwConnectionState {
case .preparing: print("preparing")
case .setup: print("setting up")
case .waiting(let error): print("\(error.localizedDescription)")
case .ready:
self?.delegate?.receivedIncoming(connection)
case .cancelled:
connection.cancel()
// remove incoming connection from an array that holds it/them and remove corresponding cell
case .failed(let error):
connection.cancel()
// remove incoming connection from an array that holds it/them and remove corresponding cell
default:break
}
}
connection.start(queue: .main)
}
func startOutgoingConnection() {
guard let connection = connection else { return }
connection.stateUpdateHandler = { [weak self](nwConnectionState) in
switch nwConnectionState {
case .preparing: print("preparing")
case .setup: print("setting up")
case .waiting(let error): print(error.localizedDescription)
case .ready:
self?.delegate?.sendOutgoing(connection)
case .cancelled:
connection.cancel()
// remove outgoing connection from an array that holds it/them
case .failed(let error):
connection.cancel()
// remove outgoing connection from an array that holds it/them
default:break
}
}
connection.start(queue: .main)
}
}
The connection isn't failing on either end/device because once it is cancelled I remove data from both devices which never happens after they both send and receive messages from each other. For example Device_A sends its info to Device_B and vice versa. I have a cell that shows the connection info String data like connection.endpoint.debugDescription and the uid from the opposite device. Once either device goes to the background and the connection is cancelled then both devices remove the corresponding cell.
If the connection was cancelled after the initial send/receive then both cells (the cell on each device) would immediately disappear.
@eskimo ok thanks for the answer. I'll start a new thread about the issue that you just identified. As far as this one, how do I know who is talking to who by using self inside the receive(_:) method?
Once a sender sends a message, I used these for the receiver to identify the sender:
let endPointDescription = connection.currentPath?.localEndpoint?.debugDescription ... encode then send
let remoteEndPoint = connection.currentPath?.remoteEndpoint?.debugDescription ... encode then send
// they both get sent together
This works fine when detecting when either end is cut off, for example if Device_A is connected to Device_B, if Device_B goes to the background the connection is cut and Device_A immediately knows about it. I haven't had any problems even when using 3 devices and 2 simulators. They all will know that Device_B has left the building and respond accordingly.
@eskimo Thanks for the reply.
Here are the successful print statements:
The Initial Send does not send a color, it just sends some data like a String message :
print("Sent") // successful
After the Initial Send sends the message and it is first received, I didn't add a print statement for a successful reception but the code inside Receive data and send Echo response is successful, specifically
if let data = data, !data.isEmpty { // successful only once }
Inside there, the data that is decoded, only has the String message from above, the color isn't sent yet so the colorData decoding is skipped. However once the String message is received, I send an echo with the colorData
connection.send(content: colorData, ... { }
Inside the completionHandler of the echo, this print statement always runs:
print("Color data sent") // successful
At that point the other device, the Receive Data receives nothing, there isn't a response whatsoever. Nothing prints, it's as if the connection got cut off after the first reception. I put breakpoints everywhere but none of them get hit.
I forgot to add the listener code
let tcpOptions = NWProtocolTCP.Options()
tcpOptions.enableKeepalive = true
tcpOptions.keepaliveIdle = 2
let parameters = NWParameters(tls: nil, tcp: tcpOptions)
parameters.includePeerToPeer = true
parameters.allowLocalEndpointReuse = true
Thanks for the answer! Exactly what I was looking for :)
I got the answer from here: - https://github.com/fitomad/NearbyInteraction-Guide/blob/master/NearbyInteraction-Framework-Guide.md
The distance is a Float type whose value is measured in meters: func session(_ session: NISession, didUpdate nearbyObjects: [NINearbyObject]) {		
		guard let object = nearbyObjects.first else { return }
		
		guard let distance = object.distance else { return }
		let meters = String(format: "%.2f m", distance)
		print("meters: ", meters)
}
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]()
It turns out this seems to be an issue with the simulator. I've been playing around and searching the internet for 12 hrs and nothing. I finally tried it out on my real device and 2 simulators. I sent the real device to the background and the connection.stateUpdateHandler's .failed and .cancelled worked fine on the simulators.
On the reverse when I sent either of the simulators to the background the real device's connection.stateUpdateHandler didn't get hit.
I don't have another real device on hand to try to see what happens with 2 real devices. This is a strange issue.
Any ideas out there?
Here is the reason for the the multiple call problem, which isn't a problem, as explained by @eskimo here: - https://developer.apple.com/forums/thread/654953
Apple’s connect-by-name API uses a fancy algorithm to run multiple connections in parallel to see which one connects first (see RFC 8305 for - https://tools.ietf.org/html/rfc8305 some background on this)
Here is the reason for the the multiple call problem, which isn't a problem, as explained by @eskimo here: - https://developer.apple.com/forums/thread/654953
Apple’s connect-by-name API uses a fancy algorithm to run multiple connections in parallel to see which one connects first (see RFC 8305 for - https://tools.ietf.org/html/rfc8305 some background on this)
I found the answer to my problem here - https://www.example.com/
@eskimo said:
. TCP is not a message-oriented protocol, and thus
receiveMessage(…)
doesn’t make any sense [1]. What you want is
receive(minimumIncompleteLength:maximumLength:completion:)
And he is 100% correct, as soon as I changed signatures, the callback was entered:
connection.receive(minimumIncompleteLength: 1, maximumLength: 8192) { [weak self](data, context, isComplete, error) in
print("\n*** call back entered *** ")
if let err = error {
print("Recieve error: \(err.localizedDescription)")
return
}
print("databytes", data?.count as Any)
if let data = data, !data.isEmpty {
print("Receive is complete")
self?.received(data, from: connection)
}
}
The multiple calls is still happening be that's another question that I'll ask in a different post.
PS I don't know why I have 3 different usernames under the same account
lsamaria - https://developer.apple.com/forums/profile/lsamaria is the same person as ohmannnnnn - https://developer.apple.com/forums/profile/ohmannnnnn who is the same person as lance145 - https://developer.apple.com/forums/profile/lance145
@theepicapple hi, was you able to get around this issue? I'm currently in the same situation
I don't know how to edit my question but I forgot to add that I also have this set: playerItem?.preferredForwardBufferDuration = TimeInterval(1.0)