base on MQTT 5.0 client library for iOS and macOS written in Swift # CocoaMQTT ![PodVersion](https://img.shields.io/cocoapods/v/CocoaMQTT5.svg) ![Platforms](https://img.shields.io/cocoapods/p/CocoaMQTT5.svg) ![License](https://img.shields.io/cocoapods/l/BadgeSwift.svg?style=flat) ![Swift version](https://img.shields.io/badge/swift-5-orange.svg) MQTT v3.1.1 and v5.0 client library for iOS/macOS/tvOS written with Swift 5 ## Build Build with Xcode 11.1 / Swift 5.1 IOS Target: 12.0 or above OSX Target: 10.13 or above TVOS Target: 10.0 or above ## xcode 14.3 issue: ```ruby File not found: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/arc/libarclite_iphonesimulator.a ``` If you encounter the issue, Please update your project minimum depolyments to 11.0 ## Installation ### Swift Package Manager To integrate CocoaMQTT into your Xcode project using [Swift Package Manager](https://swift.org/package-manager/), follow these steps: 1. Open your project in Xcode. 2. Go to `File` > `Swift Packages` > `Add Package Dependency`. 3. Enter the repository URL: `https://github.com/emqx/CocoaMQTT.git`. 4. Choose the latest version or specify a version range. 5. Add the package to your target. At last, import "CocoaMQTT" to your project: ```swift import CocoaMQTT ``` ### CocoaPods To integrate CocoaMQTT into your Xcode project using [CocoaPods](http://cocoapods.org), you need to modify you `Podfile` like the followings: ```ruby use_frameworks! target 'Example' do pod 'CocoaMQTT' end ``` Then, run the following command: ```bash $ pod install ``` At last, import "CocoaMQTT" to your project: ```swift import CocoaMQTT ``` ## Usage Create a client to connect [MQTT broker](https://www.emqx.com/en/mqtt/public-mqtt5-broker): ```swift ///MQTT 5.0 let clientID = "CocoaMQTT-" + String(ProcessInfo().processIdentifier) let mqtt5 = CocoaMQTT5(clientID: clientID, host: "broker.emqx.io", port: 1883) let connectProperties = MqttConnectProperties() connectProperties.topicAliasMaximum = 0 connectProperties.sessionExpiryInterval = 0 connectProperties.receiveMaximum = 100 connectProperties.maximumPacketSize = 500 mqtt5.connectProperties = connectProperties mqtt5.username = "test" mqtt5.password = "public" mqtt5.willMessage = CocoaMQTTMessage(topic: "/will", string: "dieout") mqtt5.keepAlive = 60 mqtt5.delegate = self mqtt5.connect() ///MQTT 3.1.1 let clientID = "CocoaMQTT-" + String(ProcessInfo().processIdentifier) let mqtt = CocoaMQTT(clientID: clientID, host: "broker.emqx.io", port: 1883) mqtt.username = "test" mqtt.password = "public" mqtt.willMessage = CocoaMQTTMessage(topic: "/will", string: "dieout") mqtt.keepAlive = 60 mqtt.delegate = self mqtt.connect() ``` Now you can use closures instead of `CocoaMQTTDelegate`: ```swift mqtt.didReceiveMessage = { mqtt, message, id in print("Message received in topic \(message.topic) with payload \(message.string!)") } ``` ## SSL Secure #### One-way certification No certificate is required locally. If you want to trust all untrust CA certificates, you can do this: ```swift mqtt.allowUntrustCACertificate = true ``` #### Two-way certification Need a .p12 file which is generated by a public key file and a private key file. You can generate the p12 file in the terminal: ``` openssl pkcs12 -export -clcerts -in client-cert.pem -inkey client-key.pem -out client.p12 ``` Note: Please use openssl version 1.1 (e.g. `brew install [email protected]`), otherwise you may not be able to import the generated .p12 file to the system correctly. ## MQTT over Websocket In the 1.3.0, The CocoaMQTT has supported to connect to MQTT Broker by Websocket. If you integrated by **Swift Package Manager**, follow these steps: 1. Open your project in Xcode. 2. Go to `File` > `Swift Packages` > `Add Package Dependency`. 3. Enter the repository URL: `https://github.com/emqx/CocoaMQTT.git`. 4. Choose the latest version or specify a version range. 5. Add the package to your target. At last, import "CocoaMQTT" and "Starscream" to your project: ```swift import CocoaMQTT import CocoaMQTTWebSocket import Starscream ``` If you integrated by **CocoaPods**, you need to modify you `Podfile` like the followings and execute `pod install` again: ```ruby use_frameworks! target 'Example' do pod 'CocoaMQTT/WebSockets' end ``` If you're using CocoaMQTT in a project with only a `.podspec` and no `Podfile`, e.g. in a module for React Native, add this line to your `.podspec`: ```ruby Pod::Spec.new do |s| ... s.dependency "Starscream" end ``` Then, Create a MQTT instance over Websocket: ```swift ///MQTT 5.0 let websocket = CocoaMQTTWebSocket(uri: "/mqtt") let mqtt5 = CocoaMQTT5(clientID: clientID, host: host, port: 8083, socket: websocket) let connectProperties = MqttConnectProperties() connectProperties.topicAliasMaximum = 0 // ... mqtt5.connectProperties = connectProperties // ... _ = mqtt5.connect() ///MQTT 3.1.1 let websocket = CocoaMQTTWebSocket(uri: "/mqtt") let mqtt = CocoaMQTT(clientID: clientID, host: host, port: 8083, socket: websocket) // ... _ = mqtt.connect() ``` If you want to add additional custom header to the connection, you can use the following: ```swift let websocket = CocoaMQTTWebSocket(uri: "/mqtt") websocket.headers = [ "x-api-key": "value" ] websocket.enableSSL = true let mqtt = CocoaMQTT(clientID: clientID, host: host, port: 8083, socket: websocket) // ... _ = mqtt.connect() ``` If you want to connect using WebSocket Secure (wss), you can use the following example: ```swift import CocoaMQTT import CocoaMQTTWebSocket import Starscream class WebSocketManager { private var mqttClient: CocoaMQTT? var message: String = "" var token: String = "" func setupMQTTClient(with token: String) { let socket = CocoaMQTTWebSocket(uri: "/mqtt") socket.enableSSL = true mqttClient = CocoaMQTT(clientID: token, host: "host", port: 443, socket: socket) mqttClient?.delegate = self } func connect() { guard let mqttClient = mqttClient else { return } mqttClient.connect() } } extension WebSocketManager: 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)") } func mqtt(_ mqtt: CocoaMQTT, didPublishAck id: UInt16) { print("Published message with ID: \(id)") } func mqtt(_ mqtt: CocoaMQTT, didUnsubscribeTopics topics: [String]) { print("Unsubscribed from topics: \(topics)") } func mqttDidPing(_ mqtt: CocoaMQTT) { print("MQTT did ping") } func mqttDidReceivePong(_ mqtt: CocoaMQTT) { print("MQTT did receive pong") } func mqttDidDisconnect(_ mqtt: CocoaMQTT, withError err: (any Error)?) { print("Disconnected from MQTT broker with error: \(String(describing: err))") } func mqtt(_ mqtt: CocoaMQTT, didConnectAck ack: CocoaMQTTConnAck) { print("Connected to MQTT broker with acknowledgment: \(ack)") } func mqtt(_ mqtt: CocoaMQTT, didReceiveMessage message: CocoaMQTTMessage, id: UInt16) { if let messageString = message.string { DispatchQueue.main.async { self.message = messageString } print("Received message: \(messageString) on topic: \(message.topic)") } } func mqtt(_ mqtt: CocoaMQTT, didPublishMessage message: CocoaMQTTMessage, id: UInt16) { print("Published message: \(message.string ?? "") with ID: \(id)") } func mqtt(_ mqtt: CocoaMQTT, didSubscribeTopics success: NSDictionary, failed: [String]) { print("Subscribed to topics: \(success), failed to subscribe to: \(failed)") } func mqtt(_ mqtt: CocoaMQTT, didDisconnectWithError err: Error?) { print("Disconnected from MQTT broker with error: \(String(describing: err))") } } ``` ## Example App You can follow the Example App to learn how to use it. But we need to make the Example App works first: ```bash $ cd Examples ``` Then, open the `Example.xcodeproj` by Xcode and start it! ## Dependencies These third-party functions are used: ~~[GCDAsyncSocket](https://github.com/robbiehanson/CocoaAsyncSocket)~~ * [MqttCocoaAsyncSocket](https://github.com/leeway1208/MqttCocoaAsyncSocket) * [Starscream](https://github.com/daltoniam/Starscream) ## LICENSE MIT License (see `LICENSE`) ## Contributors * [@andypiper](https://github.com/andypiper) * [@turtleDeng](https://github.com/turtleDeng) * [@jan-bednar](https://github.com/jan-bednar) * [@jmiltner](https://github.com/jmiltner) * [@manucheri](https://github.com/manucheri) * [@Cyrus Ingraham](https://github.com/cyrusingraham) ## Author - Feng Lee <[email protected]> - CrazyWisdom <[email protected]> - Alex Yu <[email protected]> - Leeway <[email protected]> ## Twitter https://twitter.com/EMQTech ", Assign "at most 3 tags" to the expected json: {"id":"13424","tags":[]} "only from the tags list I provide: []" returns me the "expected json"