Currently we've 2 iOS Apps, one developed with React-Native
and another with Swift
. Now the problem i'm facing is whenever i tried to connect to the Cloud-MQTT with WebSocket/SSL port using mobile data it's getting auto disconnect in iOS
Swift APP
(If iPhone device and Raspberry device connected to the same network then it's working fine.), the React-Native
app is working fine in all the scenarios.
And if i use Plain MQTT Port
then connection is working fine using mobile data. But problem is Raspberry device is providing connection credentials during Login. And Gateway only giving WebSocket and SSL port of Device. And all the device will have different credentials so applying static value for port will not work.
MQTT Cloud Broker
Server driver.cloudmqtt.com
User : xxxxxx
Password: xxxxxx
Port: 18658 //Only this port is working
SSL Port: 28658
Websockets Port (TLS only): 38658
Mosquitto version: 1.5.7
func connect(host: String, port: Int, username: String?, password: String?, cleanSession: Bool) {
guard !host.isEmpty else {
delegate?.onMqttDisconnected()
return
}
status = .connecting
let clientId = "CONNECTED_GEAR-" + String(ProcessInfo().processIdentifier)
if NetworkMonitor.shared.connection == .wifi {
mqttClient = CocoaMQTT(clientID: clientId, host: host, port: UInt16(port))
if let mqttClient = mqttClient {
UserManager.shared.isConnected = true
mqttClient.username = username
mqttClient.password = password
mqttClient.keepAlive = UInt16(60)
mqttClient.cleanSession = cleanSession
mqttClient.backgroundOnSocket = true
mqttClient.delegate = self
_ = mqttClient.connect()
} else {
UserManager.shared.isConnected = false
delegate?.onMqttError(message: "Mqtt initialization error")
status = .error
}
} else {
let socket = CocoaMQTTWebSocket()
socket.enableSSL = false
mqttClient = CocoaMQTT(clientID: clientId, host: host, port: UInt16(port), socket: socket)
if let mqttClient = mqttClient {
UserManager.shared.isConnected = true
mqttClient.username = username
mqttClient.password = password
mqttClient.keepAlive = UInt16(60)
mqttClient.cleanSession = cleanSession
mqttClient.logLevel = .debug
mqttClient.autoReconnect = false
mqttClient.allowUntrustCACertificate = true
mqttClient.backgroundOnSocket = true
mqttClient.delegate = self
_ = mqttClient.connect()
} else {
UserManager.shared.isConnected = false
delegate?.onMqttError(message: "Mqtt initialization error")
status = .error
}
}
}
When i set enableSSL = false
Network reachable via Cellular
External Host: driver.cloudmqtt.com
External Port: 38658
External User: xxxxxxx
External Password: xxxxxxx
CocoaMQTT(debug): ping
CocoaMQTT(debug): SEND: PING
CocoaMQTT(debug): =========================MQTT 3.1.1=========================
CocoaMQTT(debug): packetFixedHeaderType 192
CocoaMQTT(debug): remainingLen(len: len) [0]
CocoaMQTT(debug): variableHeader []
CocoaMQTT(debug): payload []
CocoaMQTT(debug): =============================================================
CocoaMQTT(info): Connected to 3.83.156.245 : 38658
CocoaMQTT(info): Enable backgrounding socket successfully
CocoaMQTT(debug): SEND: CONNECT(id: CONNECTED_GEAR-933, username: xxxxxxx, password: xxxxxxx, keepAlive : 60, cleansess: false)
CocoaMQTT(debug): =========================MQTT 3.1.1=========================
CocoaMQTT(debug): packetFixedHeaderType 16
CocoaMQTT(debug): remainingLen(len: len) [54]
CocoaMQTT(debug): variableHeader [0, 4, 77, 81, 84, 84, 4, 192, 0, 60]
CocoaMQTT(debug): payload [0, 18, 67, 79, 78, 78, 69, 67, 84, 69, 68, 95, 71, 69, 65, 82, 45, 57, 51, 51, 0, 8, 104, 101, 121, 103, 108, 105, 104, 103, 0, 12, 71, 103, 107, 106, 74, 101, 110, 81, 114, 119, 120, 48]
CocoaMQTT(debug): =============================================================
CocoaMQTT(debug): socket wrote data -192
CocoaMQTT(debug): socket wrote data 0
CocoaMQTT(debug): socket disconnected
Disconnect: Socket closed by remote peer
When i set enableSSL = true
External Host: driver.cloudmqtt.com
External Port: 38658
External User: xxxxxxx
External Password: xxxxxxx
CocoaMQTT(debug): ping
CocoaMQTT(debug): SEND: PING
CocoaMQTT(debug): =========================MQTT 3.1.1=========================
CocoaMQTT(debug): packetFixedHeaderType 192
CocoaMQTT(debug): remainingLen(len: len) [0]
CocoaMQTT(debug): variableHeader []
CocoaMQTT(debug): payload []
CocoaMQTT(debug): =============================================================
CocoaMQTT(info): Connected to 3.83.156.245 : 38658
CocoaMQTT(info): Enable backgrounding socket successfully
CocoaMQTT(debug): socket wrote data -192
CocoaMQTT(debug): socket disconnected
Disconnect: The operation couldn’t be completed. (kCFStreamErrorDomainSSL error -9806.)
CocoaMQTT(info): Try reconnect to server after 1s
CocoaMQTT(info): Connected to 3.83.156.245 : 38658
CocoaMQTT(info): Enable backgrounding socket successfully
CocoaMQTT(debug): Call the SSL/TLS manually validating function
MQTT Over WebSocket and SSL
port but nothing is working.Note:- I've set
AllowArbitraryLoads = true
in info.plist
FYI:- Due to GeoFence issue in React-Native, we've developed applications in Native Swift and Kotlin
At last i found a solution, and now MQTT Connection
and WebSocket Connection
over any network type working fine.
func connect(host: String, port: Int, username: String?, password: String?, cleanSession: Bool) {
guard !host.isEmpty else {
delegate?.onMqttDisconnected()
return
}
status = .connecting
let clientId = "CONNECTED_GEAR-" + String(ProcessInfo().processIdentifier)
//During connection without HomeNetwork wifi, required to connect with WebSocket. That's the main reason to implement different connection request for Network type.
if NetworkMonitor.shared.connection != .wifi {
let websocket = CocoaMQTTWebSocket(uri: "/ws")
mqttClient = CocoaMQTT(clientID: clientId, host: host, port: UInt16(port), socket: websocket)
if let mqttClient = mqttClient {
UserManager.shared.isConnected = true
mqttClient.enableSSL = true
mqttClient.username = username
mqttClient.password = password
mqttClient.keepAlive = UInt16(60)
mqttClient.cleanSession = cleanSession
mqttClient.backgroundOnSocket = true
mqttClient.allowUntrustCACertificate = true
mqttClient.willMessage = CocoaMQTTMessage(topic: "/will", string: "dieout")
mqttClient.autoReconnect = true
mqttClient.logLevel = .debug
mqttClient.delegate = self
_ = mqttClient.connect()
} else {
UserManager.shared.isConnected = false
delegate?.onMqttError(message: "Mqtt initialization error")
status = .error
}
} else {
mqttClient = CocoaMQTT(clientID: clientId, host: host, port: UInt16(port))
if let mqttClient = mqttClient {
UserManager.shared.isConnected = true
mqttClient.delegate = self
mqttClient.username = username
mqttClient.password = password
mqttClient.keepAlive = UInt16(60)
mqttClient.cleanSession = cleanSession
mqttClient.backgroundOnSocket = true
mqttClient.logLevel = .error
mqttClient.deliverTimeout = 15.00
mqttClient.autoReconnect = true
mqttClient.willMessage = CocoaMQTTMessage(topic: "/will", string: "dieout")
_ = mqttClient.connect()
} else {
UserManager.shared.isConnected = false
delegate?.onMqttError(message: "Mqtt initialization error")
status = .error
}
}
}
delegate
method was missingNote:- I've asked to chatGPT, and got suggestion to add this 2 methods
extension MqttManager: CocoaMQTTDelegate {
func mqtt(_ mqtt: CocoaMQTT, didReceive trust: SecTrust, completionHandler: @escaping (Bool) -> Void) {
// Implement your custom SSL validation logic here.
// For example, you might want to always trust the certificate for testing purposes:
completionHandler(true)
}
func mqtt(_ mqtt: CocoaMQTT, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust {
if let serverTrust = challenge.protectionSpace.serverTrust {
completionHandler(.useCredential, URLCredential(trust: serverTrust))
return
}
}
completionHandler(.performDefaultHandling, nil)
}
func mqttUrlSession(_ mqtt: CocoaMQTT, didReceiveTrust trust: SecTrust, didReceiveChallenge challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
print("\(#function), \n result:- \(challenge.debugDescription)")
}
info.plist
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>my-domain.cloudmqtt.com</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<true/>
<key>NSIncludesSubdomains</key>
<true/>
<key>NSTemporaryExceptionRequiresForwardSecrecy</key>
<false/>
</dict>
</dict>
</dict>