- MQTT(上报位置、订阅成员、收到消息更新成员列表对应信息)
1
Podfile
|
|
@ -50,6 +50,7 @@ target 'QuickLocation' do
|
|||
#第三方SDk
|
||||
pod 'GYSDK' #个推一键登录
|
||||
pod 'OpenIMSDK' #OpenIM
|
||||
pod 'CocoaMQTT' #MQTT
|
||||
|
||||
#支付宝
|
||||
pod 'AlipaySDK-iOS'
|
||||
|
|
|
|||
12
Podfile.lock
|
|
@ -11,6 +11,10 @@ PODS:
|
|||
- CocoaLumberjack/Core (3.9.1)
|
||||
- CocoaLumberjack/Swift (3.9.1):
|
||||
- CocoaLumberjack/Core
|
||||
- CocoaMQTT (2.2.5):
|
||||
- CocoaMQTT/Core (= 2.2.5)
|
||||
- CocoaMQTT/Core (2.2.5):
|
||||
- MqttCocoaAsyncSocket (~> 1.0.8)
|
||||
- Differentiator (5.0.0)
|
||||
- GTCommonSDK (3.2.3.0):
|
||||
- ZXSDK
|
||||
|
|
@ -89,6 +93,7 @@ PODS:
|
|||
- Moya/Core (= 15.0.0)
|
||||
- Moya/Core (15.0.0):
|
||||
- Alamofire (~> 5.0)
|
||||
- MqttCocoaAsyncSocket (1.0.8)
|
||||
- ObjectMapper (4.4.2)
|
||||
- "OpenIMSDK (3.8.3+hotfix.3.1)":
|
||||
- MJExtension
|
||||
|
|
@ -174,6 +179,7 @@ DEPENDENCIES:
|
|||
- AMapLocation
|
||||
- AMapSearch
|
||||
- CocoaLumberjack/Swift
|
||||
- CocoaMQTT
|
||||
- GYSDK
|
||||
- HXPHPicker
|
||||
- IQKeyboardManagerSwift
|
||||
|
|
@ -215,6 +221,7 @@ SPEC REPOS:
|
|||
- AMapLocation
|
||||
- AMapSearch
|
||||
- CocoaLumberjack
|
||||
- CocoaMQTT
|
||||
- Differentiator
|
||||
- GTCommonSDK
|
||||
- GYSDK
|
||||
|
|
@ -236,6 +243,7 @@ SPEC REPOS:
|
|||
- MJExtension
|
||||
- MJRefresh
|
||||
- Moya
|
||||
- MqttCocoaAsyncSocket
|
||||
- ObjectMapper
|
||||
- OpenIMSDK
|
||||
- OpenIMSDKCore
|
||||
|
|
@ -277,6 +285,7 @@ SPEC CHECKSUMS:
|
|||
AMapLocation: 6e44f50b044dc54c6b3dcb1dee5ffd6de2689e41
|
||||
AMapSearch: 29224a399b56b17da1540e4312fc4d9dc37342bb
|
||||
CocoaLumberjack: e4ba3b414dfca8c1916c6303d37f63b3a95134c6
|
||||
CocoaMQTT: 1e2fa493d57045d66fb1eba2bb5ffb8d77039c60
|
||||
Differentiator: e8497ceab83c1b10ca233716d547b9af21b9344d
|
||||
GTCommonSDK: 238c6735add91e654f8564638854686ee3dcb4ac
|
||||
GYSDK: db5f4b3aae8df06201be5e786076211ec11791be
|
||||
|
|
@ -299,6 +308,7 @@ SPEC CHECKSUMS:
|
|||
MJExtension: e97d164cb411aa9795cf576093a1fa208b4a8dd8
|
||||
MJRefresh: ff9e531227924c84ce459338414550a05d2aea78
|
||||
Moya: 138f0573e53411fb3dc17016add0b748dfbd78ee
|
||||
MqttCocoaAsyncSocket: 77d3b74f76228dd5a05d1f9526eab101d415b30c
|
||||
ObjectMapper: e6e4d91ff7f2861df7aecc536c92d8363f4c9677
|
||||
OpenIMSDK: c1880296ead605bcac2a289ad0aef9170fe6e6df
|
||||
OpenIMSDKCore: 75caba22be8ee484011f367711178312bd64a17d
|
||||
|
|
@ -323,6 +333,6 @@ SPEC CHECKSUMS:
|
|||
YYImage: 1e1b62a9997399593e4b9c4ecfbbabbf1d3f3b54
|
||||
ZXSDK: 786338c0a18e98e03eda00699c3bfd2700b97117
|
||||
|
||||
PODFILE CHECKSUM: 6ba8ada83ccc565c9bdcc418e9a936818640882c
|
||||
PODFILE CHECKSUM: 861114200722c29adba98535aef6b0494d123b44
|
||||
|
||||
COCOAPODS: 1.16.2
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
This project is dual licensed under the Eclipse Public License 1.0 and the
|
||||
Eclipse Distribution License 1.0 as described in the epl-v10 and edl-v10 files.
|
||||
|
|
@ -0,0 +1,350 @@
|
|||
# CocoaMQTT
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
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 openssl@1.1`), 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 <feng@emqx.io>
|
||||
- CrazyWisdom <zh.whong@gmail.com>
|
||||
- Alex Yu <alexyu.dc@gmail.com>
|
||||
- Leeway <leeway1208@gmail.com>
|
||||
|
||||
|
||||
## Twitter
|
||||
|
||||
https://twitter.com/EMQTech
|
||||
|
|
@ -0,0 +1,842 @@
|
|||
//
|
||||
// CocoaMQTT.swift
|
||||
// CocoaMQTT
|
||||
//
|
||||
// Created by Feng Lee<feng@eqmtt.io> on 14/8/3.
|
||||
// Copyright (c) 2015 emqx.io. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import MqttCocoaAsyncSocket
|
||||
|
||||
/**
|
||||
* Conn Ack
|
||||
*/
|
||||
@objc public enum CocoaMQTTConnAck: UInt8, CustomStringConvertible {
|
||||
case accept = 0
|
||||
case unacceptableProtocolVersion
|
||||
case identifierRejected
|
||||
case serverUnavailable
|
||||
case badUsernameOrPassword
|
||||
case notAuthorized
|
||||
case reserved
|
||||
|
||||
public init(byte: UInt8) {
|
||||
switch byte {
|
||||
case CocoaMQTTConnAck.accept.rawValue..<CocoaMQTTConnAck.reserved.rawValue:
|
||||
self.init(rawValue: byte)!
|
||||
default:
|
||||
self = .reserved
|
||||
}
|
||||
}
|
||||
|
||||
public var description: String {
|
||||
switch self {
|
||||
case .accept: return "accept"
|
||||
case .unacceptableProtocolVersion: return "unacceptableProtocolVersion"
|
||||
case .identifierRejected: return "identifierRejected"
|
||||
case .serverUnavailable: return "serverUnavailable"
|
||||
case .badUsernameOrPassword: return "badUsernameOrPassword"
|
||||
case .notAuthorized: return "notAuthorized"
|
||||
case .reserved: return "reserved"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// CocoaMQTT Delegate
|
||||
@objc public protocol CocoaMQTTDelegate {
|
||||
|
||||
///
|
||||
func mqtt(_ mqtt: CocoaMQTT, didConnectAck ack: CocoaMQTTConnAck)
|
||||
|
||||
///
|
||||
func mqtt(_ mqtt: CocoaMQTT, didPublishMessage message: CocoaMQTTMessage, id: UInt16)
|
||||
|
||||
///
|
||||
func mqtt(_ mqtt: CocoaMQTT, didPublishAck id: UInt16)
|
||||
|
||||
///
|
||||
func mqtt(_ mqtt: CocoaMQTT, didReceiveMessage message: CocoaMQTTMessage, id: UInt16 )
|
||||
|
||||
///
|
||||
func mqtt(_ mqtt: CocoaMQTT, didSubscribeTopics success: NSDictionary, failed: [String])
|
||||
|
||||
///
|
||||
func mqtt(_ mqtt: CocoaMQTT, didUnsubscribeTopics topics: [String])
|
||||
|
||||
///
|
||||
func mqttDidPing(_ mqtt: CocoaMQTT)
|
||||
|
||||
///
|
||||
func mqttDidReceivePong(_ mqtt: CocoaMQTT)
|
||||
|
||||
///
|
||||
func mqttDidDisconnect(_ mqtt: CocoaMQTT, withError err: Error?)
|
||||
|
||||
/// Manually validate SSL/TLS server certificate.
|
||||
///
|
||||
/// This method will be called if enable `allowUntrustCACertificate`
|
||||
@objc optional func mqtt(_ mqtt: CocoaMQTT, didReceive trust: SecTrust, completionHandler: @escaping (Bool) -> Void)
|
||||
|
||||
@objc optional func mqttUrlSession(_ mqtt: CocoaMQTT, didReceiveTrust trust: SecTrust, didReceiveChallenge challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
|
||||
|
||||
///
|
||||
@objc optional func mqtt(_ mqtt: CocoaMQTT, didPublishComplete id: UInt16)
|
||||
|
||||
///
|
||||
@objc optional func mqtt(_ mqtt: CocoaMQTT, didStateChangeTo state: CocoaMQTTConnState)
|
||||
|
||||
/// Called when auto-reconnect schedules a reconnect attempt after an unexpected disconnect.
|
||||
@objc optional func mqtt(_ mqtt: CocoaMQTT, didScheduleReconnect attemptCount: UInt, after interval: UInt16)
|
||||
}
|
||||
|
||||
/// set mqtt version to 3.1.1
|
||||
public func setMqtt3Version() {
|
||||
if let storage = CocoaMQTTStorage() {
|
||||
storage.setMQTTVersion("3.1.1")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Blueprint of the MQTT Client
|
||||
*/
|
||||
protocol CocoaMQTTClient {
|
||||
|
||||
/* Basic Properties */
|
||||
|
||||
var host: String { get set }
|
||||
var port: UInt16 { get set }
|
||||
var clientID: String { get }
|
||||
var username: String? {get set}
|
||||
var password: String? {get set}
|
||||
var cleanSession: Bool {get set}
|
||||
var keepAlive: UInt16 {get set}
|
||||
var willMessage: CocoaMQTTMessage? {get set}
|
||||
|
||||
/* Basic Properties */
|
||||
|
||||
/* CONNNEC/DISCONNECT */
|
||||
|
||||
func connect() -> Bool
|
||||
func connect(timeout: TimeInterval) -> Bool
|
||||
func disconnect()
|
||||
func ping()
|
||||
|
||||
/* CONNNEC/DISCONNECT */
|
||||
|
||||
/* PUBLISH/SUBSCRIBE */
|
||||
|
||||
func subscribe(_ topic: String, qos: CocoaMQTTQoS)
|
||||
func subscribe(_ topics: [(String, CocoaMQTTQoS)])
|
||||
|
||||
func unsubscribe(_ topic: String)
|
||||
func unsubscribe(_ topics: [String])
|
||||
|
||||
func publish(_ topic: String, withString string: String, qos: CocoaMQTTQoS, retained: Bool) -> Int
|
||||
func publish(_ message: CocoaMQTTMessage) -> Int
|
||||
|
||||
/* PUBLISH/SUBSCRIBE */
|
||||
}
|
||||
|
||||
/// MQTT Client
|
||||
///
|
||||
/// - Note: MGCDAsyncSocket need delegate to extend NSObject
|
||||
public class CocoaMQTT: NSObject, CocoaMQTTClient {
|
||||
|
||||
public weak var delegate: CocoaMQTTDelegate?
|
||||
|
||||
private var version = "3.1.1"
|
||||
|
||||
public var host = "localhost"
|
||||
|
||||
public var port: UInt16 = 1883
|
||||
|
||||
public var clientID: String
|
||||
|
||||
public var username: String?
|
||||
|
||||
public var password: String?
|
||||
|
||||
/// Clean Session flag. Default is true
|
||||
///
|
||||
/// - TODO: What's behavior each Clean Session flags???
|
||||
public var cleanSession = true
|
||||
|
||||
/// Setup a **Last Will Message** to client before connecting to broker
|
||||
public var willMessage: CocoaMQTTMessage?
|
||||
|
||||
/// Enable backgounding socket if running on iOS platform. Default is true
|
||||
///
|
||||
/// - Note:
|
||||
public var backgroundOnSocket: Bool {
|
||||
get { return (self.socket as? CocoaMQTTSocket)?.backgroundOnSocket ?? true }
|
||||
set { (self.socket as? CocoaMQTTSocket)?.backgroundOnSocket = newValue }
|
||||
}
|
||||
|
||||
/// Delegate Executed queue. Default is `DispatchQueue.main`
|
||||
///
|
||||
/// The delegate/closure callback function will be committed asynchronously to it
|
||||
public var delegateQueue = DispatchQueue.main
|
||||
|
||||
public var connState = CocoaMQTTConnState.disconnected {
|
||||
didSet {
|
||||
__delegate_queue {
|
||||
self.delegate?.mqtt?(self, didStateChangeTo: self.connState)
|
||||
self.didChangeState(self, self.connState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// deliver
|
||||
private var deliver = CocoaMQTTDeliver()
|
||||
|
||||
/// Re-deliver the un-acked messages
|
||||
public var deliverTimeout: Double {
|
||||
get { return deliver.retryTimeInterval }
|
||||
set { deliver.retryTimeInterval = newValue }
|
||||
}
|
||||
|
||||
/// Message queue size. default 1000
|
||||
///
|
||||
/// The new publishing messages of Qos1/Qos2 will be drop, if the queue is full
|
||||
public var messageQueueSize: UInt {
|
||||
get { return deliver.mqueueSize }
|
||||
set { deliver.mqueueSize = newValue }
|
||||
}
|
||||
|
||||
/// In-flight window size. default 10
|
||||
public var inflightWindowSize: UInt {
|
||||
get { return deliver.inflightWindowSize }
|
||||
set { deliver.inflightWindowSize = newValue }
|
||||
}
|
||||
|
||||
/// Keep alive time interval
|
||||
public var keepAlive: UInt16 = 60
|
||||
private var aliveTimer: CocoaMQTTTimer?
|
||||
|
||||
/// Enable auto-reconnect mechanism
|
||||
public var autoReconnect = false
|
||||
|
||||
/// Reconnect time interval
|
||||
///
|
||||
/// - note: This value will be increased with `autoReconnectTimeInterval *= 2`
|
||||
/// if reconnect failed
|
||||
public var autoReconnectTimeInterval: UInt16 = 1 // starts from 1 second
|
||||
|
||||
/// Maximum auto reconnect time interval
|
||||
///
|
||||
/// The timer starts from `autoReconnectTimeInterval` second and grows exponentially until this value
|
||||
/// After that, it uses this value for subsequent requests.
|
||||
public var maxAutoReconnectTimeInterval: UInt16 = 128 // 128 seconds
|
||||
|
||||
/// Auto-reconnect backoff interval in seconds for the current reconnect cycle.
|
||||
///
|
||||
/// This value is advanced for the next reconnect attempt while auto-reconnect is active,
|
||||
/// and resets to `0` when auto-reconnect is inactive.
|
||||
public private(set) var reconnectTimeInterval: UInt16 = 0
|
||||
|
||||
/// Number of reconnect attempts scheduled in the current auto-reconnect cycle.
|
||||
///
|
||||
/// The value resets to `0` after a successful connection or expected disconnect.
|
||||
public private(set) var reconnectAttemptCount: UInt = 0
|
||||
|
||||
private var autoReconnTimer: CocoaMQTTTimer?
|
||||
private var is_internal_disconnected = false
|
||||
|
||||
/// Console log level
|
||||
public var logLevel: CocoaMQTTLoggerLevel {
|
||||
get {
|
||||
return CocoaMQTTLogger.logger.minLevel
|
||||
}
|
||||
set {
|
||||
CocoaMQTTLogger.logger.minLevel = newValue
|
||||
}
|
||||
}
|
||||
|
||||
/// Enable SSL connection
|
||||
public var enableSSL: Bool {
|
||||
get { return self.socket.enableSSL }
|
||||
set { socket.enableSSL = newValue }
|
||||
}
|
||||
|
||||
///
|
||||
public var sslSettings: [String: NSObject]? {
|
||||
get { return (self.socket as? CocoaMQTTSocket)?.sslSettings ?? nil }
|
||||
set { (self.socket as? CocoaMQTTSocket)?.sslSettings = newValue }
|
||||
}
|
||||
|
||||
/// Allow self-signed ca certificate.
|
||||
///
|
||||
/// Default is false
|
||||
public var allowUntrustCACertificate: Bool {
|
||||
get { return (self.socket as? CocoaMQTTSocket)?.allowUntrustCACertificate ?? false }
|
||||
set { (self.socket as? CocoaMQTTSocket)?.allowUntrustCACertificate = newValue }
|
||||
}
|
||||
|
||||
/// The subscribed topics in current communication
|
||||
///
|
||||
/// Keeping this dictionary-typed preserves the public API while the backing store remains thread-safe.
|
||||
public var subscriptions: [String: CocoaMQTTQoS] {
|
||||
get { subscriptionsStorage.snapshot() }
|
||||
set { subscriptionsStorage.replace(with: newValue) }
|
||||
}
|
||||
private var subscriptionsStorage = ThreadSafeDictionary<String, CocoaMQTTQoS>(label: "subscriptions")
|
||||
|
||||
fileprivate var subscriptionsWaitingAck = ThreadSafeDictionary<UInt16, [(String, CocoaMQTTQoS)]>(label: "subscriptionsWaitingAck")
|
||||
fileprivate var unsubscriptionsWaitingAck = ThreadSafeDictionary<UInt16, [String]>(label: "unsubscriptionsWaitingAck")
|
||||
|
||||
/// Sending messages
|
||||
fileprivate var sendingMessages: [UInt16: CocoaMQTTMessage] = [:]
|
||||
|
||||
/// message id counter
|
||||
private var _msgid: UInt16 = 0
|
||||
fileprivate var socket: CocoaMQTTSocketProtocol
|
||||
fileprivate var reader: CocoaMQTTReader?
|
||||
|
||||
// Closures
|
||||
public var didConnectAck: (CocoaMQTT, CocoaMQTTConnAck) -> Void = { _, _ in }
|
||||
public var didPublishMessage: (CocoaMQTT, CocoaMQTTMessage, UInt16) -> Void = { _, _, _ in }
|
||||
public var didPublishAck: (CocoaMQTT, UInt16) -> Void = { _, _ in }
|
||||
public var didReceiveMessage: (CocoaMQTT, CocoaMQTTMessage, UInt16) -> Void = { _, _, _ in }
|
||||
public var didSubscribeTopics: (CocoaMQTT, NSDictionary, [String]) -> Void = { _, _, _ in }
|
||||
public var didUnsubscribeTopics: (CocoaMQTT, [String]) -> Void = { _, _ in }
|
||||
public var didPing: (CocoaMQTT) -> Void = { _ in }
|
||||
public var didReceivePong: (CocoaMQTT) -> Void = { _ in }
|
||||
public var didDisconnect: (CocoaMQTT, Error?) -> Void = { _, _ in }
|
||||
public var didReceiveTrust: (CocoaMQTT, SecTrust, @escaping (Bool) -> Swift.Void) -> Void = { _, _, _ in }
|
||||
public var didCompletePublish: (CocoaMQTT, UInt16) -> Void = { _, _ in }
|
||||
public var didChangeState: (CocoaMQTT, CocoaMQTTConnState) -> Void = { _, _ in }
|
||||
public var didScheduleReconnect: (CocoaMQTT, UInt, UInt16) -> Void = { _, _, _ in }
|
||||
|
||||
/// Initial client object
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - clientID: Client Identifier
|
||||
/// - host: The MQTT broker host domain or IP address. Default is "localhost"
|
||||
/// - port: The MQTT service port of host. Default is 1883
|
||||
public init(clientID: String, host: String = "localhost", port: UInt16 = 1883, socket: CocoaMQTTSocketProtocol = CocoaMQTTSocket()) {
|
||||
self.clientID = clientID
|
||||
self.host = host
|
||||
self.port = port
|
||||
self.socket = socket
|
||||
super.init()
|
||||
deliver.delegate = self
|
||||
if let storage = CocoaMQTTStorage() {
|
||||
storage.setMQTTVersion("3.1.1")
|
||||
} else {
|
||||
printWarning("Localstorage initial failed for key: \(clientID)")
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
aliveTimer?.suspend()
|
||||
autoReconnTimer?.suspend()
|
||||
|
||||
socket.setDelegate(nil, delegateQueue: nil)
|
||||
socket.disconnect()
|
||||
}
|
||||
|
||||
fileprivate func send(_ frame: Frame, tag: Int = 0) {
|
||||
printDebug("SEND: \(frame)")
|
||||
let data = frame.bytes(version: version)
|
||||
socket.write(Data(bytes: data, count: data.count), withTimeout: 5, tag: tag)
|
||||
}
|
||||
|
||||
fileprivate func sendConnectFrame() {
|
||||
|
||||
var connect = FrameConnect(clientID: clientID)
|
||||
connect.keepAlive = keepAlive
|
||||
connect.username = username
|
||||
connect.password = password
|
||||
connect.willMsg = willMessage
|
||||
connect.cleansess = cleanSession
|
||||
|
||||
send(connect)
|
||||
reader!.start()
|
||||
}
|
||||
|
||||
fileprivate func nextMessageID() -> UInt16 {
|
||||
if _msgid == UInt16.max {
|
||||
_msgid = 0
|
||||
}
|
||||
_msgid += 1
|
||||
return _msgid
|
||||
}
|
||||
|
||||
fileprivate func puback(_ type: FrameType, msgid: UInt16) {
|
||||
switch type {
|
||||
case .puback:
|
||||
send(FramePubAck(msgid: msgid))
|
||||
case .pubrec:
|
||||
send(FramePubRec(msgid: msgid))
|
||||
case .pubcomp:
|
||||
send(FramePubComp(msgid: msgid))
|
||||
default: return
|
||||
}
|
||||
}
|
||||
|
||||
/// Connect to MQTT broker
|
||||
///
|
||||
/// - Returns:
|
||||
/// - Bool: It indicates whether successfully calling socket connect function.
|
||||
/// Not yet established correct MQTT session
|
||||
public func connect() -> Bool {
|
||||
return connect(timeout: -1)
|
||||
}
|
||||
|
||||
/// Connect to MQTT broker
|
||||
/// - Parameters:
|
||||
/// - timeout: Connect timeout
|
||||
/// - Returns:
|
||||
/// - Bool: It indicates whether successfully calling socket connect function.
|
||||
/// Not yet established correct MQTT session
|
||||
public func connect(timeout: TimeInterval) -> Bool {
|
||||
socket.setDelegate(self, delegateQueue: delegateQueue)
|
||||
reader = CocoaMQTTReader(socket: socket, delegate: self)
|
||||
do {
|
||||
if timeout > 0 {
|
||||
try socket.connect(toHost: self.host, onPort: self.port, withTimeout: timeout)
|
||||
} else {
|
||||
try socket.connect(toHost: self.host, onPort: self.port)
|
||||
}
|
||||
|
||||
delegateQueue.async { [weak self] in
|
||||
guard let self = self else { return }
|
||||
self.connState = .connecting
|
||||
}
|
||||
|
||||
return true
|
||||
} catch let error as NSError {
|
||||
printError("socket connect error: \(error.description)")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/// Send a DISCONNECT packet to the broker then close the connection
|
||||
///
|
||||
/// - Note: Only can be called from outside.
|
||||
/// This closes the connection expectedly, so auto-reconnect will not run.
|
||||
public func disconnect() {
|
||||
expected_disconnect()
|
||||
}
|
||||
|
||||
/// Disconnect unexpectedly.
|
||||
/// This keeps auto-reconnect behavior enabled.
|
||||
func internal_disconnect() {
|
||||
is_internal_disconnected = false
|
||||
socket.disconnect()
|
||||
}
|
||||
|
||||
private func expected_disconnect() {
|
||||
is_internal_disconnected = true
|
||||
send(FrameDisconnect(), tag: -0xE0)
|
||||
socket.disconnect()
|
||||
}
|
||||
|
||||
/// Send a PING request to broker
|
||||
public func ping() {
|
||||
printDebug("ping")
|
||||
send(FramePingReq(), tag: -0xC0)
|
||||
|
||||
__delegate_queue {
|
||||
self.delegate?.mqttDidPing(self)
|
||||
self.didPing(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Publish a message to broker
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - topic: Topic Name. It can not contain '#', '+' wildcards
|
||||
/// - string: Payload string
|
||||
/// - qos: Qos. Default is Qos1
|
||||
/// - retained: Retained flag. Mark this message is a retained message. default is false
|
||||
/// - Returns:
|
||||
/// - 0 will be returned, if the message's qos is qos0
|
||||
/// - 1-65535 will be returned, if the messages's qos is qos1/qos2
|
||||
/// - -1 will be returned, if the messages queue is full
|
||||
@discardableResult
|
||||
public func publish(_ topic: String, withString string: String, qos: CocoaMQTTQoS = .qos1, retained: Bool = false) -> Int {
|
||||
let message = CocoaMQTTMessage(topic: topic, string: string, qos: qos, retained: retained)
|
||||
return publish(message)
|
||||
}
|
||||
|
||||
/// Publish a message to broker
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - message: Message
|
||||
@discardableResult
|
||||
public func publish(_ message: CocoaMQTTMessage) -> Int {
|
||||
let msgid: UInt16
|
||||
|
||||
if message.qos == .qos0 {
|
||||
msgid = 0
|
||||
} else {
|
||||
msgid = nextMessageID()
|
||||
}
|
||||
|
||||
var frame = FramePublish(topic: message.topic,
|
||||
payload: message.payload,
|
||||
qos: message.qos,
|
||||
msgid: msgid)
|
||||
|
||||
frame.retained = message.retained
|
||||
|
||||
delegateQueue.async {
|
||||
self.sendingMessages[msgid] = message
|
||||
}
|
||||
|
||||
// Push frame to deliver message queue
|
||||
guard deliver.add(frame) else {
|
||||
delegateQueue.async {
|
||||
self.sendingMessages.removeValue(forKey: msgid)
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
return Int(msgid)
|
||||
}
|
||||
|
||||
/// Subscribe a `<Topic Name>/<Topic Filter>`
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - topic: Topic Name or Topic Filter
|
||||
/// - qos: Qos. Default is qos1
|
||||
public func subscribe(_ topic: String, qos: CocoaMQTTQoS = .qos1) {
|
||||
return subscribe([(topic, qos)])
|
||||
}
|
||||
|
||||
/// Subscribe a lists of topics
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - topics: A list of tuples presented by `(<Topic Names>/<Topic Filters>, Qos)`
|
||||
public func subscribe(_ topics: [(String, CocoaMQTTQoS)]) {
|
||||
let msgid = nextMessageID()
|
||||
let frame = FrameSubscribe(msgid: msgid, topics: topics)
|
||||
send(frame, tag: Int(msgid))
|
||||
subscriptionsWaitingAck[msgid] = topics
|
||||
}
|
||||
|
||||
/// Unsubscribe a Topic
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - topic: A Topic Name or Topic Filter
|
||||
public func unsubscribe(_ topic: String) {
|
||||
return unsubscribe([topic])
|
||||
}
|
||||
|
||||
/// Unsubscribe a list of topics
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - topics: A list of `<Topic Names>/<Topic Filters>`
|
||||
public func unsubscribe(_ topics: [String]) {
|
||||
let msgid = nextMessageID()
|
||||
let frame = FrameUnsubscribe(msgid: msgid, topics: topics)
|
||||
unsubscriptionsWaitingAck[msgid] = topics
|
||||
send(frame, tag: Int(msgid))
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: CocoaMQTTDeliverProtocol
|
||||
extension CocoaMQTT: CocoaMQTTDeliverProtocol {
|
||||
|
||||
func deliver(_ deliver: CocoaMQTTDeliver, wantToSend frame: Frame) {
|
||||
if let publish = frame as? FramePublish {
|
||||
let msgid = publish.msgid
|
||||
|
||||
var message: CocoaMQTTMessage?
|
||||
|
||||
if let sendingMessage = sendingMessages[msgid] {
|
||||
message = sendingMessage
|
||||
// printError("Want send \(frame), but not found in CocoaMQTT cache")
|
||||
|
||||
} else {
|
||||
message = CocoaMQTTMessage(topic: publish.topic, payload: publish.payload())
|
||||
}
|
||||
|
||||
send(publish, tag: Int(msgid))
|
||||
|
||||
if let message = message {
|
||||
self.delegate?.mqtt(self, didPublishMessage: message, id: msgid)
|
||||
self.didPublishMessage(self, message, msgid)
|
||||
}
|
||||
} else if let pubrel = frame as? FramePubRel {
|
||||
// -- Send PUBREL
|
||||
send(pubrel, tag: Int(pubrel.msgid))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension CocoaMQTT {
|
||||
|
||||
func __delegate_queue(_ fun: @escaping () -> Void) {
|
||||
delegateQueue.async { [weak self] in
|
||||
guard self != nil else { return }
|
||||
fun()
|
||||
}
|
||||
}
|
||||
|
||||
private func prepareAutoReconnectAttempt() {
|
||||
if reconnectTimeInterval == 0 {
|
||||
reconnectTimeInterval = min(autoReconnectTimeInterval, maxAutoReconnectTimeInterval)
|
||||
}
|
||||
reconnectAttemptCount += 1
|
||||
}
|
||||
|
||||
private func updateAutoReconnectIntervalForNextAttempt() {
|
||||
let doubledInterval = UInt32(reconnectTimeInterval) * 2
|
||||
reconnectTimeInterval = UInt16(min(doubledInterval, UInt32(maxAutoReconnectTimeInterval)))
|
||||
}
|
||||
|
||||
private func resetAutoReconnectState() {
|
||||
reconnectTimeInterval = 0
|
||||
reconnectAttemptCount = 0
|
||||
autoReconnTimer = nil
|
||||
}
|
||||
|
||||
private func notifyAutoReconnectScheduled() {
|
||||
delegate?.mqtt?(self, didScheduleReconnect: reconnectAttemptCount, after: reconnectTimeInterval)
|
||||
didScheduleReconnect(self, reconnectAttemptCount, reconnectTimeInterval)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - CocoaMQTTSocketDelegate
|
||||
extension CocoaMQTT: CocoaMQTTSocketDelegate {
|
||||
|
||||
public func socketConnected(_ socket: CocoaMQTTSocketProtocol) {
|
||||
sendConnectFrame()
|
||||
}
|
||||
|
||||
public func socket(_ socket: CocoaMQTTSocketProtocol,
|
||||
didReceive trust: SecTrust,
|
||||
completionHandler: @escaping (Bool) -> Swift.Void) {
|
||||
|
||||
printDebug("Call the SSL/TLS manually validating function")
|
||||
|
||||
delegate?.mqtt?(self, didReceive: trust, completionHandler: completionHandler)
|
||||
didReceiveTrust(self, trust, completionHandler)
|
||||
}
|
||||
|
||||
public func socketUrlSession(_ socket: CocoaMQTTSocketProtocol, didReceiveTrust trust: SecTrust, didReceiveChallenge challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
|
||||
printDebug("Call the SSL/TLS manually validating function - socketUrlSession")
|
||||
|
||||
delegate?.mqttUrlSession?(self, didReceiveTrust: trust, didReceiveChallenge: challenge, completionHandler: completionHandler)
|
||||
}
|
||||
|
||||
// ?
|
||||
public func socketDidSecure(_ sock: MGCDAsyncSocket) {
|
||||
printDebug("Socket has successfully completed SSL/TLS negotiation")
|
||||
sendConnectFrame()
|
||||
}
|
||||
|
||||
public func socket(_ socket: CocoaMQTTSocketProtocol, didWriteDataWithTag tag: Int) {
|
||||
// XXX: How to print writed bytes??
|
||||
}
|
||||
|
||||
public func socket(_ socket: CocoaMQTTSocketProtocol, didRead data: Data, withTag tag: Int) {
|
||||
let etag = CocoaMQTTReadTag(rawValue: tag)!
|
||||
var bytes = [UInt8]([0])
|
||||
switch etag {
|
||||
case CocoaMQTTReadTag.header:
|
||||
data.copyBytes(to: &bytes, count: 1)
|
||||
reader!.headerReady(bytes[0])
|
||||
case CocoaMQTTReadTag.length:
|
||||
data.copyBytes(to: &bytes, count: 1)
|
||||
reader!.lengthReady(bytes[0])
|
||||
case CocoaMQTTReadTag.payload:
|
||||
reader!.payloadReady(data)
|
||||
}
|
||||
}
|
||||
|
||||
public func socketDidDisconnect(_ socket: CocoaMQTTSocketProtocol, withError err: Error?) {
|
||||
// Clean up
|
||||
socket.setDelegate(nil, delegateQueue: nil)
|
||||
if is_internal_disconnected || !autoReconnect {
|
||||
resetAutoReconnectState()
|
||||
}
|
||||
|
||||
connState = .disconnected
|
||||
delegate?.mqttDidDisconnect(self, withError: err)
|
||||
didDisconnect(self, err)
|
||||
|
||||
guard !is_internal_disconnected else {
|
||||
is_internal_disconnected = false
|
||||
return
|
||||
}
|
||||
|
||||
guard autoReconnect else {
|
||||
resetAutoReconnectState()
|
||||
return
|
||||
}
|
||||
|
||||
prepareAutoReconnectAttempt()
|
||||
|
||||
// Start reconnector once socket error occurred
|
||||
printInfo("Try reconnect to server after \(reconnectTimeInterval)s")
|
||||
notifyAutoReconnectScheduled()
|
||||
autoReconnTimer = CocoaMQTTTimer.after(Double(reconnectTimeInterval), name: "autoReconnTimer", { [weak self] in
|
||||
guard let self = self else { return }
|
||||
self.updateAutoReconnectIntervalForNextAttempt()
|
||||
_ = self.connect()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - CocoaMQTTReaderDelegate
|
||||
extension CocoaMQTT: CocoaMQTTReaderDelegate {
|
||||
|
||||
func didReceive(_ reader: CocoaMQTTReader, connack: FrameConnAck) {
|
||||
printDebug("RECV: \(connack)")
|
||||
|
||||
if connack.returnCode == .accept {
|
||||
|
||||
// Disable auto-reconnect
|
||||
|
||||
resetAutoReconnectState()
|
||||
is_internal_disconnected = false
|
||||
|
||||
// Start keepalive timer
|
||||
|
||||
let interval = Double(keepAlive <= 0 ? 60: keepAlive)
|
||||
|
||||
aliveTimer = CocoaMQTTTimer.every(interval, name: "aliveTimer") { [weak self] in
|
||||
guard let self = self else { return }
|
||||
self.delegateQueue.async {
|
||||
guard self.connState == .connected else {
|
||||
self.aliveTimer = nil
|
||||
return
|
||||
}
|
||||
self.ping()
|
||||
}
|
||||
}
|
||||
|
||||
// recover session if enable
|
||||
|
||||
if cleanSession {
|
||||
deliver.cleanAll()
|
||||
} else {
|
||||
if let storage = CocoaMQTTStorage(by: clientID) {
|
||||
deliver.recoverSessionBy(storage)
|
||||
} else {
|
||||
printWarning("Localstorage initial failed for key: \(clientID)")
|
||||
}
|
||||
}
|
||||
|
||||
connState = .connected
|
||||
|
||||
} else {
|
||||
connState = .disconnected
|
||||
expected_disconnect()
|
||||
}
|
||||
|
||||
delegate?.mqtt(self, didConnectAck: connack.returnCode ?? CocoaMQTTConnAck.serverUnavailable)
|
||||
didConnectAck(self, connack.returnCode ?? CocoaMQTTConnAck.serverUnavailable)
|
||||
}
|
||||
|
||||
func didReceive(_ reader: CocoaMQTTReader, publish: FramePublish) {
|
||||
printDebug("RECV: \(publish)")
|
||||
|
||||
let message = CocoaMQTTMessage(topic: publish.topic, payload: publish.payload(), qos: publish.qos, retained: publish.retained)
|
||||
|
||||
message.duplicated = publish.dup
|
||||
|
||||
printInfo("Received message: \(message)")
|
||||
delegate?.mqtt(self, didReceiveMessage: message, id: publish.msgid)
|
||||
didReceiveMessage(self, message, publish.msgid)
|
||||
|
||||
if message.qos == .qos1 {
|
||||
puback(FrameType.puback, msgid: publish.msgid)
|
||||
} else if message.qos == .qos2 {
|
||||
puback(FrameType.pubrec, msgid: publish.msgid)
|
||||
}
|
||||
}
|
||||
|
||||
func didReceive(_ reader: CocoaMQTTReader, puback: FramePubAck) {
|
||||
printDebug("RECV: \(puback)")
|
||||
|
||||
deliver.ack(by: puback)
|
||||
|
||||
delegate?.mqtt(self, didPublishAck: puback.msgid)
|
||||
didPublishAck(self, puback.msgid)
|
||||
}
|
||||
|
||||
func didReceive(_ reader: CocoaMQTTReader, pubrec: FramePubRec) {
|
||||
printDebug("RECV: \(pubrec)")
|
||||
|
||||
deliver.ack(by: pubrec)
|
||||
}
|
||||
|
||||
func didReceive(_ reader: CocoaMQTTReader, pubrel: FramePubRel) {
|
||||
printDebug("RECV: \(pubrel)")
|
||||
|
||||
puback(FrameType.pubcomp, msgid: pubrel.msgid)
|
||||
}
|
||||
|
||||
func didReceive(_ reader: CocoaMQTTReader, pubcomp: FramePubComp) {
|
||||
printDebug("RECV: \(pubcomp)")
|
||||
|
||||
deliver.ack(by: pubcomp)
|
||||
|
||||
delegate?.mqtt?(self, didPublishComplete: pubcomp.msgid)
|
||||
didCompletePublish(self, pubcomp.msgid)
|
||||
}
|
||||
|
||||
func didReceive(_ reader: CocoaMQTTReader, suback: FrameSubAck) {
|
||||
printDebug("RECV: \(suback)")
|
||||
guard let topicsAndQos = subscriptionsWaitingAck.removeValue(forKey: suback.msgid) else {
|
||||
printWarning("UNEXPECT SUBACK Received: \(suback)")
|
||||
return
|
||||
}
|
||||
|
||||
guard topicsAndQos.count == suback.grantedQos.count else {
|
||||
printWarning("UNEXPECT SUBACK Recivied: \(suback)")
|
||||
return
|
||||
}
|
||||
|
||||
let success: NSMutableDictionary = NSMutableDictionary()
|
||||
var failed = [String]()
|
||||
for (idx, (topic, _)) in topicsAndQos.enumerated() {
|
||||
if suback.grantedQos[idx] != .FAILURE {
|
||||
subscriptionsStorage[topic] = suback.grantedQos[idx]
|
||||
success[topic] = suback.grantedQos[idx].rawValue
|
||||
} else {
|
||||
failed.append(topic)
|
||||
}
|
||||
}
|
||||
|
||||
delegate?.mqtt(self, didSubscribeTopics: success, failed: failed)
|
||||
didSubscribeTopics(self, success, failed)
|
||||
}
|
||||
|
||||
func didReceive(_ reader: CocoaMQTTReader, unsuback: FrameUnsubAck) {
|
||||
printDebug("RECV: \(unsuback)")
|
||||
|
||||
guard let topics = unsubscriptionsWaitingAck.removeValue(forKey: unsuback.msgid) else {
|
||||
printWarning("UNEXPECT UNSUBACK Received: \(unsuback.msgid)")
|
||||
return
|
||||
}
|
||||
// Remove local subscription
|
||||
for t in topics {
|
||||
subscriptionsStorage.removeValue(forKey: t)
|
||||
}
|
||||
delegate?.mqtt(self, didUnsubscribeTopics: topics)
|
||||
didUnsubscribeTopics(self, topics)
|
||||
}
|
||||
|
||||
func didReceive(_ reader: CocoaMQTTReader, pingresp: FramePingResp) {
|
||||
printDebug("RECV: \(pingresp)")
|
||||
|
||||
delegate?.mqttDidReceivePong(self)
|
||||
didReceivePong(self)
|
||||
}
|
||||
|
||||
func didReceive(_ reader: CocoaMQTTReader, disconnect: FrameDisconnect) {
|
||||
printWarning("Received DISCONNECT in MQTT 3.1.1 mode, closing socket")
|
||||
internal_disconnect()
|
||||
}
|
||||
|
||||
func didReceive(_ reader: CocoaMQTTReader, auth: FrameAuth) {
|
||||
printWarning("Received AUTH in MQTT 3.1.1 mode, closing socket")
|
||||
internal_disconnect()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,903 @@
|
|||
//
|
||||
// CocoaMQTT5.swift
|
||||
// CocoaMQTT5
|
||||
//
|
||||
// Created by Feng Lee<feng@eqmtt.io> on 14/8/3.
|
||||
// Copyright (c) 2015 emqx.io. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import MqttCocoaAsyncSocket
|
||||
|
||||
/**
|
||||
* Connection State
|
||||
*/
|
||||
@objc public enum CocoaMQTTConnState: UInt8, CustomStringConvertible {
|
||||
case disconnected = 0
|
||||
case connecting
|
||||
case connected
|
||||
|
||||
public var description: String {
|
||||
switch self {
|
||||
case .connecting: return "connecting"
|
||||
case .connected: return "connected"
|
||||
case .disconnected: return "disconnected"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// CocoaMQTT5 Delegate
|
||||
@objc public protocol CocoaMQTT5Delegate {
|
||||
|
||||
///
|
||||
func mqtt5(_ mqtt5: CocoaMQTT5, didConnectAck ack: CocoaMQTTCONNACKReasonCode, connAckData: MqttDecodeConnAck?)
|
||||
|
||||
///
|
||||
func mqtt5(_ mqtt5: CocoaMQTT5, didPublishMessage message: CocoaMQTT5Message, id: UInt16)
|
||||
|
||||
///
|
||||
func mqtt5(_ mqtt5: CocoaMQTT5, didPublishAck id: UInt16, pubAckData: MqttDecodePubAck?)
|
||||
|
||||
///
|
||||
func mqtt5(_ mqtt5: CocoaMQTT5, didPublishRec id: UInt16, pubRecData: MqttDecodePubRec?)
|
||||
|
||||
///
|
||||
func mqtt5(_ mqtt5: CocoaMQTT5, didReceiveMessage message: CocoaMQTT5Message, id: UInt16, publishData: MqttDecodePublish?)
|
||||
|
||||
///
|
||||
func mqtt5(_ mqtt5: CocoaMQTT5, didSubscribeTopics success: NSDictionary, failed: [String], subAckData: MqttDecodeSubAck?)
|
||||
|
||||
///
|
||||
func mqtt5(_ mqtt5: CocoaMQTT5, didUnsubscribeTopics topics: [String], unsubAckData: MqttDecodeUnsubAck?)
|
||||
|
||||
///
|
||||
func mqtt5(_ mqtt5: CocoaMQTT5, didReceiveDisconnectReasonCode reasonCode: CocoaMQTTDISCONNECTReasonCode)
|
||||
|
||||
///
|
||||
func mqtt5(_ mqtt5: CocoaMQTT5, didReceiveAuthReasonCode reasonCode: CocoaMQTTAUTHReasonCode)
|
||||
|
||||
///
|
||||
func mqtt5DidPing(_ mqtt5: CocoaMQTT5)
|
||||
|
||||
///
|
||||
func mqtt5DidReceivePong(_ mqtt5: CocoaMQTT5)
|
||||
|
||||
///
|
||||
func mqtt5DidDisconnect(_ mqtt5: CocoaMQTT5, withError err: Error?)
|
||||
|
||||
/// Manually validate SSL/TLS server certificate.
|
||||
///
|
||||
/// This method will be called if enable `allowUntrustCACertificate`
|
||||
@objc optional func mqtt5(_ mqtt5: CocoaMQTT5, didReceive trust: SecTrust, completionHandler: @escaping (Bool) -> Void)
|
||||
|
||||
@objc optional func mqtt5UrlSession(_ mqtt: CocoaMQTT5, didReceiveTrust trust: SecTrust, didReceiveChallenge challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
|
||||
|
||||
///
|
||||
@objc optional func mqtt5(_ mqtt5: CocoaMQTT5, didPublishComplete id: UInt16, pubCompData: MqttDecodePubComp?)
|
||||
|
||||
///
|
||||
@objc optional func mqtt5(_ mqtt5: CocoaMQTT5, didStateChangeTo state: CocoaMQTTConnState)
|
||||
|
||||
/// Called when auto-reconnect schedules a reconnect attempt after an unexpected disconnect.
|
||||
@objc optional func mqtt5(_ mqtt5: CocoaMQTT5, didScheduleReconnect attemptCount: UInt, after interval: UInt16)
|
||||
}
|
||||
|
||||
/// set mqtt version to 5.0
|
||||
public func setMqtt5Version() {
|
||||
if let storage = CocoaMQTTStorage() {
|
||||
storage.setMQTTVersion("5.0")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Blueprint of the MQTT Client
|
||||
*/
|
||||
protocol CocoaMQTT5Client {
|
||||
|
||||
/* Basic Properties */
|
||||
|
||||
var host: String { get set }
|
||||
var port: UInt16 { get set }
|
||||
var clientID: String { get }
|
||||
var username: String? {get set}
|
||||
var password: String? {get set}
|
||||
var cleanSession: Bool {get set}
|
||||
var keepAlive: UInt16 {get set}
|
||||
var willMessage: CocoaMQTT5Message? {get set}
|
||||
var connectProperties: MqttConnectProperties? {get set}
|
||||
var authProperties: MqttAuthProperties? {get set}
|
||||
|
||||
/* Basic Properties */
|
||||
|
||||
/* CONNNEC/DISCONNECT */
|
||||
|
||||
func connect() -> Bool
|
||||
func connect(timeout: TimeInterval) -> Bool
|
||||
func disconnect()
|
||||
func ping()
|
||||
|
||||
/* CONNNEC/DISCONNECT */
|
||||
|
||||
/* PUBLISH/SUBSCRIBE */
|
||||
|
||||
func subscribe(_ topic: String, qos: CocoaMQTTQoS)
|
||||
func subscribe(_ topics: [MqttSubscription])
|
||||
|
||||
func unsubscribe(_ topic: String)
|
||||
func unsubscribe(_ topics: [MqttSubscription])
|
||||
|
||||
func publish(_ topic: String, withString string: String, qos: CocoaMQTTQoS, DUP: Bool, retained: Bool, properties: MqttPublishProperties) -> Int
|
||||
func publish(_ message: CocoaMQTT5Message, DUP: Bool, retained: Bool, properties: MqttPublishProperties) -> Int
|
||||
|
||||
/* PUBLISH/SUBSCRIBE */
|
||||
}
|
||||
|
||||
/// MQTT Client
|
||||
///
|
||||
/// - Note: MGCDAsyncSocket need delegate to extend NSObject
|
||||
public class CocoaMQTT5: NSObject, CocoaMQTT5Client {
|
||||
|
||||
public weak var delegate: CocoaMQTT5Delegate?
|
||||
|
||||
private var version = "5.0"
|
||||
|
||||
public var host = "localhost"
|
||||
|
||||
public var port: UInt16 = 1883
|
||||
|
||||
public var clientID: String
|
||||
|
||||
public var username: String?
|
||||
|
||||
public var password: String?
|
||||
|
||||
/// Clean Session flag. Default is true
|
||||
///
|
||||
/// - TODO: What's behavior each Clean Session flags???
|
||||
public var cleanSession = true
|
||||
|
||||
/// Setup a **Last Will Message** to client before connecting to broker
|
||||
public var willMessage: CocoaMQTT5Message?
|
||||
|
||||
/// Enable backgounding socket if running on iOS platform. Default is true
|
||||
///
|
||||
/// - Note:
|
||||
public var backgroundOnSocket: Bool {
|
||||
get { return (self.socket as? CocoaMQTTSocket)?.backgroundOnSocket ?? true }
|
||||
set { (self.socket as? CocoaMQTTSocket)?.backgroundOnSocket = newValue }
|
||||
}
|
||||
|
||||
/// Delegate Executed queue. Default is `DispatchQueue.main`
|
||||
///
|
||||
/// The delegate/closure callback function will be committed asynchronously to it
|
||||
public var delegateQueue = DispatchQueue.main
|
||||
|
||||
@ConcurrentAtomic(wrappedValue: CocoaMQTTConnState.disconnected, label: "CocoaMQTT5.connState")
|
||||
public var connState {
|
||||
didSet {
|
||||
__delegate_queue {
|
||||
self.delegate?.mqtt5?(self, didStateChangeTo: self.connState)
|
||||
self.didChangeState(self, self.connState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// deliver
|
||||
private var deliver = CocoaMQTTDeliver()
|
||||
|
||||
/// Re-deliver the un-acked messages
|
||||
public var deliverTimeout: Double {
|
||||
get { return deliver.retryTimeInterval }
|
||||
set { deliver.retryTimeInterval = newValue }
|
||||
}
|
||||
|
||||
/// Message queue size. default 1000
|
||||
///
|
||||
/// The new publishing messages of Qos1/Qos2 will be drop, if the queue is full
|
||||
public var messageQueueSize: UInt {
|
||||
get { return deliver.mqueueSize }
|
||||
set { deliver.mqueueSize = newValue }
|
||||
}
|
||||
|
||||
/// In-flight window size. default 10
|
||||
public var inflightWindowSize: UInt {
|
||||
get { return deliver.inflightWindowSize }
|
||||
set { deliver.inflightWindowSize = newValue }
|
||||
}
|
||||
|
||||
/// Keep alive time interval
|
||||
public var keepAlive: UInt16 = 60
|
||||
private var aliveTimer: CocoaMQTTTimer?
|
||||
|
||||
/// Enable auto-reconnect mechanism
|
||||
public var autoReconnect = false
|
||||
|
||||
/// Reconnect time interval
|
||||
///
|
||||
/// - note: This value will be increased with `autoReconnectTimeInterval *= 2`
|
||||
/// if reconnect failed
|
||||
public var autoReconnectTimeInterval: UInt16 = 1 // starts from 1 second
|
||||
|
||||
/// Maximum auto reconnect time interval
|
||||
///
|
||||
/// The timer starts from `autoReconnectTimeInterval` second and grows exponentially until this value
|
||||
/// After that, it uses this value for subsequent requests.
|
||||
public var maxAutoReconnectTimeInterval: UInt16 = 128 // 128 seconds
|
||||
|
||||
/// 3.1.2.11 CONNECT Properties
|
||||
public var connectProperties: MqttConnectProperties?
|
||||
|
||||
/// 3.15.2.2 AUTH Properties
|
||||
public var authProperties: MqttAuthProperties?
|
||||
|
||||
/// Auto-reconnect backoff interval in seconds for the current reconnect cycle.
|
||||
///
|
||||
/// This value is advanced for the next reconnect attempt while auto-reconnect is active,
|
||||
/// and resets to `0` when auto-reconnect is inactive.
|
||||
public private(set) var reconnectTimeInterval: UInt16 = 0
|
||||
|
||||
/// Number of reconnect attempts scheduled in the current auto-reconnect cycle.
|
||||
///
|
||||
/// The value resets to `0` after a successful connection or expected disconnect.
|
||||
public private(set) var reconnectAttemptCount: UInt = 0
|
||||
|
||||
private var autoReconnTimer: CocoaMQTTTimer?
|
||||
private var is_internal_disconnected = false
|
||||
|
||||
/// Console log level
|
||||
public var logLevel: CocoaMQTTLoggerLevel {
|
||||
get {
|
||||
return CocoaMQTTLogger.logger.minLevel
|
||||
}
|
||||
set {
|
||||
CocoaMQTTLogger.logger.minLevel = newValue
|
||||
}
|
||||
}
|
||||
|
||||
/// Enable SSL connection
|
||||
public var enableSSL: Bool {
|
||||
get { return self.socket.enableSSL }
|
||||
set { socket.enableSSL = newValue }
|
||||
}
|
||||
|
||||
///
|
||||
public var sslSettings: [String: NSObject]? {
|
||||
get { return (self.socket as? CocoaMQTTSocket)?.sslSettings ?? nil }
|
||||
set { (self.socket as? CocoaMQTTSocket)?.sslSettings = newValue }
|
||||
}
|
||||
|
||||
/// Allow self-signed ca certificate.
|
||||
///
|
||||
/// Default is false
|
||||
public var allowUntrustCACertificate: Bool {
|
||||
get { return (self.socket as? CocoaMQTTSocket)?.allowUntrustCACertificate ?? false }
|
||||
set { (self.socket as? CocoaMQTTSocket)?.allowUntrustCACertificate = newValue }
|
||||
}
|
||||
|
||||
/// The subscribed topics in current communication
|
||||
public var subscriptions = ThreadSafeDictionary<String, CocoaMQTTQoS>(label: "subscriptions")
|
||||
|
||||
fileprivate var subscriptionsWaitingAck = ThreadSafeDictionary<UInt16, [MqttSubscription]>(label: "subscriptionsWaitingAck")
|
||||
fileprivate var unsubscriptionsWaitingAck = ThreadSafeDictionary<UInt16, [MqttSubscription]>(label: "unsubscriptionsWaitingAck")
|
||||
|
||||
/// Sending messages
|
||||
fileprivate var sendingMessages: [UInt16: CocoaMQTT5Message] = [:]
|
||||
|
||||
/// message id counter
|
||||
private var _msgid: UInt16 = 0
|
||||
fileprivate var socket: CocoaMQTTSocketProtocol
|
||||
fileprivate var reader: CocoaMQTTReader?
|
||||
|
||||
// Closures
|
||||
public var didConnectAck: (CocoaMQTT5, CocoaMQTTCONNACKReasonCode, MqttDecodeConnAck?) -> Void = { _, _, _ in }
|
||||
public var didPublishMessage: (CocoaMQTT5, CocoaMQTT5Message, UInt16) -> Void = { _, _, _ in }
|
||||
public var didPublishAck: (CocoaMQTT5, UInt16, MqttDecodePubAck?) -> Void = { _, _, _ in }
|
||||
public var didPublishRec: (CocoaMQTT5, UInt16, MqttDecodePubRec?) -> Void = { _, _, _ in }
|
||||
public var didReceiveMessage: (CocoaMQTT5, CocoaMQTT5Message, UInt16, MqttDecodePublish?) -> Void = { _, _, _, _ in }
|
||||
public var didSubscribeTopics: (CocoaMQTT5, NSDictionary, [String], MqttDecodeSubAck?) -> Void = { _, _, _, _ in }
|
||||
public var didUnsubscribeTopics: (CocoaMQTT5, [String], MqttDecodeUnsubAck?) -> Void = { _, _, _ in }
|
||||
public var didPing: (CocoaMQTT5) -> Void = { _ in }
|
||||
public var didReceivePong: (CocoaMQTT5) -> Void = { _ in }
|
||||
public var didDisconnect: (CocoaMQTT5, Error?) -> Void = { _, _ in }
|
||||
public var didDisconnectReasonCode: (CocoaMQTT5, CocoaMQTTDISCONNECTReasonCode) -> Void = { _, _ in }
|
||||
public var didAuthReasonCode: (CocoaMQTT5, CocoaMQTTAUTHReasonCode) -> Void = { _, _ in }
|
||||
public var didReceiveTrust: (CocoaMQTT5, SecTrust, @escaping (Bool) -> Swift.Void) -> Void = { _, _, _ in }
|
||||
public var didCompletePublish: (CocoaMQTT5, UInt16, MqttDecodePubComp?) -> Void = { _, _, _ in }
|
||||
public var didChangeState: (CocoaMQTT5, CocoaMQTTConnState) -> Void = { _, _ in }
|
||||
public var didScheduleReconnect: (CocoaMQTT5, UInt, UInt16) -> Void = { _, _, _ in }
|
||||
|
||||
/// Initial client object
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - clientID: Client Identifier
|
||||
/// - host: The MQTT broker host domain or IP address. Default is "localhost"
|
||||
/// - port: The MQTT service port of host. Default is 1883
|
||||
public init(clientID: String, host: String = "localhost", port: UInt16 = 1883, socket: CocoaMQTTSocketProtocol = CocoaMQTTSocket()) {
|
||||
self.clientID = clientID
|
||||
self.host = host
|
||||
self.port = port
|
||||
self.socket = socket
|
||||
super.init()
|
||||
deliver.delegate = self
|
||||
if let storage = CocoaMQTTStorage() {
|
||||
storage.setMQTTVersion("5.0")
|
||||
} else {
|
||||
printWarning("Localstorage initial failed for key: \(clientID)")
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
aliveTimer?.suspend()
|
||||
autoReconnTimer?.suspend()
|
||||
|
||||
socket.setDelegate(nil, delegateQueue: nil)
|
||||
socket.disconnect()
|
||||
}
|
||||
|
||||
fileprivate func send(_ frame: Frame, tag: Int = 0) {
|
||||
printDebug("SEND: \(frame)")
|
||||
let data = frame.bytes(version: version)
|
||||
|
||||
socket.write(Data(bytes: data, count: data.count), withTimeout: 5, tag: tag)
|
||||
}
|
||||
|
||||
fileprivate func sendConnectFrame() {
|
||||
|
||||
var connect = FrameConnect(clientID: clientID)
|
||||
connect.keepAlive = keepAlive
|
||||
connect.username = username
|
||||
connect.password = password
|
||||
connect.willMsg5 = willMessage
|
||||
connect.cleansess = cleanSession
|
||||
|
||||
connect.connectProperties = connectProperties
|
||||
|
||||
send(connect)
|
||||
reader!.start()
|
||||
}
|
||||
|
||||
fileprivate func nextMessageID() -> UInt16 {
|
||||
if _msgid == UInt16.max {
|
||||
_msgid = 0
|
||||
}
|
||||
_msgid += 1
|
||||
return _msgid
|
||||
}
|
||||
|
||||
fileprivate func puback(_ type: FrameType, msgid: UInt16) {
|
||||
switch type {
|
||||
case .puback:
|
||||
send(FramePubAck(msgid: msgid, reasonCode: CocoaMQTTPUBACKReasonCode.success))
|
||||
case .pubrec:
|
||||
send(FramePubRec(msgid: msgid, reasonCode: CocoaMQTTPUBRECReasonCode.success))
|
||||
case .pubcomp:
|
||||
send(FramePubComp(msgid: msgid, reasonCode: CocoaMQTTPUBCOMPReasonCode.success))
|
||||
default: return
|
||||
}
|
||||
}
|
||||
|
||||
/// Connect to MQTT broker
|
||||
///
|
||||
/// - Returns:
|
||||
/// - Bool: It indicates whether successfully calling socket connect function.
|
||||
/// Not yet established correct MQTT session
|
||||
public func connect() -> Bool {
|
||||
return connect(timeout: -1)
|
||||
}
|
||||
|
||||
/// Connect to MQTT broker
|
||||
/// - Parameters:
|
||||
/// - timeout: Connect timeout
|
||||
/// - Returns:
|
||||
/// - Bool: It indicates whether successfully calling socket connect function.
|
||||
/// Not yet established correct MQTT session
|
||||
public func connect(timeout: TimeInterval) -> Bool {
|
||||
socket.setDelegate(self, delegateQueue: delegateQueue)
|
||||
reader = CocoaMQTTReader(socket: socket, delegate: self)
|
||||
do {
|
||||
if timeout > 0 {
|
||||
try socket.connect(toHost: self.host, onPort: self.port, withTimeout: timeout)
|
||||
} else {
|
||||
try socket.connect(toHost: self.host, onPort: self.port)
|
||||
}
|
||||
|
||||
delegateQueue.async { [weak self] in
|
||||
guard let self = self else { return }
|
||||
self.connState = .connecting
|
||||
}
|
||||
|
||||
return true
|
||||
} catch let error as NSError {
|
||||
printError("socket connect error: \(error.description)")
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/// Send a DISCONNECT packet to the broker then close the connection
|
||||
///
|
||||
/// - Note: Only can be called from outside.
|
||||
/// This closes the connection expectedly, so auto-reconnect will not run.
|
||||
public func disconnect() {
|
||||
expected_disconnect(reasonCode: .normalDisconnection)
|
||||
}
|
||||
|
||||
public func disconnect(reasonCode: CocoaMQTTDISCONNECTReasonCode, userProperties: [String: String] ) {
|
||||
expected_disconnect(reasonCode: reasonCode, userProperties: userProperties)
|
||||
}
|
||||
|
||||
/// Disconnect unexpectedly.
|
||||
/// This keeps auto-reconnect behavior enabled.
|
||||
func internal_disconnect() {
|
||||
is_internal_disconnected = false
|
||||
socket.disconnect()
|
||||
}
|
||||
|
||||
func internal_disconnect_withProperties(reasonCode: CocoaMQTTDISCONNECTReasonCode, userProperties: [String: String] ) {
|
||||
expected_disconnect(reasonCode: reasonCode, userProperties: userProperties)
|
||||
}
|
||||
|
||||
private func expected_disconnect(reasonCode: CocoaMQTTDISCONNECTReasonCode, userProperties: [String: String]? = nil) {
|
||||
is_internal_disconnected = true
|
||||
var frameDisconnect = FrameDisconnect(disconnectReasonCode: reasonCode)
|
||||
frameDisconnect.userProperties = userProperties ?? [:]
|
||||
send(frameDisconnect, tag: -0xE0)
|
||||
socket.disconnect()
|
||||
}
|
||||
/// Send a PING request to broker
|
||||
public func ping() {
|
||||
printDebug("ping")
|
||||
send(FramePingReq(), tag: -0xC0)
|
||||
|
||||
__delegate_queue {
|
||||
self.delegate?.mqtt5DidPing(self)
|
||||
self.didPing(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Publish a message to broker
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - topic: Topic Name. It can not contain '#', '+' wildcards
|
||||
/// - string: Payload string
|
||||
/// - qos: Qos. Default is Qos1
|
||||
/// - retained: Retained flag. Mark this message is a retained message. default is false
|
||||
/// - properties: Publish Properties
|
||||
/// - Returns:
|
||||
/// - 0 will be returned, if the message's qos is qos0
|
||||
/// - 1-65535 will be returned, if the messages's qos is qos1/qos2
|
||||
/// - -1 will be returned, if the messages queue is full
|
||||
@discardableResult
|
||||
public func publish(_ topic: String, withString string: String, qos: CocoaMQTTQoS = .qos1, DUP: Bool = false, retained: Bool = false, properties: MqttPublishProperties) -> Int {
|
||||
assert(!(DUP && qos == .qos0), "DUP=true with QoS0 is invalid for MQTT PUBLISH.")
|
||||
guard !(DUP && qos == .qos0) else {
|
||||
printError("Invalid PUBLISH flags: DUP=true requires QoS1 or QoS2.")
|
||||
return -1
|
||||
}
|
||||
let message = CocoaMQTT5Message(topic: topic, string: string, qos: qos, retained: retained)
|
||||
return publish(message, DUP: DUP, retained: retained, properties: properties)
|
||||
}
|
||||
|
||||
/// Publish a message to broker
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - message: Message
|
||||
/// - properties: Publish Properties
|
||||
@discardableResult
|
||||
public func publish(_ message: CocoaMQTT5Message, DUP: Bool = false, retained: Bool = false, properties: MqttPublishProperties) -> Int {
|
||||
assert(!(DUP && message.qos == .qos0), "DUP=true with QoS0 is invalid for MQTT PUBLISH.")
|
||||
guard !(DUP && message.qos == .qos0) else {
|
||||
printError("Invalid PUBLISH flags: DUP=true requires QoS1 or QoS2.")
|
||||
return -1
|
||||
}
|
||||
|
||||
let msgid: UInt16
|
||||
|
||||
if message.qos == .qos0 {
|
||||
msgid = 0
|
||||
} else {
|
||||
msgid = nextMessageID()
|
||||
}
|
||||
|
||||
printDebug("message.topic \(message.topic ) = message.payload \(message.payload)")
|
||||
|
||||
var frame = FramePublish(topic: message.topic,
|
||||
payload: message.payload,
|
||||
qos: message.qos,
|
||||
msgid: msgid)
|
||||
frame.qos = message.qos
|
||||
frame.dup = DUP
|
||||
frame.publishProperties = properties
|
||||
frame.retained = message.retained
|
||||
|
||||
delegateQueue.async {
|
||||
self.sendingMessages[msgid] = message
|
||||
}
|
||||
|
||||
// Push frame to deliver message queue
|
||||
guard deliver.add(frame) else {
|
||||
delegateQueue.async {
|
||||
self.sendingMessages.removeValue(forKey: msgid)
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
return Int(msgid)
|
||||
}
|
||||
|
||||
/// Subscribe a `<Topic Name>/<Topic Filter>`
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - topic: Topic Name or Topic Filter
|
||||
/// - qos: Qos. Default is qos1
|
||||
public func subscribe(_ topic: String, qos: CocoaMQTTQoS = .qos1) {
|
||||
let filter = MqttSubscription(topic: topic, qos: qos)
|
||||
return subscribe([filter])
|
||||
}
|
||||
|
||||
/// Subscribe a lists of topics
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - topics: A list of tuples presented by `(<Topic Names>/<Topic Filters>, Qos)`
|
||||
public func subscribe(_ topics: [MqttSubscription]) {
|
||||
let msgid = nextMessageID()
|
||||
let frame = FrameSubscribe(msgid: msgid, subscriptionList: topics)
|
||||
send(frame, tag: Int(msgid))
|
||||
subscriptionsWaitingAck[msgid] = topics
|
||||
}
|
||||
|
||||
/// Subscribe a lists of topics
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - topics: A list of tuples presented by `(<Topic Names>/<Topic Filters>, Qos)`
|
||||
/// - packetIdentifier: SUBSCRIBE Variable Header
|
||||
/// - subscriptionIdentifier: Subscription Identifier
|
||||
/// - userProperty: User Property
|
||||
public func subscribe(_ topics: [MqttSubscription], packetIdentifier: UInt16? = nil, subscriptionIdentifier: UInt32? = nil, userProperty: [String: String] = [:]) {
|
||||
let msgid = nextMessageID()
|
||||
let frame = FrameSubscribe(msgid: msgid, subscriptionList: topics, packetIdentifier: packetIdentifier, subscriptionIdentifier: subscriptionIdentifier, userProperty: userProperty)
|
||||
send(frame, tag: Int(msgid))
|
||||
subscriptionsWaitingAck[msgid] = topics
|
||||
}
|
||||
|
||||
/// Unsubscribe a Topic
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - topic: A Topic Name or Topic Filter
|
||||
public func unsubscribe(_ topic: String) {
|
||||
let filter = MqttSubscription(topic: topic)
|
||||
return unsubscribe([filter])
|
||||
}
|
||||
|
||||
/// Unsubscribe a list of topics
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - topics: A list of `<Topic Names>/<Topic Filters>`
|
||||
public func unsubscribe(_ topics: [MqttSubscription]) {
|
||||
let msgid = nextMessageID()
|
||||
let frame = FrameUnsubscribe(msgid: msgid, topics: topics)
|
||||
unsubscriptionsWaitingAck[msgid] = topics
|
||||
send(frame, tag: Int(msgid))
|
||||
}
|
||||
|
||||
/// Authentication exchange
|
||||
///
|
||||
///
|
||||
public func auth(reasonCode: CocoaMQTTAUTHReasonCode, authProperties: MqttAuthProperties) {
|
||||
printDebug("auth")
|
||||
let frame = FrameAuth(reasonCode: reasonCode, authProperties: authProperties)
|
||||
|
||||
send(frame)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: CocoaMQTTDeliverProtocol
|
||||
extension CocoaMQTT5: CocoaMQTTDeliverProtocol {
|
||||
|
||||
func deliver(_ deliver: CocoaMQTTDeliver, wantToSend frame: Frame) {
|
||||
if let publish = frame as? FramePublish {
|
||||
let msgid = publish.msgid
|
||||
var message: CocoaMQTT5Message?
|
||||
|
||||
if let sendingMessage = sendingMessages[msgid] {
|
||||
message = sendingMessage
|
||||
// printError("Want send \(frame), but not found in CocoaMQTT cache")
|
||||
} else {
|
||||
message = CocoaMQTT5Message(topic: publish.topic, payload: publish.payload())
|
||||
}
|
||||
|
||||
send(publish, tag: Int(msgid))
|
||||
|
||||
if let message = message {
|
||||
self.delegate?.mqtt5(self, didPublishMessage: message, id: msgid)
|
||||
self.didPublishMessage(self, message, msgid)
|
||||
}
|
||||
} else if let pubrel = frame as? FramePubRel {
|
||||
// -- Send PUBREL
|
||||
send(pubrel, tag: Int(pubrel.msgid))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension CocoaMQTT5 {
|
||||
|
||||
func __delegate_queue(_ fun: @escaping () -> Void) {
|
||||
delegateQueue.async { [weak self] in
|
||||
guard self != nil else { return }
|
||||
fun()
|
||||
}
|
||||
}
|
||||
|
||||
private func prepareAutoReconnectAttempt() {
|
||||
if reconnectTimeInterval == 0 {
|
||||
reconnectTimeInterval = min(autoReconnectTimeInterval, maxAutoReconnectTimeInterval)
|
||||
}
|
||||
reconnectAttemptCount += 1
|
||||
}
|
||||
|
||||
private func updateAutoReconnectIntervalForNextAttempt() {
|
||||
let doubledInterval = UInt32(reconnectTimeInterval) * 2
|
||||
reconnectTimeInterval = UInt16(min(doubledInterval, UInt32(maxAutoReconnectTimeInterval)))
|
||||
}
|
||||
|
||||
private func resetAutoReconnectState() {
|
||||
reconnectTimeInterval = 0
|
||||
reconnectAttemptCount = 0
|
||||
autoReconnTimer = nil
|
||||
}
|
||||
|
||||
private func notifyAutoReconnectScheduled() {
|
||||
delegate?.mqtt5?(self, didScheduleReconnect: reconnectAttemptCount, after: reconnectTimeInterval)
|
||||
didScheduleReconnect(self, reconnectAttemptCount, reconnectTimeInterval)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - CocoaMQTTSocketDelegate
|
||||
extension CocoaMQTT5: CocoaMQTTSocketDelegate {
|
||||
|
||||
public func socketConnected(_ socket: CocoaMQTTSocketProtocol) {
|
||||
sendConnectFrame()
|
||||
}
|
||||
|
||||
public func socket(_ socket: CocoaMQTTSocketProtocol,
|
||||
didReceive trust: SecTrust,
|
||||
completionHandler: @escaping (Bool) -> Swift.Void) {
|
||||
|
||||
printDebug("Call the SSL/TLS manually validating function")
|
||||
|
||||
delegate?.mqtt5?(self, didReceive: trust, completionHandler: completionHandler)
|
||||
didReceiveTrust(self, trust, completionHandler)
|
||||
}
|
||||
|
||||
public func socketUrlSession(_ socket: CocoaMQTTSocketProtocol, didReceiveTrust trust: SecTrust, didReceiveChallenge challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
|
||||
printDebug("Call the SSL/TLS manually validating function - socketUrlSession")
|
||||
|
||||
delegate?.mqtt5UrlSession?(self, didReceiveTrust: trust, didReceiveChallenge: challenge, completionHandler: completionHandler)
|
||||
}
|
||||
|
||||
// ?
|
||||
public func socketDidSecure(_ sock: MGCDAsyncSocket) {
|
||||
printDebug("Socket has successfully completed SSL/TLS negotiation")
|
||||
sendConnectFrame()
|
||||
}
|
||||
|
||||
public func socket(_ socket: CocoaMQTTSocketProtocol, didWriteDataWithTag tag: Int) {
|
||||
// XXX: How to print writed bytes??
|
||||
}
|
||||
|
||||
public func socket(_ socket: CocoaMQTTSocketProtocol, didRead data: Data, withTag tag: Int) {
|
||||
let etag = CocoaMQTTReadTag(rawValue: tag)!
|
||||
var bytes = [UInt8]([0])
|
||||
switch etag {
|
||||
case CocoaMQTTReadTag.header:
|
||||
data.copyBytes(to: &bytes, count: 1)
|
||||
reader!.headerReady(bytes[0])
|
||||
case CocoaMQTTReadTag.length:
|
||||
data.copyBytes(to: &bytes, count: 1)
|
||||
reader!.lengthReady(bytes[0])
|
||||
case CocoaMQTTReadTag.payload:
|
||||
reader!.payloadReady(data)
|
||||
}
|
||||
}
|
||||
|
||||
public func socketDidDisconnect(_ socket: CocoaMQTTSocketProtocol, withError err: Error?) {
|
||||
// Clean up
|
||||
socket.setDelegate(nil, delegateQueue: nil)
|
||||
if is_internal_disconnected || !autoReconnect {
|
||||
resetAutoReconnectState()
|
||||
}
|
||||
|
||||
connState = .disconnected
|
||||
|
||||
delegate?.mqtt5DidDisconnect(self, withError: err)
|
||||
didDisconnect(self, err)
|
||||
|
||||
guard !is_internal_disconnected else {
|
||||
is_internal_disconnected = false
|
||||
return
|
||||
}
|
||||
|
||||
guard autoReconnect else {
|
||||
resetAutoReconnectState()
|
||||
return
|
||||
}
|
||||
|
||||
prepareAutoReconnectAttempt()
|
||||
|
||||
// Start reconnector once socket error occurred
|
||||
printInfo("Try reconnect to server after \(reconnectTimeInterval)s")
|
||||
notifyAutoReconnectScheduled()
|
||||
autoReconnTimer = CocoaMQTTTimer.after(Double(reconnectTimeInterval), name: "autoReconnTimer", { [weak self] in
|
||||
guard let self = self else { return }
|
||||
self.updateAutoReconnectIntervalForNextAttempt()
|
||||
_ = self.connect()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - CocoaMQTTReaderDelegate
|
||||
extension CocoaMQTT5: CocoaMQTTReaderDelegate {
|
||||
|
||||
func didReceive(_ reader: CocoaMQTTReader, disconnect: FrameDisconnect) {
|
||||
let reasonCode = disconnect.receiveReasonCode ?? .normalDisconnection
|
||||
delegate?.mqtt5(self, didReceiveDisconnectReasonCode: reasonCode)
|
||||
didDisconnectReasonCode(self, reasonCode)
|
||||
}
|
||||
|
||||
func didReceive(_ reader: CocoaMQTTReader, auth: FrameAuth) {
|
||||
let reasonCode = auth.receiveReasonCode ?? .success
|
||||
delegate?.mqtt5(self, didReceiveAuthReasonCode: reasonCode)
|
||||
didAuthReasonCode(self, reasonCode)
|
||||
}
|
||||
|
||||
func didReceive(_ reader: CocoaMQTTReader, connack: FrameConnAck) {
|
||||
printDebug("RECV: \(connack)")
|
||||
|
||||
if connack.reasonCode == .success {
|
||||
|
||||
// Disable auto-reconnect
|
||||
|
||||
resetAutoReconnectState()
|
||||
is_internal_disconnected = false
|
||||
|
||||
// Start keepalive timer
|
||||
|
||||
let interval = Double(keepAlive <= 0 ? 60: keepAlive)
|
||||
|
||||
aliveTimer = CocoaMQTTTimer.every(interval, name: "aliveTimer") { [weak self] in
|
||||
guard let self = self else { return }
|
||||
self.delegateQueue.async {
|
||||
guard self.connState == .connected else {
|
||||
self.aliveTimer = nil
|
||||
return
|
||||
}
|
||||
self.ping()
|
||||
}
|
||||
}
|
||||
|
||||
// recover session if enable
|
||||
|
||||
if cleanSession {
|
||||
deliver.cleanAll()
|
||||
} else {
|
||||
if let storage = CocoaMQTTStorage(by: clientID) {
|
||||
deliver.recoverSessionBy(storage)
|
||||
} else {
|
||||
printWarning("Localstorage initial failed for key: \(clientID)")
|
||||
}
|
||||
}
|
||||
|
||||
connState = .connected
|
||||
|
||||
} else {
|
||||
connState = .disconnected
|
||||
expected_disconnect(reasonCode: .normalDisconnection)
|
||||
}
|
||||
|
||||
delegate?.mqtt5(self, didConnectAck: connack.reasonCode ?? CocoaMQTTCONNACKReasonCode.unspecifiedError, connAckData: connack.connackProperties ?? nil)
|
||||
didConnectAck(self, connack.reasonCode ?? CocoaMQTTCONNACKReasonCode.unspecifiedError, connack.connackProperties ?? nil)
|
||||
}
|
||||
|
||||
func didReceive(_ reader: CocoaMQTTReader, publish: FramePublish) {
|
||||
printDebug("RECV: \(publish)")
|
||||
|
||||
let message = CocoaMQTT5Message(topic: publish.mqtt5Topic, payload: publish.payload5(), qos: publish.qos, retained: publish.retained)
|
||||
|
||||
message.duplicated = publish.dup
|
||||
message.contentType = publish.publishRecProperties?.contentType
|
||||
|
||||
printInfo("Received message: \(message)")
|
||||
delegate?.mqtt5(self, didReceiveMessage: message, id: publish.msgid, publishData: publish.publishRecProperties ?? nil)
|
||||
didReceiveMessage(self, message, publish.msgid, publish.publishRecProperties ?? nil)
|
||||
|
||||
if message.qos == .qos1 {
|
||||
puback(FrameType.puback, msgid: publish.msgid)
|
||||
} else if message.qos == .qos2 {
|
||||
puback(FrameType.pubrec, msgid: publish.msgid)
|
||||
}
|
||||
}
|
||||
|
||||
func didReceive(_ reader: CocoaMQTTReader, puback: FramePubAck) {
|
||||
printDebug("RECV: \(puback)")
|
||||
|
||||
deliver.ack(by: puback)
|
||||
|
||||
delegate?.mqtt5(self, didPublishAck: puback.msgid, pubAckData: puback.pubAckProperties ?? nil)
|
||||
didPublishAck(self, puback.msgid, puback.pubAckProperties ?? nil)
|
||||
}
|
||||
|
||||
func didReceive(_ reader: CocoaMQTTReader, pubrec: FramePubRec) {
|
||||
printDebug("RECV: \(pubrec)")
|
||||
|
||||
deliver.ack(by: pubrec)
|
||||
|
||||
delegate?.mqtt5(self, didPublishRec: pubrec.msgid, pubRecData: pubrec.pubRecProperties ?? nil)
|
||||
didPublishRec(self, pubrec.msgid, pubrec.pubRecProperties ?? nil)
|
||||
}
|
||||
|
||||
func didReceive(_ reader: CocoaMQTTReader, pubrel: FramePubRel) {
|
||||
printDebug("RECV: \(pubrel)")
|
||||
|
||||
puback(FrameType.pubcomp, msgid: pubrel.msgid)
|
||||
}
|
||||
|
||||
func didReceive(_ reader: CocoaMQTTReader, pubcomp: FramePubComp) {
|
||||
printDebug("RECV: \(pubcomp)")
|
||||
|
||||
deliver.ack(by: pubcomp)
|
||||
|
||||
delegate?.mqtt5?(self, didPublishComplete: pubcomp.msgid, pubCompData: pubcomp.pubCompProperties ?? nil)
|
||||
didCompletePublish(self, pubcomp.msgid, pubcomp.pubCompProperties ?? nil)
|
||||
}
|
||||
|
||||
func didReceive(_ reader: CocoaMQTTReader, suback: FrameSubAck) {
|
||||
printDebug("RECV: \(suback)")
|
||||
guard let topicsAndQos = subscriptionsWaitingAck.removeValue(forKey: suback.msgid) else {
|
||||
printWarning("UNEXPECT SUBACK Received: \(suback)")
|
||||
return
|
||||
}
|
||||
|
||||
guard topicsAndQos.count == suback.grantedQos.count else {
|
||||
printWarning("UNEXPECT SUBACK Recivied: \(suback)")
|
||||
return
|
||||
}
|
||||
|
||||
let success: NSMutableDictionary = NSMutableDictionary()
|
||||
var failed = [String]()
|
||||
for (idx, subscriptionList) in topicsAndQos.enumerated() {
|
||||
if suback.grantedQos[idx] != .FAILURE {
|
||||
subscriptions[subscriptionList.topic] = suback.grantedQos[idx]
|
||||
success[subscriptionList.topic] = suback.grantedQos[idx].rawValue
|
||||
} else {
|
||||
failed.append(subscriptionList.topic)
|
||||
}
|
||||
}
|
||||
|
||||
delegate?.mqtt5(self, didSubscribeTopics: success, failed: failed, subAckData: suback.subAckProperties ?? nil)
|
||||
didSubscribeTopics(self, success, failed, suback.subAckProperties ?? nil)
|
||||
}
|
||||
|
||||
func didReceive(_ reader: CocoaMQTTReader, unsuback: FrameUnsubAck) {
|
||||
printDebug("RECV: \(unsuback)")
|
||||
|
||||
guard let topics = unsubscriptionsWaitingAck.removeValue(forKey: unsuback.msgid) else {
|
||||
printWarning("UNEXPECT UNSUBACK Received: \(unsuback.msgid)")
|
||||
return
|
||||
}
|
||||
// Remove local subscription
|
||||
var removeTopics: [String] = []
|
||||
for t in topics {
|
||||
removeTopics.append(t.topic)
|
||||
subscriptions.removeValue(forKey: t.topic)
|
||||
}
|
||||
|
||||
delegate?.mqtt5(self, didUnsubscribeTopics: removeTopics, unsubAckData: unsuback.unSubAckProperties ?? nil)
|
||||
didUnsubscribeTopics(self, removeTopics, unsuback.unSubAckProperties ?? nil)
|
||||
}
|
||||
|
||||
func didReceive(_ reader: CocoaMQTTReader, pingresp: FramePingResp) {
|
||||
printDebug("RECV: \(pingresp)")
|
||||
|
||||
delegate?.mqtt5DidReceivePong(self)
|
||||
didReceivePong(self)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,155 @@
|
|||
//
|
||||
// CocoaMQTT5Message.swift
|
||||
// CocoaMQTT
|
||||
//
|
||||
// Created by Created by liwei wang on 2021/11/10.
|
||||
// Copyright (c) 2015 emqx.io. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// MQTT Message
|
||||
public class CocoaMQTT5Message: NSObject {
|
||||
|
||||
public var qos = CocoaMQTTQoS.qos1
|
||||
|
||||
public var topic: String
|
||||
|
||||
public var payload: [UInt8]
|
||||
|
||||
public var retained = false
|
||||
|
||||
/// The `duplicated` property show that this message maybe has be received before
|
||||
///
|
||||
/// - note: Readonly property
|
||||
public var duplicated = false
|
||||
|
||||
/// 3.1.3.2.3 Payload Format Indicator
|
||||
public var isUTF8EncodedData: Bool = true
|
||||
/// 3.1.3.2.2 Will Delay Interval
|
||||
public var willDelayInterval: UInt32? = 0
|
||||
/// 3.1.3.2.4 Message Expiry Interval
|
||||
public var willExpiryInterval: UInt32? = .max
|
||||
/// 3.1.3.2.5 Content Type
|
||||
public var contentType: String?
|
||||
/// 3.1.3.2.6 Response Topic
|
||||
public var willResponseTopic: String?
|
||||
/// 3.1.3.2.7 Correlation Data
|
||||
public var willCorrelationData: [UInt8]?
|
||||
/// 3.1.3.2.8 User Property
|
||||
public var willUserProperty: [String: String]?
|
||||
|
||||
/// Return the payload as a utf8 string if possible
|
||||
///
|
||||
/// It will return nil if the payload is not a valid utf8 string
|
||||
public var string: String? {
|
||||
NSString(bytes: payload, length: payload.count, encoding: String.Encoding.utf8.rawValue) as String?
|
||||
}
|
||||
|
||||
public var properties: [UInt8] {
|
||||
|
||||
var properties = [UInt8]()
|
||||
var retVal = [UInt8]()
|
||||
|
||||
/// 3.1.3.2.2 Property Length
|
||||
if let willDelayInterval = self.willDelayInterval {
|
||||
properties += getMQTTPropertyData(type: CocoaMQTTPropertyName.willDelayInterval.rawValue, value: willDelayInterval.byteArrayLittleEndian)
|
||||
}
|
||||
|
||||
/// 3.1.3.2.4 Message Expiry Interval
|
||||
if let willExpiryInterval = self.willExpiryInterval {
|
||||
properties += getMQTTPropertyData(type: CocoaMQTTPropertyName.willExpiryInterval.rawValue, value: willExpiryInterval.byteArrayLittleEndian)
|
||||
}
|
||||
|
||||
/// 3.1.3.2.3 Payload Format Indicator
|
||||
if isUTF8EncodedData {
|
||||
properties += [1, 1]
|
||||
} else {
|
||||
properties += [1, 0]
|
||||
}
|
||||
|
||||
/// 3.1.3.2.5 Content Type
|
||||
if var contentType = self.contentType {
|
||||
if isUTF8EncodedData {
|
||||
contentType = contentType.stringUTF8
|
||||
}
|
||||
properties += getMQTTPropertyData(type: CocoaMQTTPropertyName.contentType.rawValue, value: contentType.bytesWithLength)
|
||||
}
|
||||
|
||||
/// 3.1.3.2.6 Response Topic
|
||||
if var willResponseTopic = self.willResponseTopic {
|
||||
if isUTF8EncodedData {
|
||||
willResponseTopic = willResponseTopic.stringUTF8
|
||||
}
|
||||
properties += getMQTTPropertyData(type: CocoaMQTTPropertyName.responseTopic.rawValue, value: willResponseTopic.bytesWithLength)
|
||||
}
|
||||
|
||||
/// 3.1.3.2.7 Correlation Data
|
||||
if let willCorrelationData = self.willCorrelationData {
|
||||
let buff = UInt16(willCorrelationData.count).hlBytes + willCorrelationData
|
||||
properties += getMQTTPropertyData(type: CocoaMQTTPropertyName.correlationData.rawValue, value: buff)
|
||||
}
|
||||
|
||||
/// 3.1.3.2.8 User Property
|
||||
if let willUserProperty = self.willUserProperty {
|
||||
willUserProperty.forEach { element in
|
||||
properties.append(UInt8(CocoaMQTTPropertyName.userProperty.rawValue))
|
||||
if isUTF8EncodedData {
|
||||
let key = element.key.stringUTF8
|
||||
properties += key .bytesWithLength
|
||||
let value = element.value.stringUTF8
|
||||
properties += value.bytesWithLength
|
||||
} else {
|
||||
properties += element.key.bytesWithLength
|
||||
properties += element.value.bytesWithLength
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
retVal += properties
|
||||
return retVal
|
||||
}
|
||||
|
||||
public init(topic: String, string: String, qos: CocoaMQTTQoS = .qos1, retained: Bool = false) {
|
||||
|
||||
self.topic = topic
|
||||
self.payload = [UInt8](string.utf8)
|
||||
self.qos = qos
|
||||
self.retained = retained
|
||||
}
|
||||
|
||||
public init(topic: String, payload: [UInt8], qos: CocoaMQTTQoS = .qos1, retained: Bool = false) {
|
||||
|
||||
self.topic = topic
|
||||
self.payload = payload
|
||||
self.qos = qos
|
||||
self.retained = retained
|
||||
}
|
||||
|
||||
public init(topic: String, payload: [String: Any], qos: CocoaMQTTQoS = .qos1, retained: Bool = false) throws {
|
||||
let data = try JSONSerialization.data(withJSONObject: payload)
|
||||
self.topic = topic
|
||||
self.payload = [UInt8](data)
|
||||
self.qos = qos
|
||||
self.retained = retained
|
||||
}
|
||||
}
|
||||
|
||||
extension CocoaMQTT5Message {
|
||||
|
||||
public override var description: String {
|
||||
return "CocoaMQTT5Message(topic: \(topic), qos: \(qos), payload: \(payload.summary))"
|
||||
}
|
||||
}
|
||||
|
||||
// For test
|
||||
extension CocoaMQTT5Message {
|
||||
|
||||
var t_pub_frame: FramePublish {
|
||||
var frame = FramePublish(topic: topic, payload: payload, qos: qos, msgid: 0)
|
||||
frame.retained = retained
|
||||
frame.dup = duplicated
|
||||
return frame
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,353 @@
|
|||
//
|
||||
// CocoaMQTTDeliver.swift
|
||||
// CocoaMQTT
|
||||
//
|
||||
// Created by HJianBo on 2019/5/2.
|
||||
// Copyright © 2019 emqx.io. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Dispatch
|
||||
|
||||
protocol CocoaMQTTDeliverProtocol: AnyObject {
|
||||
|
||||
var delegateQueue: DispatchQueue { get set }
|
||||
|
||||
func deliver(_ deliver: CocoaMQTTDeliver, wantToSend frame: Frame)
|
||||
}
|
||||
|
||||
private struct InflightFrame {
|
||||
|
||||
/// The infligth frame maybe a `FramePublish` or `FramePubRel`
|
||||
var frame: Frame
|
||||
|
||||
/// Monotonic time (Dispatch uptime) at which this frame should be retried next.
|
||||
var nextRetryAtUptimeNs: UInt64
|
||||
|
||||
}
|
||||
|
||||
extension Array where Element == InflightFrame {
|
||||
|
||||
func filterMap(isIncluded: (Element) -> (Bool, Element)) -> [Element] {
|
||||
var tmp = [Element]()
|
||||
for e in self {
|
||||
let res = isIncluded(e)
|
||||
if res.0 {
|
||||
tmp.append(res.1)
|
||||
}
|
||||
}
|
||||
return tmp
|
||||
}
|
||||
}
|
||||
|
||||
// CocoaMQTTDeliver
|
||||
class CocoaMQTTDeliver: NSObject {
|
||||
|
||||
/// The dispatch queue is used by delivering frames in serially
|
||||
private var deliverQueue = DispatchQueue.init(label: "deliver.cocoamqtt.emqx", qos: .default)
|
||||
|
||||
weak var delegate: CocoaMQTTDeliverProtocol?
|
||||
|
||||
fileprivate var inflight = [InflightFrame]()
|
||||
|
||||
fileprivate var mqueue = [Frame]()
|
||||
|
||||
var mqueueSize: UInt = 1000
|
||||
|
||||
var inflightWindowSize: UInt = 10
|
||||
|
||||
/// Retry time interval millisecond
|
||||
var retryTimeInterval: Double = 5000
|
||||
|
||||
private var awaitingTimer: CocoaMQTTTimer?
|
||||
|
||||
var isQueueEmpty: Bool { mqueue.isEmpty }
|
||||
var isQueueFull: Bool { mqueue.count >= mqueueSize }
|
||||
var isInflightFull: Bool { inflight.count >= inflightWindowSize }
|
||||
var isInflightEmpty: Bool { inflight.isEmpty }
|
||||
|
||||
var storage: CocoaMQTTStorage?
|
||||
|
||||
func recoverSessionBy(_ storage: CocoaMQTTStorage) {
|
||||
let frames = storage.readAll()
|
||||
// Sync to push the frame to mqueue for avoiding overcommit
|
||||
deliverQueue.sync {
|
||||
self.storage = storage
|
||||
for f in frames {
|
||||
mqueue.append(f)
|
||||
}
|
||||
if !frames.isEmpty {
|
||||
printInfo("Deliver recover \(frames.count) msgs")
|
||||
printDebug("Recover message \(frames)")
|
||||
}
|
||||
}
|
||||
|
||||
guard !frames.isEmpty else {
|
||||
return
|
||||
}
|
||||
|
||||
deliverQueue.async { [weak self] in
|
||||
guard let self = self else { return }
|
||||
self.tryTransport()
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a FramePublish to the message queue to wait for sending
|
||||
///
|
||||
/// return false means the frame is rejected because of the buffer is full
|
||||
func add(_ frame: FramePublish) -> Bool {
|
||||
guard !isQueueFull else {
|
||||
printError("Sending buffer is full, frame \(frame) has been rejected to add.")
|
||||
return false
|
||||
}
|
||||
|
||||
// Sync to push the frame to mqueue for avoiding overcommit
|
||||
deliverQueue.sync {
|
||||
mqueue.append(frame)
|
||||
_ = storage?.write(frame)
|
||||
}
|
||||
|
||||
deliverQueue.async { [weak self] in
|
||||
guard let self = self else { return }
|
||||
self.tryTransport()
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/// Acknowledge a PUBLISH/PUBREL by msgid
|
||||
func ack(by frame: Frame) {
|
||||
let msgid: UInt16
|
||||
if let puback = frame as? FramePubAck {
|
||||
msgid = puback.msgid
|
||||
} else if let pubrec = frame as? FramePubRec {
|
||||
msgid = pubrec.msgid
|
||||
} else if let pubcom = frame as? FramePubComp {
|
||||
msgid = pubcom.msgid
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
let ackType = frame.type
|
||||
let shouldRemoveFromStorage = frame is FramePubAck || frame is FramePubComp
|
||||
let ackFrameDescription = String(describing: frame)
|
||||
|
||||
deliverQueue.async { [weak self] in
|
||||
guard let self = self else { return }
|
||||
let acked = self.ackInflightFrame(withMsgid: msgid, type: ackType)
|
||||
if acked.count == 0 {
|
||||
printWarning("Acknowledge by \(ackFrameDescription), but not found in inflight window")
|
||||
} else {
|
||||
// TODO: ACK DONT DELETE PUBREL
|
||||
for f in acked where shouldRemoveFromStorage {
|
||||
self.storage?.remove(f)
|
||||
}
|
||||
printDebug("Acknowledge frame id \(msgid) success, acked: \(acked)")
|
||||
self.tryTransport()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Clean Inflight content to prevent message blocked, when next connection established
|
||||
///
|
||||
/// !!Warning: it's a temporary method for hotfix #221
|
||||
func cleanAll() {
|
||||
deliverQueue.sync { [weak self] in
|
||||
guard let self = self else { return }
|
||||
self.mqueue.removeAll()
|
||||
self.inflight.removeAll()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Private Funcs
|
||||
extension CocoaMQTTDeliver {
|
||||
|
||||
// try transport a frame from mqueue to inflight
|
||||
private func tryTransport() {
|
||||
if isQueueEmpty || isInflightFull { return }
|
||||
|
||||
// take out the earliest frame
|
||||
if mqueue.isEmpty { return }
|
||||
let frame = mqueue.remove(at: 0)
|
||||
|
||||
deliver(frame)
|
||||
|
||||
// keep trying after a transport
|
||||
self.tryTransport()
|
||||
}
|
||||
|
||||
/// Try to deliver a frame
|
||||
private func deliver(_ frame: Frame) {
|
||||
if frame.qos == .qos0 {
|
||||
// Send Qos0 message, whatever the in-flight queue is full
|
||||
// TODO: A retrict deliver mode is need?
|
||||
sendfun(frame)
|
||||
} else {
|
||||
|
||||
sendfun(frame)
|
||||
let nowUptimeNs = DispatchTime.now().uptimeNanoseconds
|
||||
inflight.append(InflightFrame(frame: frame, nextRetryAtUptimeNs: nextRetryDeadline(from: nowUptimeNs)))
|
||||
|
||||
// Start a retry timer for resending it if it not receive PUBACK or PUBREC
|
||||
if awaitingTimer == nil {
|
||||
awaitingTimer = CocoaMQTTTimer.every(retryTimeInterval / 1000.0, name: "awaitingTimer") { [weak self] in
|
||||
guard let self = self else { return }
|
||||
self.deliverQueue.async {
|
||||
self.redeliver()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempt to redeliver in-flight messages
|
||||
private func redeliver(nowUptimeNs: UInt64 = DispatchTime.now().uptimeNanoseconds) {
|
||||
if isInflightEmpty {
|
||||
// Revoke the awaiting timer
|
||||
awaitingTimer = nil
|
||||
return
|
||||
}
|
||||
for (idx, frame) in inflight.enumerated() where nowUptimeNs >= frame.nextRetryAtUptimeNs {
|
||||
var duplicatedFrame = frame
|
||||
duplicatedFrame.frame.dup = true
|
||||
duplicatedFrame.nextRetryAtUptimeNs = nextRetryDeadline(after: frame.nextRetryAtUptimeNs, nowUptimeNs: nowUptimeNs)
|
||||
|
||||
inflight[idx] = duplicatedFrame
|
||||
|
||||
printInfo("Re-delivery frame \(duplicatedFrame.frame)")
|
||||
sendfun(duplicatedFrame.frame)
|
||||
}
|
||||
}
|
||||
|
||||
private func retryIntervalNanoseconds() -> UInt64 {
|
||||
let intervalNs = retryTimeInterval * 1_000_000
|
||||
guard intervalNs.isFinite, intervalNs > 0 else {
|
||||
return 1
|
||||
}
|
||||
if intervalNs >= Double(UInt64.max) {
|
||||
return UInt64.max
|
||||
}
|
||||
return UInt64(intervalNs.rounded())
|
||||
}
|
||||
|
||||
private func nextRetryDeadline(from nowUptimeNs: UInt64) -> UInt64 {
|
||||
let (nextDeadline, overflow) = nowUptimeNs.addingReportingOverflow(retryIntervalNanoseconds())
|
||||
return overflow ? UInt64.max : nextDeadline
|
||||
}
|
||||
|
||||
private func nextRetryDeadline(after currentDeadline: UInt64, nowUptimeNs: UInt64) -> UInt64 {
|
||||
let intervalNs = retryIntervalNanoseconds()
|
||||
guard nowUptimeNs >= currentDeadline else {
|
||||
return currentDeadline
|
||||
}
|
||||
|
||||
let missedIntervals = ((nowUptimeNs - currentDeadline) / intervalNs) + 1
|
||||
let (advance, multiplyOverflow) = intervalNs.multipliedReportingOverflow(by: missedIntervals)
|
||||
if multiplyOverflow {
|
||||
return UInt64.max
|
||||
}
|
||||
let (nextDeadline, addOverflow) = currentDeadline.addingReportingOverflow(advance)
|
||||
return addOverflow ? UInt64.max : nextDeadline
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
private func ackInflightFrame(withMsgid msgid: UInt16, type: FrameType) -> [Frame] {
|
||||
var ackedFrames = [Frame]()
|
||||
inflight = inflight.filterMap { frame in
|
||||
|
||||
// -- ACK for PUBLISH
|
||||
if let publish = frame.frame as? FramePublish,
|
||||
publish.msgid == msgid {
|
||||
|
||||
if publish.qos == .qos2 && type == .pubrec { // -- Replace PUBLISH with PUBREL
|
||||
let pubrel = FramePubRel(msgid: publish.msgid)
|
||||
|
||||
var nframe = frame
|
||||
nframe.frame = pubrel
|
||||
nframe.nextRetryAtUptimeNs = nextRetryDeadline(from: DispatchTime.now().uptimeNanoseconds)
|
||||
|
||||
_ = storage?.write(pubrel)
|
||||
sendfun(pubrel)
|
||||
|
||||
ackedFrames.append(publish)
|
||||
return (true, nframe)
|
||||
} else if publish.qos == .qos1 && type == .puback {
|
||||
ackedFrames.append(publish)
|
||||
return (false, frame)
|
||||
}
|
||||
}
|
||||
|
||||
// -- ACK for PUBREL
|
||||
if let pubrel = frame.frame as? FramePubRel,
|
||||
pubrel.msgid == msgid && type == .pubcomp {
|
||||
|
||||
ackedFrames.append(pubrel)
|
||||
return (false, frame)
|
||||
}
|
||||
return (true, frame)
|
||||
}
|
||||
|
||||
return ackedFrames
|
||||
}
|
||||
|
||||
private func sendfun(_ frame: Frame) {
|
||||
guard let delegate = self.delegate else {
|
||||
printError("The deliver delegate is nil!!! the frame will be drop: \(frame)")
|
||||
return
|
||||
}
|
||||
|
||||
if frame.qos == .qos0 {
|
||||
if let p = frame as? FramePublish { storage?.remove(p) }
|
||||
}
|
||||
|
||||
delegate.delegateQueue.async {
|
||||
delegate.deliver(self, wantToSend: frame)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For tests
|
||||
extension CocoaMQTTDeliver {
|
||||
|
||||
func t_inflightFrames() -> [Frame] {
|
||||
var frames = [Frame]()
|
||||
for f in inflight {
|
||||
frames.append(f.frame)
|
||||
}
|
||||
return frames
|
||||
}
|
||||
|
||||
func t_queuedFrames() -> [Frame] {
|
||||
return mqueue
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
func t_setInflightNextRetryTime(_ nextRetryAtUptimeNs: UInt64, forMsgid msgid: UInt16) -> Bool {
|
||||
return deliverQueue.sync {
|
||||
for idx in inflight.indices {
|
||||
if let publish = inflight[idx].frame as? FramePublish, publish.msgid == msgid {
|
||||
inflight[idx].nextRetryAtUptimeNs = nextRetryAtUptimeNs
|
||||
return true
|
||||
}
|
||||
if let pubrel = inflight[idx].frame as? FramePubRel, pubrel.msgid == msgid {
|
||||
inflight[idx].nextRetryAtUptimeNs = nextRetryAtUptimeNs
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func t_retryIntervalNanoseconds() -> UInt64 {
|
||||
return deliverQueue.sync {
|
||||
retryIntervalNanoseconds()
|
||||
}
|
||||
}
|
||||
|
||||
func t_redeliver(atUptimeNanoseconds uptimeNs: UInt64) {
|
||||
deliverQueue.sync {
|
||||
self.redeliver(nowUptimeNs: uptimeNs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
//
|
||||
// CocoaMQTTLogger.swift
|
||||
// CocoaMQTT
|
||||
//
|
||||
// Created by HJianBo on 2019/5/2.
|
||||
// Copyright © 2019 emqx.io. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
// Convenience functions
|
||||
func printDebug(_ message: String) {
|
||||
CocoaMQTTLogger.logger.debug(message)
|
||||
}
|
||||
|
||||
func printInfo(_ message: String) {
|
||||
CocoaMQTTLogger.logger.info(message)
|
||||
}
|
||||
|
||||
func printWarning(_ message: String) {
|
||||
CocoaMQTTLogger.logger.warning(message)
|
||||
}
|
||||
|
||||
func printError(_ message: String) {
|
||||
CocoaMQTTLogger.logger.error(message)
|
||||
}
|
||||
|
||||
// Enum log levels
|
||||
public enum CocoaMQTTLoggerLevel: Int {
|
||||
case debug = 0, info, warning, error, off
|
||||
}
|
||||
|
||||
open class CocoaMQTTLogger: NSObject {
|
||||
|
||||
// Singleton
|
||||
// Keep mutable global logger for backward compatibility.
|
||||
// Accesses are intentionally marked as unsafe-isolated for Swift 6 readiness.
|
||||
#if swift(>=6)
|
||||
public nonisolated(unsafe) static var logger = CocoaMQTTLogger()
|
||||
#else
|
||||
public static var logger = CocoaMQTTLogger()
|
||||
#endif
|
||||
public override init() { super.init() }
|
||||
|
||||
// min level
|
||||
@ConcurrentAtomic(wrappedValue: .warning, label: "CocoaMQTTLogger.minLevel")
|
||||
public var minLevel: CocoaMQTTLoggerLevel
|
||||
|
||||
// logs
|
||||
open func log(level: CocoaMQTTLoggerLevel, message: String) {
|
||||
guard level.rawValue >= minLevel.rawValue else { return }
|
||||
print("CocoaMQTT(\(level)): \(message)")
|
||||
}
|
||||
|
||||
func debug(_ message: String) {
|
||||
log(level: .debug, message: message)
|
||||
}
|
||||
|
||||
func info(_ message: String) {
|
||||
log(level: .info, message: message)
|
||||
}
|
||||
|
||||
func warning(_ message: String) {
|
||||
log(level: .warning, message: message)
|
||||
}
|
||||
|
||||
func error(_ message: String) {
|
||||
log(level: .error, message: message)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
//
|
||||
// CocoaMQTTMessage.swift
|
||||
// CocoaMQTT
|
||||
//
|
||||
// Created by Feng Lee<feng@eqmtt.io> on 14/8/3.
|
||||
// Copyright (c) 2015 emqx.io. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// MQTT Message
|
||||
public class CocoaMQTTMessage: NSObject {
|
||||
|
||||
public var qos = CocoaMQTTQoS.qos1
|
||||
|
||||
public var topic: String
|
||||
|
||||
public var payload: [UInt8]
|
||||
|
||||
public var retained = false
|
||||
|
||||
/// The `duplicated` property show that this message maybe has be received before
|
||||
///
|
||||
/// - note: Readonly property
|
||||
public var duplicated = false
|
||||
|
||||
/// Return the payload as a utf8 string if possible
|
||||
///
|
||||
/// It will return nil if the payload is not a valid utf8 string
|
||||
public var string: String? {
|
||||
NSString(bytes: payload, length: payload.count, encoding: String.Encoding.utf8.rawValue) as String?
|
||||
}
|
||||
|
||||
public init(topic: String, string: String, qos: CocoaMQTTQoS = .qos1, retained: Bool = false) {
|
||||
self.topic = topic
|
||||
self.payload = [UInt8](string.utf8)
|
||||
self.qos = qos
|
||||
self.retained = retained
|
||||
}
|
||||
|
||||
public init(topic: String, payload: [UInt8], qos: CocoaMQTTQoS = .qos1, retained: Bool = false) {
|
||||
self.topic = topic
|
||||
self.payload = payload
|
||||
self.qos = qos
|
||||
self.retained = retained
|
||||
}
|
||||
}
|
||||
|
||||
extension CocoaMQTTMessage {
|
||||
|
||||
public override var description: String {
|
||||
return "CocoaMQTTMessage(topic: \(topic), qos: \(qos), payload: \(payload.summary))"
|
||||
}
|
||||
}
|
||||
|
||||
// For test
|
||||
extension CocoaMQTTMessage {
|
||||
|
||||
var t_pub_frame: FramePublish {
|
||||
var frame = FramePublish(topic: topic, payload: payload, qos: qos, msgid: 0)
|
||||
frame.retained = retained
|
||||
frame.dup = duplicated
|
||||
return frame
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,189 @@
|
|||
//
|
||||
// CocoaMQTTPropertyType.swift
|
||||
// CocoaMQTT
|
||||
//
|
||||
// Created by liwei wang on 2021/7/6.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public enum CocoaMQTTPropertyName: UInt8 {
|
||||
case payloadFormatIndicator = 0x01
|
||||
case willExpiryInterval = 0x02
|
||||
case contentType = 0x03
|
||||
case responseTopic = 0x08
|
||||
case correlationData = 0x09
|
||||
case subscriptionIdentifier = 0x0B
|
||||
case sessionExpiryInterval = 0x11
|
||||
case assignedClientIdentifier = 0x12
|
||||
case serverKeepAlive = 0x13
|
||||
case authenticationMethod = 0x15
|
||||
case authenticationData = 0x16
|
||||
case requestProblemInformation = 0x17
|
||||
case willDelayInterval = 0x18
|
||||
case requestResponseInformation = 0x19
|
||||
case responseInformation = 0x1A
|
||||
case serverReference = 0x1C
|
||||
case reasonString = 0x1F
|
||||
case receiveMaximum = 0x21
|
||||
case topicAliasMaximum = 0x22
|
||||
case topicAlias = 0x23
|
||||
case maximumQoS = 0x24
|
||||
case retainAvailable = 0x25
|
||||
case userProperty = 0x26
|
||||
case maximumPacketSize = 0x27
|
||||
case wildcardSubscriptionAvailable = 0x28
|
||||
case subscriptionIdentifiersAvailable = 0x29
|
||||
case sharedSubscriptionAvailable = 0x2A
|
||||
}
|
||||
|
||||
public enum formatInt: Int {
|
||||
case formatUint8 = 0x11
|
||||
case formatUint16 = 0x12
|
||||
case formatUint32 = 0x14
|
||||
case formatSint8 = 0x21
|
||||
case formatSint16 = 0x22
|
||||
case formatSint32 = 0x24
|
||||
}
|
||||
|
||||
func getMQTTPropertyData(type: UInt8, value: [UInt8]) -> [UInt8] {
|
||||
var properties = [UInt8]()
|
||||
properties.append(UInt8(type))
|
||||
properties += value
|
||||
return properties
|
||||
}
|
||||
|
||||
func getMQTTPropertyLength(type: UInt8, value: [UInt8]) -> [UInt8] {
|
||||
var properties = [UInt8]()
|
||||
properties.append(UInt8(type))
|
||||
properties += value
|
||||
return properties
|
||||
}
|
||||
|
||||
func integerCompute(data: [UInt8], formatType: Int, offset: Int) -> (res: Int, newOffset: Int)? {
|
||||
|
||||
switch formatType {
|
||||
case formatInt.formatUint8.rawValue:
|
||||
return (unsignedByteToInt(data: data[offset]), offset + 1)
|
||||
case formatInt.formatUint16.rawValue:
|
||||
return (unsignedBytesToInt(data0: data[offset], data1: data[offset + 1]), offset + 2)
|
||||
case formatInt.formatUint32.rawValue:
|
||||
return (unsignedBytesToInt(data0: data[offset], data1: data[offset + 1], data2: data[offset + 2], data3: data[offset + 3]), offset + 4)
|
||||
case formatInt.formatSint8.rawValue:
|
||||
return (unsignedToSigned(unsign: unsignedByteToInt(data: data[offset]), size: 8), offset + 1)
|
||||
case formatInt.formatSint16.rawValue:
|
||||
return (unsignedToSigned(unsign: unsignedBytesToInt(data0: data[offset], data1: data[offset + 1]), size: 16), offset + 2)
|
||||
case formatInt.formatSint32.rawValue:
|
||||
return (unsignedToSigned(unsign: unsignedBytesToInt(data0: data[offset], data1: data[offset + 1], data2: data[offset + 2], data3: data[offset + 3]), size: 32), offset + 4)
|
||||
default:
|
||||
printDebug("integerCompute nothing")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func unsignedByteToInt(data: UInt8) -> (Int) {
|
||||
return (Int)(data & 0xFF)
|
||||
}
|
||||
|
||||
func unsignedBytesToInt(data0: UInt8, data1: UInt8) -> (Int) {
|
||||
return (unsignedByteToInt(data: data0) << 8) + unsignedByteToInt(data: data1)
|
||||
}
|
||||
|
||||
func unsignedBytesToInt(data0: UInt8, data1: UInt8, data2: UInt8, data3: UInt8) -> (Int) {
|
||||
return unsignedByteToInt(data: data3) + (unsignedByteToInt(data: data2) << 8) + (unsignedByteToInt(data: data1) << 16) + (unsignedByteToInt(data: data0) << 24)
|
||||
}
|
||||
|
||||
func unsignedToSigned(unsign: NSInteger, size: NSInteger) -> (Int) {
|
||||
var res = unsign
|
||||
if (res & (1 << size-1)) != 0 {
|
||||
res = -1 * ((1 << size-1) - (res & ((1 << size-1) - 1)))
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func unsignedByteToString(data: [UInt8], offset: Int) -> (resStr: String, newOffset: Int)? {
|
||||
var newOffset = offset
|
||||
|
||||
if offset + 1 > data.count {
|
||||
return nil
|
||||
}
|
||||
|
||||
var length = 0
|
||||
let comRes = integerCompute(data: data, formatType: formatInt.formatUint16.rawValue, offset: newOffset)
|
||||
length = comRes!.res
|
||||
newOffset = comRes!.newOffset
|
||||
|
||||
var stringData = Data()
|
||||
for _ in 0 ..< length {
|
||||
stringData.append(data[newOffset])
|
||||
newOffset += 1
|
||||
}
|
||||
guard let res = String(data: stringData, encoding: .utf8) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return (res, newOffset)
|
||||
}
|
||||
|
||||
func unsignedByteToBinary(data: [UInt8], offset: Int) -> (resStr: [UInt8], newOffset: Int)? {
|
||||
var newOffset = offset
|
||||
|
||||
if offset + 1 > data.count {
|
||||
return nil
|
||||
}
|
||||
|
||||
var length = 0
|
||||
let comRes = integerCompute(data: data, formatType: formatInt.formatUint16.rawValue, offset: newOffset)
|
||||
length = comRes!.res
|
||||
newOffset = comRes!.newOffset
|
||||
|
||||
var res = [UInt8]()
|
||||
for _ in 0 ..< length {
|
||||
res.append(data[newOffset])
|
||||
newOffset += 1
|
||||
}
|
||||
|
||||
return (res, newOffset)
|
||||
}
|
||||
|
||||
// 1.5.5 Variable Byte Integer
|
||||
// The Variable Byte Integer is encoded using an encoding scheme which uses a single byte for values up to 127. Larger values are handled as follows. The least significant seven bits of each byte encode the data, and the most significant bit is used to indicate whether there are bytes following in the representation. Thus, each byte encodes 128 values and a "continuation bit". The maximum number of bytes in the Variable Byte Integer field is four. The encoded value MUST use the minimum number of bytes necessary to represent the value [MQTT-1.5.5-1]. This is shown in Table 1‑1 Size of Variable Byte Integer.
|
||||
func decodeVariableByteInteger(data: [UInt8], offset: Int) -> (res: Int, newOffset: Int) {
|
||||
var newOffset = offset
|
||||
var count = 0
|
||||
var res: Int = 0
|
||||
while newOffset < data.count {
|
||||
let newValue = Int(data[newOffset] & 0x7f) << count
|
||||
res += newValue
|
||||
if (data[newOffset] & 0x80) == 0 || count >= 21 {
|
||||
newOffset += 1
|
||||
break
|
||||
}
|
||||
newOffset += 1
|
||||
count += 7
|
||||
}
|
||||
return (res, newOffset)
|
||||
}
|
||||
|
||||
func beVariableByteInteger(length: Int) -> [UInt8] {
|
||||
var res = [UInt8]()
|
||||
var tmpLen: Int = length
|
||||
repeat {
|
||||
var d: UInt8 = UInt8(tmpLen % 128)
|
||||
tmpLen /= 128
|
||||
if tmpLen > 0 {
|
||||
d |= 0x80
|
||||
}
|
||||
res.append(d)
|
||||
} while(tmpLen > 0)
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func beVariableByteInteger(_ data: UInt32) -> [UInt8]? {
|
||||
if data > 0x0fffffff {
|
||||
return nil
|
||||
}
|
||||
return beVariableByteInteger(length: Int(data))
|
||||
}
|
||||
|
|
@ -0,0 +1,220 @@
|
|||
//
|
||||
// CocoaMQTTReader.swift
|
||||
// CocoaMQTT
|
||||
//
|
||||
// Created by HJianBo on 2019/5/21.
|
||||
// Copyright © 2019 emqx.io. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Read tag for AsyncSocket
|
||||
enum CocoaMQTTReadTag: Int {
|
||||
case header = 0
|
||||
case length
|
||||
case payload
|
||||
}
|
||||
|
||||
///
|
||||
protocol CocoaMQTTReaderDelegate: AnyObject {
|
||||
|
||||
func didReceive(_ reader: CocoaMQTTReader, connack: FrameConnAck)
|
||||
|
||||
func didReceive(_ reader: CocoaMQTTReader, publish: FramePublish)
|
||||
|
||||
func didReceive(_ reader: CocoaMQTTReader, puback: FramePubAck)
|
||||
|
||||
func didReceive(_ reader: CocoaMQTTReader, pubrec: FramePubRec)
|
||||
|
||||
func didReceive(_ reader: CocoaMQTTReader, pubrel: FramePubRel)
|
||||
|
||||
func didReceive(_ reader: CocoaMQTTReader, pubcomp: FramePubComp)
|
||||
|
||||
func didReceive(_ reader: CocoaMQTTReader, suback: FrameSubAck)
|
||||
|
||||
func didReceive(_ reader: CocoaMQTTReader, unsuback: FrameUnsubAck)
|
||||
|
||||
func didReceive(_ reader: CocoaMQTTReader, pingresp: FramePingResp)
|
||||
|
||||
func didReceive(_ reader: CocoaMQTTReader, disconnect: FrameDisconnect)
|
||||
|
||||
func didReceive(_ reader: CocoaMQTTReader, auth: FrameAuth)
|
||||
}
|
||||
|
||||
class CocoaMQTTReader {
|
||||
|
||||
private var socket: CocoaMQTTSocketProtocol
|
||||
|
||||
private weak var delegate: CocoaMQTTReaderDelegate?
|
||||
|
||||
private let timeout: TimeInterval = 30_000
|
||||
|
||||
/* -- Reader states -- */
|
||||
private var header: UInt8 = 0
|
||||
private var length: UInt = 0
|
||||
private var data: [UInt8] = []
|
||||
private var multiply = 1
|
||||
/* -- Reader states -- */
|
||||
|
||||
init(socket: CocoaMQTTSocketProtocol, delegate: CocoaMQTTReaderDelegate?) {
|
||||
self.socket = socket
|
||||
self.delegate = delegate
|
||||
}
|
||||
|
||||
func start() {
|
||||
readHeader()
|
||||
}
|
||||
|
||||
func headerReady(_ header: UInt8) {
|
||||
self.header = header
|
||||
readLength()
|
||||
}
|
||||
|
||||
func lengthReady(_ byte: UInt8) {
|
||||
length += (UInt)((Int)(byte & 127) * multiply)
|
||||
// done
|
||||
if byte & 0x80 == 0 {
|
||||
if length == 0 {
|
||||
frameReady()
|
||||
} else {
|
||||
readPayload()
|
||||
}
|
||||
// more
|
||||
} else {
|
||||
let result = multiply.multipliedReportingOverflow(by: 128)
|
||||
if !result.overflow {
|
||||
multiply = result.partialValue
|
||||
} else {
|
||||
reset()
|
||||
}
|
||||
readLength()
|
||||
}
|
||||
}
|
||||
|
||||
func payloadReady(_ data: Data) {
|
||||
self.data = [UInt8](repeating: 0, count: data.count)
|
||||
data.copyBytes(to: &(self.data), count: data.count)
|
||||
frameReady()
|
||||
}
|
||||
|
||||
private func readHeader() {
|
||||
reset()
|
||||
socket.readData(toLength: 1, withTimeout: -1, tag: CocoaMQTTReadTag.header.rawValue)
|
||||
}
|
||||
|
||||
private func readLength() {
|
||||
socket.readData(toLength: 1, withTimeout: timeout, tag: CocoaMQTTReadTag.length.rawValue)
|
||||
}
|
||||
|
||||
private func readPayload() {
|
||||
socket.readData(toLength: length, withTimeout: timeout, tag: CocoaMQTTReadTag.payload.rawValue)
|
||||
}
|
||||
|
||||
private func frameReady() {
|
||||
|
||||
guard let frameType = FrameType(rawValue: UInt8(header & 0xF0)) else {
|
||||
protocolError("Received unknown frame type, header: \(header), data:\(data)")
|
||||
return
|
||||
}
|
||||
|
||||
// XXX: stupid implement
|
||||
|
||||
switch frameType {
|
||||
case .connack:
|
||||
guard let connack = FrameConnAck(packetFixedHeaderType: header, bytes: data) else {
|
||||
protocolError("Reader parse \(frameType) failed, data: \(data)")
|
||||
return
|
||||
}
|
||||
delegate?.didReceive(self, connack: connack)
|
||||
case .publish:
|
||||
guard let publish = FramePublish(packetFixedHeaderType: header, bytes: data) else {
|
||||
protocolError("Reader parse \(frameType) failed, data: \(data)")
|
||||
return
|
||||
}
|
||||
delegate?.didReceive(self, publish: publish)
|
||||
case .puback:
|
||||
guard let puback = FramePubAck(packetFixedHeaderType: header, bytes: data) else {
|
||||
protocolError("Reader parse \(frameType) failed, data: \(data)")
|
||||
return
|
||||
}
|
||||
delegate?.didReceive(self, puback: puback)
|
||||
case .pubrec:
|
||||
guard let pubrec = FramePubRec(packetFixedHeaderType: header, bytes: data) else {
|
||||
protocolError("Reader parse \(frameType) failed, data: \(data)")
|
||||
return
|
||||
}
|
||||
delegate?.didReceive(self, pubrec: pubrec)
|
||||
case .pubrel:
|
||||
guard let pubrel = FramePubRel(packetFixedHeaderType: header, bytes: data) else {
|
||||
protocolError("Reader parse \(frameType) failed, data: \(data)")
|
||||
return
|
||||
}
|
||||
delegate?.didReceive(self, pubrel: pubrel)
|
||||
case .pubcomp:
|
||||
guard let pubcomp = FramePubComp(packetFixedHeaderType: header, bytes: data) else {
|
||||
protocolError("Reader parse \(frameType) failed, data: \(data)")
|
||||
return
|
||||
}
|
||||
delegate?.didReceive(self, pubcomp: pubcomp)
|
||||
case .suback:
|
||||
guard let frame = FrameSubAck(packetFixedHeaderType: header, bytes: data) else {
|
||||
protocolError("Reader parse \(frameType) failed, data: \(data)")
|
||||
return
|
||||
}
|
||||
delegate?.didReceive(self, suback: frame)
|
||||
case .unsuback:
|
||||
guard let frame = FrameUnsubAck(packetFixedHeaderType: header, bytes: data) else {
|
||||
protocolError("Reader parse \(frameType) failed, data: \(data)")
|
||||
return
|
||||
}
|
||||
delegate?.didReceive(self, unsuback: frame)
|
||||
case .pingresp:
|
||||
guard let frame = FramePingResp(packetFixedHeaderType: header, bytes: data) else {
|
||||
protocolError("Reader parse \(frameType) failed, data: \(data)")
|
||||
return
|
||||
}
|
||||
delegate?.didReceive(self, pingresp: frame)
|
||||
case .disconnect:
|
||||
guard isMQTT5ProtocolVersion() else {
|
||||
protocolError("Reader received MQTT5-only frame \(frameType) in non-MQTT5 mode, data: \(data)")
|
||||
return
|
||||
}
|
||||
guard let frame = FrameDisconnect(packetFixedHeaderType: header, bytes: data) else {
|
||||
protocolError("Reader parse \(frameType) failed, data: \(data)")
|
||||
return
|
||||
}
|
||||
delegate?.didReceive(self, disconnect: frame)
|
||||
case .auth:
|
||||
guard isMQTT5ProtocolVersion() else {
|
||||
protocolError("Reader received MQTT5-only frame \(frameType) in non-MQTT5 mode, data: \(data)")
|
||||
return
|
||||
}
|
||||
guard let frame = FrameAuth(packetFixedHeaderType: header, bytes: data) else {
|
||||
protocolError("Reader parse \(frameType) failed, data: \(data)")
|
||||
return
|
||||
}
|
||||
delegate?.didReceive(self, auth: frame)
|
||||
default:
|
||||
protocolError("Received unsupported frame type \(frameType), data: \(data)")
|
||||
return
|
||||
}
|
||||
|
||||
readHeader()
|
||||
}
|
||||
|
||||
private func protocolError(_ reason: String) {
|
||||
printError(reason)
|
||||
socket.disconnect()
|
||||
}
|
||||
|
||||
private func isMQTT5ProtocolVersion() -> Bool {
|
||||
return CocoaMQTTStorage()?.queryMQTTVersion() == "5.0"
|
||||
}
|
||||
|
||||
private func reset() {
|
||||
length = 0
|
||||
multiply = 1
|
||||
header = 0
|
||||
data = []
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,141 @@
|
|||
//
|
||||
// CocoaMQTTReasonCode.swift
|
||||
// CocoaMQTT
|
||||
//
|
||||
// Created by liwei wang on 2021/7/4.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@objc public enum CocoaMQTTAUTHReasonCode: UInt8 {
|
||||
case success = 0x00
|
||||
case continueAuthentication = 0x18
|
||||
case ReAuthenticate = 0x19
|
||||
}
|
||||
|
||||
@objc public enum CocoaMQTTCONNACKReasonCode: UInt8 {
|
||||
case success = 0x00
|
||||
case unspecifiedError = 0x80
|
||||
case malformedPacket = 0x81
|
||||
case protocolError = 0x82
|
||||
case implementationSpecificError = 0x83
|
||||
case unsupportedProtocolVersion = 0x84
|
||||
case clientIdentifierNotValid = 0x85
|
||||
case badUsernameOrPassword = 0x86
|
||||
case notAuthorized = 0x87
|
||||
case serverUnavailable = 0x88
|
||||
case serverBusy = 0x89
|
||||
case banned = 0x8A
|
||||
case badAuthenticationMethod = 0x8C
|
||||
case topicNameInvalid = 0x90
|
||||
case packetTooLarge = 0x95
|
||||
case quotaExceeded = 0x97
|
||||
case payloadFormatInvalid = 0x99
|
||||
case retainNotSupported = 0x9A
|
||||
case qosNotSupported = 0x9B
|
||||
case useAnotherServer = 0x9C
|
||||
case serverMoved = 0x9D
|
||||
case connectionRateExceeded = 0x9F
|
||||
}
|
||||
|
||||
@objc public enum CocoaMQTTDISCONNECTReasonCode: UInt8 {
|
||||
case normalDisconnection = 0x00
|
||||
case disconnectWithWillMessage = 0x04
|
||||
case unspecifiedError = 0x80
|
||||
case malformedPacket = 0x81
|
||||
case protocolError = 0x82
|
||||
case implementationSpecificError = 0x83
|
||||
case notAuthorized = 0x87
|
||||
case serverBusy = 0x89
|
||||
case serverShuttingDown = 0x8B
|
||||
case keepAliveTimeout = 0x8D
|
||||
case sessionTakenOver = 0x8E
|
||||
case topicFilterInvalid = 0x8F
|
||||
case topicNameInvalid = 0x90
|
||||
case receiveMaximumExceeded = 0x93
|
||||
case topicAliasInvalid = 0x94
|
||||
case packetTooLarge = 0x95
|
||||
case messageRateTooHigh = 0x96
|
||||
case quotaExceeded = 0x97
|
||||
case administrativeAction = 0x98
|
||||
case payloadFormatInvalid = 0x99
|
||||
case retainNotSupported = 0x9A
|
||||
case qosNotSupported = 0x9B
|
||||
case useAnotherServer = 0x9C
|
||||
case serverMoved = 0x9D
|
||||
case sharedSubscriptionsNotSupported = 0x9E
|
||||
case connectionRateExceeded = 0x9F
|
||||
case maximumConnectTime = 0xA0
|
||||
case subscriptionIdentifiersNotSupported = 0xA1
|
||||
case wildcardSubscriptionsNotSupported = 0xA2
|
||||
}
|
||||
|
||||
@objc public enum CocoaMQTTPUBACKReasonCode: UInt8 {
|
||||
case success = 0x00
|
||||
case noMatchingSubscribers = 0x10
|
||||
case unspecifiedError = 0x80
|
||||
case implementationSpecificError = 0x83
|
||||
case notAuthorized = 0x87
|
||||
case topicNameInvalid = 0x90
|
||||
case packetIdentifierInUse = 0x91
|
||||
case quotaExceeded = 0x97
|
||||
case payloadFormatInvalid = 0x99
|
||||
}
|
||||
|
||||
@objc public enum CocoaMQTTPUBCOMPReasonCode: UInt8 {
|
||||
case success = 0x00
|
||||
case packetIdentifierNotFound = 0x92
|
||||
}
|
||||
|
||||
@objc public enum CocoaMQTTPUBRECReasonCode: UInt8 {
|
||||
case success = 0x00
|
||||
case noMatchingSubscribers = 0x10
|
||||
case unspecifiedError = 0x80
|
||||
case implementationSpecificError = 0x83
|
||||
case notAuthorized = 0x87
|
||||
case topicNameInvalid = 0x90
|
||||
case packetIdentifierInUse = 0x91
|
||||
case quotaExceeded = 0x97
|
||||
case payloadFormatInvalid = 0x99
|
||||
}
|
||||
|
||||
@objc public enum CocoaMQTTPUBRELReasonCode: UInt8 {
|
||||
case success = 0x00
|
||||
case packetIdentifierNotFound = 0x92
|
||||
}
|
||||
|
||||
@objc public enum CocoaMQTTSUBACKReasonCode: UInt8 {
|
||||
case grantedQoS0 = 0x00
|
||||
case grantedQoS1 = 0x01
|
||||
case grantedQoS2 = 0x02
|
||||
case unspecifiedError = 0x80
|
||||
case implementationSpecificError = 0x83
|
||||
case notAuthorized = 0x87
|
||||
case topicFilterInvalid = 0x8F
|
||||
case packetIdentifierInUse = 0x91
|
||||
case quotaExceeded = 0x97
|
||||
case sharedSubscriptionsNotSupported = 0x9E
|
||||
case subscriptionIdentifiersNotSupported = 0xA1
|
||||
case wildcardSubscriptionsNotSupported = 0xA2
|
||||
}
|
||||
|
||||
@objc public enum CocoaMQTTUNSUBACKReasonCode: UInt8 {
|
||||
case success = 0x00
|
||||
case noSubscriptionExisted = 0x11
|
||||
case unspecifiedError = 0x80
|
||||
case implementationSpecificError = 0x83
|
||||
case notAuthorized = 0x87
|
||||
case topicFilterInvalid = 0x8F
|
||||
case packetIdentifierInUse = 0x91
|
||||
}
|
||||
|
||||
@objc public enum CocoaRetainHandlingOption: UInt8 {
|
||||
case sendOnSubscribe = 0
|
||||
case sendOnlyWhenSubscribeIsNew = 1
|
||||
case none = 2
|
||||
}
|
||||
|
||||
@objc public enum PayloadFormatIndicator: UInt8 {
|
||||
case unspecified = 0x00
|
||||
case utf8 = 0x01
|
||||
}
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
//
|
||||
// CocoaMQTTSocket.swift
|
||||
// CocoaMQTT
|
||||
//
|
||||
// Created by Cyrus Ingraham on 12/13/19.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import MqttCocoaAsyncSocket
|
||||
|
||||
// MARK: - Interfaces
|
||||
|
||||
public protocol CocoaMQTTSocketDelegate: AnyObject {
|
||||
func socketConnected(_ socket: CocoaMQTTSocketProtocol)
|
||||
func socket(_ socket: CocoaMQTTSocketProtocol, didReceive trust: SecTrust, completionHandler: @escaping (Bool) -> Swift.Void)
|
||||
func socketUrlSession(_ socket: CocoaMQTTSocketProtocol, didReceiveTrust trust: SecTrust, didReceiveChallenge challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
|
||||
func socket(_ socket: CocoaMQTTSocketProtocol, didWriteDataWithTag tag: Int)
|
||||
func socket(_ socket: CocoaMQTTSocketProtocol, didRead data: Data, withTag tag: Int)
|
||||
func socketDidDisconnect(_ socket: CocoaMQTTSocketProtocol, withError err: Error?)
|
||||
}
|
||||
|
||||
public protocol CocoaMQTTSocketProtocol {
|
||||
|
||||
var enableSSL: Bool { get set }
|
||||
|
||||
func setDelegate(_ theDelegate: CocoaMQTTSocketDelegate?, delegateQueue: DispatchQueue?)
|
||||
func connect(toHost host: String, onPort port: UInt16) throws
|
||||
func connect(toHost host: String, onPort port: UInt16, withTimeout timeout: TimeInterval) throws
|
||||
func disconnect()
|
||||
func readData(toLength length: UInt, withTimeout timeout: TimeInterval, tag: Int)
|
||||
func write(_ data: Data, withTimeout timeout: TimeInterval, tag: Int)
|
||||
}
|
||||
|
||||
// MARK: - CocoaMQTTSocket
|
||||
|
||||
public class CocoaMQTTSocket: NSObject {
|
||||
|
||||
public var backgroundOnSocket = true
|
||||
|
||||
public var enableSSL = false
|
||||
|
||||
///
|
||||
public var sslSettings: [String: NSObject]?
|
||||
|
||||
/// Allow self-signed ca certificate.
|
||||
///
|
||||
/// Default is false
|
||||
public var allowUntrustCACertificate = false
|
||||
|
||||
fileprivate let reference = MGCDAsyncSocket()
|
||||
fileprivate weak var delegate: CocoaMQTTSocketDelegate?
|
||||
|
||||
public override init() { super.init() }
|
||||
}
|
||||
|
||||
extension CocoaMQTTSocket: CocoaMQTTSocketProtocol {
|
||||
public func setDelegate(_ theDelegate: CocoaMQTTSocketDelegate?, delegateQueue: DispatchQueue?) {
|
||||
delegate = theDelegate
|
||||
reference.setDelegate((delegate != nil ? self : nil), delegateQueue: delegateQueue)
|
||||
}
|
||||
|
||||
public func connect(toHost host: String, onPort port: UInt16) throws {
|
||||
try connect(toHost: host, onPort: port, withTimeout: -1)
|
||||
}
|
||||
|
||||
public func connect(toHost host: String, onPort port: UInt16, withTimeout timeout: TimeInterval) throws {
|
||||
try reference.connect(toHost: host, onPort: port, withTimeout: timeout)
|
||||
}
|
||||
|
||||
public func disconnect() {
|
||||
reference.disconnect()
|
||||
}
|
||||
|
||||
public func readData(toLength length: UInt, withTimeout timeout: TimeInterval, tag: Int) {
|
||||
reference.readData(toLength: length, withTimeout: timeout, tag: tag)
|
||||
}
|
||||
|
||||
public func write(_ data: Data, withTimeout timeout: TimeInterval, tag: Int) {
|
||||
reference.write(data, withTimeout: timeout, tag: tag)
|
||||
}
|
||||
}
|
||||
|
||||
extension CocoaMQTTSocket: MGCDAsyncSocketDelegate {
|
||||
public func socket(_ sock: MGCDAsyncSocket, didConnectToHost host: String, port: UInt16) {
|
||||
printInfo("Connected to \(host) : \(port)")
|
||||
|
||||
#if os(iOS)
|
||||
if backgroundOnSocket {
|
||||
sock.perform {
|
||||
guard sock.enableBackgroundingOnSocket() else {
|
||||
printWarning("Enable backgrounding socket failed, please check related permissions")
|
||||
return
|
||||
}
|
||||
printInfo("Enable backgrounding socket successfully")
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if enableSSL {
|
||||
var setting = sslSettings ?? [:]
|
||||
if allowUntrustCACertificate {
|
||||
setting[MGCDAsyncSocketManuallyEvaluateTrust as String] = NSNumber(value: true)
|
||||
}
|
||||
sock.startTLS(setting)
|
||||
} else {
|
||||
delegate?.socketConnected(self)
|
||||
}
|
||||
}
|
||||
|
||||
public func socket(_ sock: MGCDAsyncSocket, didReceive trust: SecTrust, completionHandler: @escaping (Bool) -> Swift.Void) {
|
||||
if let theDelegate = delegate {
|
||||
theDelegate.socket(self, didReceive: trust, completionHandler: completionHandler)
|
||||
} else {
|
||||
completionHandler(false)
|
||||
}
|
||||
}
|
||||
|
||||
public func socketDidSecure(_ sock: MGCDAsyncSocket) {
|
||||
printDebug("socket did secure")
|
||||
delegate?.socketConnected(self)
|
||||
}
|
||||
|
||||
public func socket(_ sock: MGCDAsyncSocket, didWriteDataWithTag tag: Int) {
|
||||
printDebug("socket wrote data \(tag)")
|
||||
delegate?.socket(self, didWriteDataWithTag: tag)
|
||||
}
|
||||
|
||||
public func socket(_ sock: MGCDAsyncSocket, didRead data: Data, withTag tag: Int) {
|
||||
delegate?.socket(self, didRead: data, withTag: tag)
|
||||
}
|
||||
|
||||
public func socketDidDisconnect(_ sock: MGCDAsyncSocket, withError err: Error?) {
|
||||
printDebug("socket disconnected")
|
||||
delegate?.socketDidDisconnect(self, withError: err)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,161 @@
|
|||
//
|
||||
// CocoaMQTTStorage.swift
|
||||
// CocoaMQTT
|
||||
//
|
||||
// Created by JianBo on 2019/10/6.
|
||||
// Copyright © 2019 emqtt.io. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
protocol CocoaMQTTStorageProtocol {
|
||||
|
||||
var clientId: String { get set }
|
||||
|
||||
init?(by clientId: String)
|
||||
|
||||
func write(_ frame: FramePublish) -> Bool
|
||||
|
||||
func write(_ frame: FramePubRel) -> Bool
|
||||
|
||||
func remove(_ frame: FramePublish)
|
||||
|
||||
func remove(_ frame: FramePubRel)
|
||||
|
||||
func synchronize() -> Bool
|
||||
|
||||
/// Read all stored messages by saving order
|
||||
func readAll() -> [Frame]
|
||||
}
|
||||
|
||||
final class CocoaMQTTStorage: CocoaMQTTStorageProtocol {
|
||||
|
||||
var clientId: String = ""
|
||||
|
||||
var userDefault: UserDefaults = UserDefaults()
|
||||
|
||||
var versionDefault: UserDefaults = UserDefaults()
|
||||
|
||||
init?() {
|
||||
versionDefault = UserDefaults()
|
||||
}
|
||||
|
||||
init?(by clientId: String) {
|
||||
guard let userDefault = UserDefaults(suiteName: CocoaMQTTStorage.name(clientId)) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
self.clientId = clientId
|
||||
self.userDefault = userDefault
|
||||
}
|
||||
|
||||
deinit {
|
||||
userDefault.synchronize()
|
||||
versionDefault.synchronize()
|
||||
}
|
||||
|
||||
func setMQTTVersion(_ version: String) {
|
||||
versionDefault.set(version, forKey: "cocoamqtt.emqx.version")
|
||||
}
|
||||
|
||||
func queryMQTTVersion() -> String {
|
||||
return versionDefault.string(forKey: "cocoamqtt.emqx.version") ?? "3.1.1"
|
||||
}
|
||||
|
||||
func write(_ frame: FramePublish) -> Bool {
|
||||
guard frame.qos > .qos0 else {
|
||||
return false
|
||||
}
|
||||
userDefault.set(frame.bytes(version: queryMQTTVersion()), forKey: key(frame.msgid))
|
||||
return true
|
||||
}
|
||||
|
||||
func write(_ frame: FramePubRel) -> Bool {
|
||||
userDefault.set(frame.bytes( version: queryMQTTVersion()), forKey: key(frame.msgid))
|
||||
return true
|
||||
}
|
||||
|
||||
func remove(_ frame: FramePublish) {
|
||||
userDefault.removeObject(forKey: key(frame.msgid))
|
||||
}
|
||||
|
||||
func remove(_ frame: FramePubRel) {
|
||||
userDefault.removeObject(forKey: key(frame.msgid))
|
||||
}
|
||||
|
||||
func remove(_ frame: Frame) {
|
||||
if let pub = frame as? FramePublish {
|
||||
userDefault.removeObject(forKey: key(pub.msgid))
|
||||
} else if let rel = frame as? FramePubRel {
|
||||
userDefault.removeObject(forKey: key(rel.msgid))
|
||||
}
|
||||
}
|
||||
|
||||
func synchronize() -> Bool {
|
||||
return userDefault.synchronize()
|
||||
}
|
||||
|
||||
func readAll() -> [Frame] {
|
||||
return __read(needDelete: false)
|
||||
}
|
||||
|
||||
func takeAll() -> [Frame] {
|
||||
return __read(needDelete: true)
|
||||
}
|
||||
|
||||
private func key(_ msgid: UInt16) -> String {
|
||||
return "\(msgid)"
|
||||
}
|
||||
|
||||
private static func name(_ clientId: String) -> String {
|
||||
return "cocomqtt-\(clientId)"
|
||||
}
|
||||
|
||||
private func parse(_ bytes: [UInt8]) -> (UInt8, [UInt8])? {
|
||||
// FramePubRel is 4 bytes long
|
||||
guard bytes.count > 3 else {
|
||||
return nil
|
||||
}
|
||||
// bytes 1..<5 may be 'Remaining Length'
|
||||
for i in 1 ..< min(5, bytes.count) where (bytes[i] & 0x80) == 0 {
|
||||
return (bytes[0], Array(bytes.suffix(from: i + 1)))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
private func __read(needDelete: Bool) -> [Frame] {
|
||||
var frames = [Frame]()
|
||||
let allObjs = userDefault.dictionaryRepresentation().sorted { (k1, k2) in
|
||||
let left = UInt16(k1.key)
|
||||
let right = UInt16(k2.key)
|
||||
|
||||
switch (left, right) {
|
||||
case let (l?, r?):
|
||||
return l < r
|
||||
case (_?, nil):
|
||||
return true
|
||||
case (nil, _?):
|
||||
return false
|
||||
case (nil, nil):
|
||||
return k1.key < k2.key
|
||||
}
|
||||
}
|
||||
for (k, v) in allObjs {
|
||||
guard let bytes = v as? [UInt8] else { continue }
|
||||
guard let parsed = parse(bytes) else { continue }
|
||||
|
||||
if needDelete {
|
||||
userDefault.removeObject(forKey: k)
|
||||
}
|
||||
|
||||
if let f = FramePublish(packetFixedHeaderType: parsed.0, bytes: parsed.1) {
|
||||
frames.append(f)
|
||||
} else if let f = FramePubRel(packetFixedHeaderType: parsed.0, bytes: parsed.1) {
|
||||
frames.append(f)
|
||||
}
|
||||
}
|
||||
return frames
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
//
|
||||
// CocoaMQTTTimer.swift
|
||||
// CocoaMQTT
|
||||
//
|
||||
// Contributed by Jens(https://github.com/jmiltner)
|
||||
//
|
||||
// Copyright © 2019 emqx.io. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
// modeled after RepeatingTimer by Daniel Galasko: https://medium.com/@danielgalasko/a-background-repeating-timer-in-swift-412cecfd2ef9
|
||||
/// RepeatingTimer mimics the API of DispatchSourceTimer but in a way that prevents
|
||||
/// crashes that occur from calling resume multiple times on a timer that is
|
||||
/// already resumed (noted by https://github.com/SiftScience/sift-ios/issues/52)
|
||||
class CocoaMQTTTimer {
|
||||
|
||||
let timeInterval: TimeInterval
|
||||
let startDelay: TimeInterval
|
||||
let name: String
|
||||
|
||||
init(delay: TimeInterval?=nil, name: String, timeInterval: TimeInterval) {
|
||||
self.name = name
|
||||
self.timeInterval = timeInterval
|
||||
if let delay = delay {
|
||||
self.startDelay = delay
|
||||
} else {
|
||||
self.startDelay = timeInterval
|
||||
}
|
||||
}
|
||||
|
||||
class func every(_ interval: TimeInterval, name: String, _ block: @escaping () -> Void) -> CocoaMQTTTimer {
|
||||
let timer = CocoaMQTTTimer(name: name, timeInterval: interval)
|
||||
timer.eventHandler = block
|
||||
timer.resume()
|
||||
return timer
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
class func after(_ interval: TimeInterval, name: String, _ block: @escaping () -> Void) -> CocoaMQTTTimer {
|
||||
let timer: CocoaMQTTTimer? = CocoaMQTTTimer(delay: interval, name: name, timeInterval: 0)
|
||||
timer?.eventHandler = { [weak timer] in
|
||||
block()
|
||||
timer?.suspend()
|
||||
timer = nil
|
||||
}
|
||||
timer?.resume()
|
||||
return timer!
|
||||
}
|
||||
|
||||
/// Execute the tasks concurrently on the target_queue with default QOS
|
||||
private static let target_queue = DispatchQueue(label: "io.emqx.CocoaMQTT.TimerQueue", qos: .default, attributes: .concurrent)
|
||||
|
||||
/// Execute each timer tasks serially and use the target queue for concurrency among timers
|
||||
private lazy var timer: DispatchSourceTimer = {
|
||||
let queue = DispatchQueue(label: "io.emqx.CocoaMQTT." + name, target: CocoaMQTTTimer.target_queue)
|
||||
let t = DispatchSource.makeTimerSource(flags: .strict, queue: queue)
|
||||
t.schedule(deadline: .now() + self.startDelay, repeating: self.timeInterval > 0 ? Double(self.timeInterval) : Double.infinity)
|
||||
t.setEventHandler(handler: { [weak self] in
|
||||
self?.eventHandler?()
|
||||
})
|
||||
return t
|
||||
}()
|
||||
|
||||
var eventHandler: (() -> Void)?
|
||||
|
||||
private enum State {
|
||||
case suspended
|
||||
case resumed
|
||||
case canceled
|
||||
}
|
||||
|
||||
private var state: State = .suspended
|
||||
|
||||
deinit {
|
||||
timer.setEventHandler {}
|
||||
timer.cancel()
|
||||
/*
|
||||
If the timer is suspended, calling cancel without resuming
|
||||
triggers a crash. This is documented here https://forums.developer.apple.com/thread/15902
|
||||
*/
|
||||
resume()
|
||||
eventHandler = nil
|
||||
}
|
||||
|
||||
func resume() {
|
||||
if state == .resumed {
|
||||
return
|
||||
}
|
||||
state = .resumed
|
||||
timer.resume()
|
||||
}
|
||||
|
||||
func suspend() {
|
||||
if state == .suspended {
|
||||
return
|
||||
}
|
||||
state = .suspended
|
||||
timer.suspend()
|
||||
}
|
||||
|
||||
/// Manually cancel timer
|
||||
func cancel() {
|
||||
if state == .canceled {
|
||||
return
|
||||
}
|
||||
state = .canceled
|
||||
timer.cancel()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,166 @@
|
|||
//
|
||||
// CocoaMQTTTypes.swift
|
||||
// CocoaMQTT
|
||||
//
|
||||
// Created by HJianBo on 2019/6/9.
|
||||
// Copyright © 2019 emqx.io. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Encode and Decode big-endian UInt16
|
||||
extension UInt16 {
|
||||
/// Most Significant Byte (MSB)
|
||||
private var highByte: UInt8 {
|
||||
return UInt8( (self & 0xFF00) >> 8)
|
||||
}
|
||||
|
||||
/// Least Significant Byte (LSB)
|
||||
private var lowByte: UInt8 {
|
||||
return UInt8(self & 0x00FF)
|
||||
}
|
||||
|
||||
var hlBytes: [UInt8] {
|
||||
return [highByte, lowByte]
|
||||
}
|
||||
}
|
||||
|
||||
extension String {
|
||||
/// String with two bytes length
|
||||
var bytesWithLength: [UInt8] {
|
||||
return UInt16(utf8.count).hlBytes + utf8
|
||||
}
|
||||
|
||||
var stringUTF8: String {
|
||||
let data = self.data(using: .nonLossyASCII)
|
||||
return String(data: data!, encoding: .utf8) ?? ""
|
||||
}
|
||||
}
|
||||
|
||||
extension Bool {
|
||||
/// Bool to bit of UInt8
|
||||
var bit: UInt8 {
|
||||
return self ? 1 : 0
|
||||
}
|
||||
|
||||
/// Initial a bool with a bit
|
||||
init(bit: UInt8) {
|
||||
self = (bit == 0) ? false : true
|
||||
}
|
||||
}
|
||||
|
||||
extension UInt8 {
|
||||
/// Read a bit value
|
||||
func bitAt(_ offset: UInt8) -> UInt8 {
|
||||
return (self >> offset) & 0x01
|
||||
}
|
||||
}
|
||||
|
||||
public enum CocoaMQTTError: Error {
|
||||
case invalidURL
|
||||
case readTimeout
|
||||
case writeTimeout
|
||||
@available(OSX 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
|
||||
public enum FoundationConnection: Error {
|
||||
case closed(URLSessionWebSocketTask.CloseCode)
|
||||
}
|
||||
}
|
||||
|
||||
extension Array where Element == UInt8 {
|
||||
var summary: String {
|
||||
if self.count <= 10 {
|
||||
return "\(self)"
|
||||
} else {
|
||||
var descr = "[\(self[0])"
|
||||
for i in self[1..<10] {
|
||||
descr += ", \(i)"
|
||||
}
|
||||
return "\(descr), ...]"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Data {
|
||||
var uint8: UInt8 {
|
||||
var number: UInt8 = 0
|
||||
self.copyBytes(to: &number, count: MemoryLayout<UInt8>.size)
|
||||
return number
|
||||
}
|
||||
|
||||
var uint16: UInt16 {
|
||||
let i16array = self.withUnsafeBytes { $0.load(as: UInt16.self) }
|
||||
return i16array
|
||||
}
|
||||
|
||||
var uint32: UInt32 {
|
||||
let i32array = self.withUnsafeBytes { $0.load(as: UInt32.self) }
|
||||
return i32array
|
||||
}
|
||||
|
||||
var uuid: NSUUID? {
|
||||
var bytes = [UInt8](repeating: 0, count: self.count)
|
||||
self.copyBytes(to: &bytes, count: self.count * MemoryLayout<UInt32>.size)
|
||||
return NSUUID(uuidBytes: bytes)
|
||||
}
|
||||
var stringASCII: String? {
|
||||
return NSString(data: self, encoding: String.Encoding.ascii.rawValue) as String?
|
||||
}
|
||||
|
||||
var stringUTF8: String? {
|
||||
return NSString(data: self, encoding: String.Encoding.utf8.rawValue) as String?
|
||||
}
|
||||
|
||||
struct HexEncodingOptions: OptionSet {
|
||||
let rawValue: Int
|
||||
static let upperCase = HexEncodingOptions(rawValue: 1 << 0)
|
||||
}
|
||||
|
||||
func hexEncodedString(options: HexEncodingOptions = []) -> String {
|
||||
let format = options.contains(.upperCase) ? "%02hhX" : "%02hhx"
|
||||
return map { String(format: format, $0) }.joined()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension Int {
|
||||
var data: Data {
|
||||
var int = self
|
||||
return Data(bytes: &int, count: MemoryLayout<Int>.size)
|
||||
}
|
||||
}
|
||||
|
||||
extension UInt8 {
|
||||
var data: Data {
|
||||
var int = self
|
||||
return Data(bytes: &int, count: MemoryLayout<UInt8>.size)
|
||||
}
|
||||
}
|
||||
|
||||
extension UInt16 {
|
||||
var data: Data {
|
||||
var int = self
|
||||
return Data(bytes: &int, count: MemoryLayout<UInt16>.size)
|
||||
}
|
||||
}
|
||||
|
||||
extension UInt32 {
|
||||
var data: Data {
|
||||
var int = self
|
||||
return Data(bytes: &int, count: MemoryLayout<UInt32>.size)
|
||||
}
|
||||
|
||||
var byteArrayLittleEndian: [UInt8] {
|
||||
return [
|
||||
UInt8((self & 0xFF000000) >> 24),
|
||||
UInt8((self & 0x00FF0000) >> 16),
|
||||
UInt8((self & 0x0000FF00) >> 8),
|
||||
UInt8(self & 0x000000FF)
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
extension Dictionary where Key == String, Value == String {
|
||||
var userPropertyBytes: [UInt8] {
|
||||
return reduce([UInt8](), { $0 + getMQTTPropertyData(type: CocoaMQTTPropertyName.userProperty.rawValue, value: $1.key.bytesWithLength + $1.value.bytesWithLength) })
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,215 @@
|
|||
//
|
||||
// Frame.swift
|
||||
// CocoaMQTT
|
||||
//
|
||||
// Created by Feng Lee<feng@eqmtt.io> on 14/8/3.
|
||||
// Copyright (c) 2015 emqx.io. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Quality of Service levels
|
||||
@objc public enum CocoaMQTTQoS: UInt8, CustomStringConvertible {
|
||||
/// At most once delivery
|
||||
case qos0 = 0
|
||||
|
||||
/// At least once delivery
|
||||
case qos1
|
||||
|
||||
/// Exactly once delivery
|
||||
case qos2
|
||||
|
||||
/// !!! Used SUBACK frame only
|
||||
case FAILURE = 0x80
|
||||
|
||||
public var description: String {
|
||||
switch self {
|
||||
case .qos0: return "qos0"
|
||||
case .qos1: return "qos1"
|
||||
case .qos2: return "qos2"
|
||||
case .FAILURE: return "Failure"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension CocoaMQTTQoS: Comparable {
|
||||
|
||||
public static func < (lhs: CocoaMQTTQoS, rhs: CocoaMQTTQoS) -> Bool {
|
||||
return lhs.rawValue < rhs.rawValue
|
||||
}
|
||||
|
||||
public static func <= (lhs: CocoaMQTTQoS, rhs: CocoaMQTTQoS) -> Bool {
|
||||
return lhs.rawValue <= rhs.rawValue
|
||||
}
|
||||
|
||||
public static func > (lhs: CocoaMQTTQoS, rhs: CocoaMQTTQoS) -> Bool {
|
||||
return lhs.rawValue > rhs.rawValue
|
||||
}
|
||||
|
||||
public static func >= (lhs: CocoaMQTTQoS, rhs: CocoaMQTTQoS) -> Bool {
|
||||
return lhs.rawValue >= rhs.rawValue
|
||||
}
|
||||
}
|
||||
|
||||
/// MQTT Frame Type
|
||||
enum FrameType: UInt8 {
|
||||
case reserved = 0x00
|
||||
case connect = 0x10
|
||||
case connack = 0x20
|
||||
case publish = 0x30
|
||||
case puback = 0x40
|
||||
case pubrec = 0x50
|
||||
case pubrel = 0x60
|
||||
case pubcomp = 0x70
|
||||
case subscribe = 0x80
|
||||
case suback = 0x90
|
||||
case unsubscribe = 0xA0
|
||||
case unsuback = 0xB0
|
||||
case pingreq = 0xC0
|
||||
case pingresp = 0xD0
|
||||
case disconnect = 0xE0
|
||||
case auth = 0xF0
|
||||
}
|
||||
|
||||
/// The frame can be initialized with a bytes
|
||||
protocol InitialWithBytes {
|
||||
|
||||
init?(packetFixedHeaderType: UInt8, bytes: [UInt8])
|
||||
}
|
||||
|
||||
/// MQTT Frame protocol
|
||||
protocol Frame {
|
||||
|
||||
/// Each MQTT Control Packet contains a fixed header
|
||||
/// MQTT 3.1.1
|
||||
var packetFixedHeaderType: UInt8 {get set}
|
||||
/// MQTT 5.0
|
||||
func fixedHeader() -> [UInt8]
|
||||
|
||||
/// Some types of MQTT Control Packets contain a variable header component
|
||||
/// MQTT 3.1.1
|
||||
func variableHeader() -> [UInt8]
|
||||
|
||||
/// MQTT 5.0
|
||||
func variableHeader5() -> [UInt8]
|
||||
|
||||
/// MQTT 5.0 The last field in the Variable Header of the CONNECT, CONNACK, PUBLISH, PUBACK, PUBREC, PUBREL, PUBCOMP, SUBSCRIBE, SUBACK, UNSUBSCRIBE, UNSUBACK, DISCONNECT, and AUTH packet is a set of Properties. In the CONNECT packet there is also an optional set of Properties in the Will Properties field with the Payload.
|
||||
func properties() -> [UInt8]
|
||||
|
||||
/// Some MQTT Control Packets contain a payload as the final part of the packet
|
||||
/// MQTT 3.1.1
|
||||
func payload() -> [UInt8]
|
||||
|
||||
/// MQTT 5.0
|
||||
func payload5() -> [UInt8]
|
||||
|
||||
/// fixedHeader + variableHeader + properties + payload
|
||||
func allData() -> [UInt8]
|
||||
}
|
||||
|
||||
extension Frame {
|
||||
|
||||
/// Pack struct to binary
|
||||
func bytes(version: String) -> [UInt8] {
|
||||
|
||||
if version == "5.0" {
|
||||
let fixedHeader = self.fixedHeader()
|
||||
let variableHeader5 = self.variableHeader5()
|
||||
let payload5 = self.payload5()
|
||||
let properties = self.properties()
|
||||
let len5 = UInt32(variableHeader5.count + properties.count + payload5.count)
|
||||
|
||||
printDebug("==========================MQTT 5.0==========================")
|
||||
printDebug("packetFixedHeaderType \(packetFixedHeaderType)")
|
||||
printDebug("fixedHeader \(fixedHeader)")
|
||||
printDebug("remainingLen(len: len) \(remainingLen(len: len5))")
|
||||
printDebug("variableHeader \(variableHeader5)")
|
||||
printDebug("properties \(properties)")
|
||||
printDebug("payload \(payload5)")
|
||||
printDebug("=============================================================")
|
||||
|
||||
return [packetFixedHeaderType] + remainingLen(len: len5) + variableHeader5 + properties + payload5
|
||||
} else {
|
||||
|
||||
let variableHeader = self.variableHeader()
|
||||
let payload = self.payload()
|
||||
|
||||
let len = UInt32(variableHeader.count + payload.count)
|
||||
|
||||
printDebug("=========================MQTT 3.1.1=========================")
|
||||
printDebug("packetFixedHeaderType \(packetFixedHeaderType)")
|
||||
printDebug("remainingLen(len: len) \(remainingLen(len: len))")
|
||||
printDebug("variableHeader \(variableHeader)")
|
||||
printDebug("payload \(payload)")
|
||||
printDebug("=============================================================")
|
||||
|
||||
return [packetFixedHeaderType] + remainingLen(len: len) + variableHeader + payload
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private func remainingLen(len: UInt32) -> [UInt8] {
|
||||
var bytes: [UInt8] = []
|
||||
var digit: UInt8 = 0
|
||||
|
||||
var len = len
|
||||
repeat {
|
||||
digit = UInt8(len % 128)
|
||||
len /= 128
|
||||
// if there are more digits to encode, set the top bit of this digit
|
||||
if len > 0 {
|
||||
digit |= 0x80
|
||||
}
|
||||
bytes.append(digit)
|
||||
} while len > 0
|
||||
|
||||
return bytes
|
||||
}
|
||||
}
|
||||
|
||||
/// Fixed Header Attributes
|
||||
extension Frame {
|
||||
|
||||
/// The Fixed Header consist of the following attritutes
|
||||
///
|
||||
/// +---------+----------+-------+--------+
|
||||
/// | 7 6 5 4 | 3 | 2 1 | 0 |
|
||||
/// +---------+----------+-------+--------+
|
||||
/// | Type | DUP flag | QoS | RETAIN |
|
||||
/// +-------------------------------------+
|
||||
|
||||
/// The type of the Frame
|
||||
var type: FrameType {
|
||||
return FrameType(rawValue: packetFixedHeaderType & 0xF0)!
|
||||
}
|
||||
|
||||
/// Dup flag
|
||||
var dup: Bool {
|
||||
get {
|
||||
return ((packetFixedHeaderType & 0x08) >> 3) == 0 ? false : true
|
||||
}
|
||||
set {
|
||||
packetFixedHeaderType = (packetFixedHeaderType & 0xF7) | (newValue.bit << 3)
|
||||
}
|
||||
}
|
||||
|
||||
/// Qos level
|
||||
var qos: CocoaMQTTQoS {
|
||||
get {
|
||||
return CocoaMQTTQoS(rawValue: (packetFixedHeaderType & 0x06) >> 1)!
|
||||
}
|
||||
set {
|
||||
packetFixedHeaderType = (packetFixedHeaderType & 0xF9) | (newValue.rawValue << 1)
|
||||
}
|
||||
}
|
||||
|
||||
/// Retained flag
|
||||
var retained: Bool {
|
||||
get {
|
||||
return (packetFixedHeaderType & 0x01) == 0 ? false : true
|
||||
}
|
||||
set {
|
||||
packetFixedHeaderType = (packetFixedHeaderType & 0xFE) | newValue.bit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
//
|
||||
// FrameAuth.swift
|
||||
// CocoaMQTT
|
||||
//
|
||||
// Created by liwei wang on 2021/7/4.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct FrameAuth: Frame {
|
||||
|
||||
var packetFixedHeaderType: UInt8 = FrameType.auth.rawValue
|
||||
|
||||
// 3.15.2.1 Authenticate Reason Code
|
||||
var sendReasonCode: CocoaMQTTAUTHReasonCode?
|
||||
var receiveReasonCode: CocoaMQTTAUTHReasonCode?
|
||||
|
||||
// 3.15.2.2 AUTH Properties
|
||||
var authProperties: MqttAuthProperties?
|
||||
|
||||
init(reasonCode: CocoaMQTTAUTHReasonCode, authProperties: MqttAuthProperties) {
|
||||
self.sendReasonCode = reasonCode
|
||||
self.authProperties = authProperties
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension FrameAuth {
|
||||
|
||||
func fixedHeader() -> [UInt8] {
|
||||
var header = [UInt8]()
|
||||
header += [FrameType.auth.rawValue]
|
||||
// header += [UInt8(variableHeader5().count)]
|
||||
|
||||
return header
|
||||
}
|
||||
|
||||
func variableHeader5() -> [UInt8] {
|
||||
var header = [UInt8]()
|
||||
header += [sendReasonCode!.rawValue]
|
||||
// MQTT 5.0
|
||||
header += beVariableByteInteger(length: self.properties().count)
|
||||
|
||||
return header
|
||||
}
|
||||
|
||||
func payload5() -> [UInt8] { return []}
|
||||
|
||||
func properties() -> [UInt8] {
|
||||
return authProperties?.properties ?? []
|
||||
|
||||
}
|
||||
|
||||
func allData() -> [UInt8] {
|
||||
var allData = [UInt8]()
|
||||
|
||||
allData += fixedHeader()
|
||||
allData += variableHeader5()
|
||||
allData += properties()
|
||||
allData += payload5()
|
||||
|
||||
return allData
|
||||
}
|
||||
|
||||
func variableHeader() -> [UInt8] { return [] }
|
||||
|
||||
func payload() -> [UInt8] { return [] }
|
||||
}
|
||||
|
||||
extension FrameAuth: InitialWithBytes {
|
||||
|
||||
init?(packetFixedHeaderType: UInt8, bytes: [UInt8]) {
|
||||
var protocolVersion = ""
|
||||
if let storage = CocoaMQTTStorage() {
|
||||
protocolVersion = storage.queryMQTTVersion()
|
||||
}
|
||||
guard protocolVersion == "5.0" else {
|
||||
return nil
|
||||
}
|
||||
if bytes.isEmpty {
|
||||
receiveReasonCode = .success
|
||||
} else {
|
||||
receiveReasonCode = CocoaMQTTAUTHReasonCode(rawValue: bytes[0])
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
//
|
||||
// FrameConnack.swift
|
||||
// CocoaMQTT
|
||||
//
|
||||
// Created by JianBo on 2019/8/7.
|
||||
// Copyright © 2019 emqx.io. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct FrameConnAck: Frame {
|
||||
|
||||
var packetFixedHeaderType: UInt8 = FrameType.connack.rawValue
|
||||
|
||||
// --- Attributes
|
||||
|
||||
/// MQTT 3.1.1
|
||||
var returnCode: CocoaMQTTConnAck?
|
||||
|
||||
/// MQTT 5.0
|
||||
var reasonCode: CocoaMQTTCONNACKReasonCode?
|
||||
|
||||
// 3.2.2.1.1 Session Present
|
||||
var sessPresent: Bool = false
|
||||
|
||||
// --- Attributes End
|
||||
|
||||
// 3.2.2.3 CONNACK Properties
|
||||
var connackProperties: MqttDecodeConnAck?
|
||||
var propertiesBytes: [UInt8]?
|
||||
// 3.2.3 CONNACK Payload
|
||||
// The CONNACK packet has no Payload.
|
||||
|
||||
/// MQTT 3.1.1
|
||||
init(returnCode: CocoaMQTTConnAck) {
|
||||
self.returnCode = returnCode
|
||||
}
|
||||
|
||||
/// MQTT 5.0
|
||||
init(code: CocoaMQTTCONNACKReasonCode) {
|
||||
reasonCode = code
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension FrameConnAck {
|
||||
|
||||
func fixedHeader() -> [UInt8] {
|
||||
var header = [UInt8]()
|
||||
header += [FrameType.connack.rawValue]
|
||||
|
||||
return header
|
||||
}
|
||||
|
||||
func variableHeader5() -> [UInt8] {
|
||||
return [sessPresent.bit, reasonCode!.rawValue]
|
||||
}
|
||||
|
||||
func payload5() -> [UInt8] { return [] }
|
||||
|
||||
func properties() -> [UInt8] { return propertiesBytes ?? [] }
|
||||
|
||||
func allData() -> [UInt8] {
|
||||
var allData = [UInt8]()
|
||||
|
||||
allData += fixedHeader()
|
||||
allData += variableHeader5()
|
||||
allData += properties()
|
||||
allData += payload5()
|
||||
|
||||
return allData
|
||||
}
|
||||
|
||||
func variableHeader() -> [UInt8] {
|
||||
return [sessPresent.bit, returnCode!.rawValue]
|
||||
}
|
||||
|
||||
func payload() -> [UInt8] { return [] }
|
||||
}
|
||||
|
||||
extension FrameConnAck: InitialWithBytes {
|
||||
|
||||
init?(packetFixedHeaderType: UInt8, bytes: [UInt8]) {
|
||||
guard packetFixedHeaderType == FrameType.connack.rawValue else {
|
||||
return nil
|
||||
}
|
||||
|
||||
guard bytes.count >= 2 else {
|
||||
return nil
|
||||
}
|
||||
|
||||
sessPresent = Bool(bit: bytes[0] & 0x01)
|
||||
|
||||
let mqtt5ack = CocoaMQTTCONNACKReasonCode(rawValue: bytes[1])
|
||||
reasonCode = mqtt5ack
|
||||
|
||||
let ack = CocoaMQTTConnAck(byte: bytes[1])
|
||||
returnCode = ack
|
||||
|
||||
propertiesBytes = bytes
|
||||
self.connackProperties = MqttDecodeConnAck()
|
||||
self.connackProperties!.properties(connackData: bytes)
|
||||
}
|
||||
}
|
||||
|
||||
extension FrameConnAck: CustomStringConvertible {
|
||||
var description: String {
|
||||
return "CONNACK(code: \(String(describing: reasonCode)), sp: \(sessPresent))"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,288 @@
|
|||
//
|
||||
// ConnectFrame.swift
|
||||
// CocoaMQTT
|
||||
//
|
||||
// Created by JianBo on 2019/8/7.
|
||||
// Copyright © 2019 emqx.io. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// MQTT CONNECT Frame
|
||||
struct FrameConnect: Frame {
|
||||
|
||||
var packetFixedHeaderType: UInt8 = FrameType.connect.rawValue
|
||||
|
||||
/// MQTT 3.1.1
|
||||
private let PROTOCOL_LEVEL = UInt8(4)
|
||||
private let PROTOCOL_VERSION: String = "MQTT/3.1.1"
|
||||
private let PROTOCOL_MAGIC: String = "MQTT"
|
||||
|
||||
// --- Attributes
|
||||
|
||||
// 3.1.2.1
|
||||
|
||||
let protocolName: String = "MQTT"
|
||||
// 3.1.2.2 Protocol Version
|
||||
let protocolVersion = UInt8(5)
|
||||
|
||||
// 3.1.2.5 Will Flag
|
||||
var willMsg: CocoaMQTTMessage?
|
||||
var willMsg5: CocoaMQTT5Message?
|
||||
// 3.1.2.6 Will QoS
|
||||
var willQoS: UInt8?
|
||||
// 3.1.2.7 Will Retain
|
||||
var willRetain: Bool = true
|
||||
// 3.1.2.8 User Name Flag
|
||||
var username: String?
|
||||
// 3.1.2.9 Password Flag
|
||||
var password: String?
|
||||
// 3.1.2.10 Keep Alive
|
||||
var keepAlive: UInt16 = 10
|
||||
var cleansess: Bool = true
|
||||
|
||||
// 3.1.2
|
||||
// 3.1.2.11 CONNECT Properties
|
||||
var connectProperties: MqttConnectProperties?
|
||||
|
||||
var authenticationData: Data?
|
||||
|
||||
// 3.1.3.1 Client Identifier (ClientID)
|
||||
var clientID: String
|
||||
|
||||
// --- Attributes End
|
||||
|
||||
init(clientID: String) {
|
||||
self.clientID = clientID
|
||||
}
|
||||
}
|
||||
|
||||
extension FrameConnect {
|
||||
|
||||
func fixedHeader() -> [UInt8] {
|
||||
|
||||
var header = [UInt8]()
|
||||
header += [FrameType.connect.rawValue]
|
||||
|
||||
return header
|
||||
}
|
||||
|
||||
func variableHeader5() -> [UInt8] {
|
||||
|
||||
var header = [UInt8]()
|
||||
var flags = ConnFlags()
|
||||
|
||||
// 3.1.2.1 Protocol Name
|
||||
header += protocolName.bytesWithLength
|
||||
|
||||
// 3.1.2.2 Protocol Version
|
||||
header.append(protocolVersion)
|
||||
|
||||
// 3.1.2.3 Connect Flags
|
||||
if let will = willMsg5 {
|
||||
flags.flagWill = true
|
||||
flags.flagWillQoS = will.qos.rawValue
|
||||
flags.flagWillRetain = will.retained
|
||||
}
|
||||
|
||||
if username != nil {
|
||||
flags.flagUsername = true
|
||||
|
||||
// Append password attribute if username presented
|
||||
if password != nil {
|
||||
flags.flagPassword = true
|
||||
}
|
||||
}
|
||||
|
||||
flags.flagCleanSession = cleansess
|
||||
|
||||
header.append(flags.rawValue)
|
||||
header += keepAlive.hlBytes
|
||||
|
||||
// MQTT 5.0
|
||||
header += beVariableByteInteger(length: self.properties().count)
|
||||
|
||||
return header
|
||||
}
|
||||
|
||||
func properties() -> [UInt8] {
|
||||
return connectProperties?.properties ?? []
|
||||
}
|
||||
|
||||
func payload5() -> [UInt8] {
|
||||
var payload = [UInt8]()
|
||||
|
||||
payload += clientID.bytesWithLength
|
||||
|
||||
if let will = willMsg5 {
|
||||
|
||||
payload += beVariableByteInteger(length: willMsg5!.properties.count)
|
||||
payload += will.properties
|
||||
payload += will.topic.bytesWithLength
|
||||
payload += UInt16(will.payload.count).hlBytes
|
||||
payload += will.payload
|
||||
}
|
||||
|
||||
if let username = username {
|
||||
|
||||
payload += username.bytesWithLength
|
||||
|
||||
// Append password attribute if username presented
|
||||
if let password = password {
|
||||
payload += password.bytesWithLength
|
||||
}
|
||||
}
|
||||
|
||||
return payload
|
||||
}
|
||||
|
||||
func allData() -> [UInt8] {
|
||||
|
||||
var allData = [UInt8]()
|
||||
|
||||
allData += fixedHeader()
|
||||
allData += variableHeader5()
|
||||
allData += properties()
|
||||
allData += payload5()
|
||||
|
||||
return allData
|
||||
}
|
||||
|
||||
func variableHeader() -> [UInt8] {
|
||||
|
||||
var header = [UInt8]()
|
||||
var flags = ConnFlags()
|
||||
|
||||
// variable header
|
||||
header += PROTOCOL_MAGIC.bytesWithLength
|
||||
header.append(PROTOCOL_LEVEL)
|
||||
|
||||
if let will = willMsg {
|
||||
flags.flagWill = true
|
||||
flags.flagWillQoS = will.qos.rawValue
|
||||
flags.flagWillRetain = will.retained
|
||||
}
|
||||
|
||||
if username != nil {
|
||||
flags.flagUsername = true
|
||||
|
||||
// Append password attribute if username presented
|
||||
if password != nil {
|
||||
flags.flagPassword = true
|
||||
}
|
||||
}
|
||||
|
||||
flags.flagCleanSession = cleansess
|
||||
|
||||
header.append(flags.rawValue)
|
||||
header += keepAlive.hlBytes
|
||||
|
||||
return header
|
||||
}
|
||||
|
||||
func payload() -> [UInt8] {
|
||||
|
||||
var payload = [UInt8]()
|
||||
|
||||
payload += clientID.bytesWithLength
|
||||
|
||||
if let will = willMsg {
|
||||
payload += will.topic.bytesWithLength
|
||||
payload += UInt16(will.payload.count).hlBytes
|
||||
payload += will.payload
|
||||
}
|
||||
if let username = username {
|
||||
payload += username.bytesWithLength
|
||||
|
||||
// Append password attribute if username presented
|
||||
if let password = password {
|
||||
payload += password.bytesWithLength
|
||||
}
|
||||
}
|
||||
|
||||
return payload
|
||||
}
|
||||
}
|
||||
|
||||
extension FrameConnect: CustomStringConvertible {
|
||||
var description: String {
|
||||
return "CONNECT(id: \(clientID), username: \(username ?? "nil"), " +
|
||||
"password: \(password ?? "nil"), keepAlive : \(keepAlive), " +
|
||||
"cleansess: \(cleansess))"
|
||||
}
|
||||
}
|
||||
|
||||
/// Connect Flags
|
||||
private struct ConnFlags {
|
||||
|
||||
/// These Flags consist of following flags:
|
||||
///
|
||||
/// +----------+----------+------------+--------------------+--------------+----------+
|
||||
/// | 7 | 6 | 5 | 4 3 | 2 | 1 | 0 |
|
||||
/// +----------+----------+------------+---------+----------+--------------+----------+
|
||||
/// | username | password | willretain | willqos | willflag | cleansession | reserved |
|
||||
/// +----------+----------+------------+---------+----------+--------------+----------+
|
||||
///
|
||||
var rawValue: UInt8 = 0
|
||||
|
||||
var flagUsername: Bool {
|
||||
get {
|
||||
return Bool(bit: (rawValue >> 7) & 0x01)
|
||||
}
|
||||
|
||||
set {
|
||||
rawValue = (rawValue & 0x7F) | (newValue.bit << 7)
|
||||
}
|
||||
}
|
||||
|
||||
var flagPassword: Bool {
|
||||
get {
|
||||
return Bool(bit: (rawValue >> 6) & 0x01)
|
||||
}
|
||||
|
||||
set {
|
||||
rawValue = (rawValue & 0xBF) | (newValue.bit << 6)
|
||||
}
|
||||
}
|
||||
|
||||
var flagWillRetain: Bool {
|
||||
get {
|
||||
return Bool(bit: (rawValue >> 5) & 0x01)
|
||||
}
|
||||
|
||||
set {
|
||||
rawValue = (rawValue & 0xDF) | (newValue.bit << 5)
|
||||
}
|
||||
}
|
||||
|
||||
var flagWillQoS: UInt8 {
|
||||
get {
|
||||
return (rawValue >> 3) & 0x03
|
||||
}
|
||||
|
||||
set {
|
||||
rawValue = (rawValue & 0xE7) | (newValue << 3)
|
||||
}
|
||||
}
|
||||
|
||||
var flagWill: Bool {
|
||||
get {
|
||||
return Bool(bit: (rawValue >> 2) & 0x01)
|
||||
}
|
||||
|
||||
set {
|
||||
rawValue = (rawValue & 0xFB) | (newValue.bit << 2)
|
||||
}
|
||||
}
|
||||
|
||||
var flagCleanSession: Bool {
|
||||
get {
|
||||
return Bool(bit: (rawValue >> 1) & 0x01)
|
||||
}
|
||||
|
||||
set {
|
||||
rawValue = (rawValue & 0xFD) | (newValue.bit << 1)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,128 @@
|
|||
//
|
||||
// FrameDisconnect.swift
|
||||
// CocoaMQTT
|
||||
//
|
||||
// Created by JianBo on 2019/8/7.
|
||||
// Copyright © 2019 emqx.io. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// MQTT Disconnect packet
|
||||
struct FrameDisconnect: Frame {
|
||||
|
||||
var packetFixedHeaderType: UInt8 = FrameType.disconnect.rawValue
|
||||
|
||||
// 3.14.2 DISCONNECT Variable Header
|
||||
public var sendReasonCode: CocoaMQTTDISCONNECTReasonCode?
|
||||
public var receiveReasonCode: CocoaMQTTDISCONNECTReasonCode?
|
||||
|
||||
// 3.14.2.2.2 Session Expiry Interval
|
||||
public var sessionExpiryInterval: UInt32?
|
||||
|
||||
// 3.14.2.2.3 Reason String
|
||||
public var reasonString: String?
|
||||
// 3.14.2.2.4 User Property
|
||||
public var userProperties: [String: String]?
|
||||
// 3.14.2.2.5 Server Reference
|
||||
public var serverReference: String?
|
||||
|
||||
/// MQTT 3.1.1
|
||||
init() { /* Nothing to do */ }
|
||||
|
||||
/// MQTT 5.0
|
||||
init(disconnectReasonCode: CocoaMQTTDISCONNECTReasonCode) {
|
||||
self.sendReasonCode = disconnectReasonCode
|
||||
}
|
||||
}
|
||||
|
||||
extension FrameDisconnect {
|
||||
|
||||
func fixedHeader() -> [UInt8] {
|
||||
var header = [UInt8]()
|
||||
header += [FrameType.disconnect.rawValue]
|
||||
|
||||
return header
|
||||
}
|
||||
|
||||
func variableHeader5() -> [UInt8] {
|
||||
|
||||
var header = [UInt8]()
|
||||
header += [sendReasonCode!.rawValue]
|
||||
|
||||
// MQTT 5.0
|
||||
header += beVariableByteInteger(length: self.properties().count)
|
||||
|
||||
return header
|
||||
}
|
||||
|
||||
func payload5() -> [UInt8] { return [] }
|
||||
|
||||
func properties() -> [UInt8] {
|
||||
|
||||
var properties = [UInt8]()
|
||||
|
||||
// 3.14.2.2.2 Session Expiry Interval
|
||||
if let sessionExpiryInterval = self.sessionExpiryInterval {
|
||||
properties += getMQTTPropertyData(type: CocoaMQTTPropertyName.sessionExpiryInterval.rawValue, value: sessionExpiryInterval.byteArrayLittleEndian)
|
||||
}
|
||||
// 3.14.2.2.3 Reason String
|
||||
if let reasonString = self.reasonString {
|
||||
properties += getMQTTPropertyData(type: CocoaMQTTPropertyName.reasonString.rawValue, value: reasonString.bytesWithLength)
|
||||
}
|
||||
// 3.14.2.2.4 User Property
|
||||
if let userProperty = self.userProperties {
|
||||
properties += userProperty.userPropertyBytes
|
||||
}
|
||||
// 3.14.2.2.5 Server Reference
|
||||
if let serverReference = self.serverReference {
|
||||
properties += getMQTTPropertyData(type: CocoaMQTTPropertyName.serverReference.rawValue, value: serverReference.bytesWithLength)
|
||||
}
|
||||
|
||||
return properties
|
||||
}
|
||||
|
||||
func allData() -> [UInt8] {
|
||||
|
||||
var allData = [UInt8]()
|
||||
|
||||
allData += fixedHeader()
|
||||
allData += variableHeader5()
|
||||
allData += properties()
|
||||
allData += payload5()
|
||||
|
||||
return allData
|
||||
}
|
||||
|
||||
func variableHeader() -> [UInt8] { return [] }
|
||||
|
||||
func payload() -> [UInt8] { return [] }
|
||||
}
|
||||
|
||||
extension FrameDisconnect: InitialWithBytes {
|
||||
|
||||
init?(packetFixedHeaderType: UInt8, bytes: [UInt8]) {
|
||||
|
||||
var protocolVersion = ""
|
||||
if let storage = CocoaMQTTStorage() {
|
||||
protocolVersion = storage.queryMQTTVersion()
|
||||
}
|
||||
|
||||
if protocolVersion == "5.0" {
|
||||
if bytes.isEmpty {
|
||||
receiveReasonCode = .normalDisconnection
|
||||
} else {
|
||||
receiveReasonCode = CocoaMQTTDISCONNECTReasonCode(rawValue: bytes[0])
|
||||
}
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension FrameDisconnect: CustomStringConvertible {
|
||||
var description: String {
|
||||
return "DISCONNECT"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
//
|
||||
// FramePingReq.swift
|
||||
// CocoaMQTT
|
||||
//
|
||||
// Created by JianBo on 2019/8/7.
|
||||
// Copyright © 2019 emqx.io. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct FramePingReq: Frame {
|
||||
|
||||
var packetFixedHeaderType: UInt8 = FrameType.pingreq.rawValue
|
||||
}
|
||||
|
||||
extension FramePingReq {
|
||||
|
||||
func fixedHeader() -> [UInt8] {
|
||||
|
||||
var header = [UInt8]()
|
||||
header += [FrameType.pingreq.rawValue]
|
||||
|
||||
return header
|
||||
}
|
||||
|
||||
func variableHeader5() -> [UInt8] { return [] }
|
||||
|
||||
func payload5() -> [UInt8] { return [] }
|
||||
|
||||
func properties() -> [UInt8] { return [] }
|
||||
|
||||
func allData() -> [UInt8] {
|
||||
|
||||
var allData = [UInt8]()
|
||||
|
||||
allData += fixedHeader()
|
||||
allData += variableHeader5()
|
||||
allData += properties()
|
||||
allData += payload5()
|
||||
|
||||
return allData
|
||||
}
|
||||
|
||||
func variableHeader() -> [UInt8] { return [] }
|
||||
|
||||
func payload() -> [UInt8] { return [] }
|
||||
|
||||
}
|
||||
|
||||
extension FramePingReq: CustomStringConvertible {
|
||||
var description: String {
|
||||
return "PING"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
//
|
||||
// FramePingResp.swift
|
||||
// CocoaMQTT
|
||||
//
|
||||
// Created by JianBo on 2019/8/7.
|
||||
// Copyright © 2019 emqx.io. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// MQTT PINGRESP packet
|
||||
struct FramePingResp: Frame {
|
||||
|
||||
var packetFixedHeaderType: UInt8 = FrameType.pingresp.rawValue
|
||||
}
|
||||
|
||||
extension FramePingResp {
|
||||
|
||||
func fixedHeader() -> [UInt8] {
|
||||
|
||||
var header = [UInt8]()
|
||||
header += [FrameType.pingresp.rawValue]
|
||||
|
||||
return header
|
||||
}
|
||||
|
||||
func variableHeader5() -> [UInt8] { return [] }
|
||||
|
||||
func payload5() -> [UInt8] { return [] }
|
||||
|
||||
func properties() -> [UInt8] { return [] }
|
||||
|
||||
func allData() -> [UInt8] {
|
||||
|
||||
var allData = [UInt8]()
|
||||
|
||||
allData += fixedHeader()
|
||||
allData += variableHeader5()
|
||||
allData += properties()
|
||||
allData += payload5()
|
||||
|
||||
return allData
|
||||
}
|
||||
|
||||
func variableHeader() -> [UInt8] { return [] }
|
||||
|
||||
func payload() -> [UInt8] { return [] }
|
||||
}
|
||||
|
||||
extension FramePingResp: InitialWithBytes {
|
||||
|
||||
init?(packetFixedHeaderType: UInt8, bytes: [UInt8]) {
|
||||
|
||||
guard packetFixedHeaderType == FrameType.pingresp.rawValue else {
|
||||
return nil
|
||||
}
|
||||
|
||||
guard bytes.count == 0 else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension FramePingResp: CustomStringConvertible {
|
||||
var description: String {
|
||||
return "PONG"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,129 @@
|
|||
//
|
||||
// FramePuback.swift
|
||||
// CocoaMQTT
|
||||
//
|
||||
// Created by JianBo on 2019/8/7.
|
||||
// Copyright © 2019 emqx.io. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// MQTT PUBACK packet
|
||||
struct FramePubAck: Frame {
|
||||
|
||||
var packetFixedHeaderType: UInt8 = FrameType.puback.rawValue
|
||||
|
||||
// --- Attributes
|
||||
|
||||
var msgid: UInt16
|
||||
|
||||
// --- Attributes End
|
||||
|
||||
// 3.4.2.1 PUBACK Reason Code
|
||||
public var reasonCode: CocoaMQTTPUBACKReasonCode?
|
||||
|
||||
// 3.4.2.2 PUBACK Properties
|
||||
public var pubAckProperties: MqttDecodePubAck?
|
||||
// 3.4.2.2.2 Reason String
|
||||
public var reasonString: String?
|
||||
// 3.4.2.2.3 User Property
|
||||
public var userProperties: [String: String]?
|
||||
|
||||
/// MQTT 3.1.1
|
||||
init(msgid: UInt16) {
|
||||
self.msgid = msgid
|
||||
}
|
||||
|
||||
/// MQTT 5.0
|
||||
init(msgid: UInt16, reasonCode: CocoaMQTTPUBACKReasonCode) {
|
||||
self.msgid = msgid
|
||||
self.reasonCode = reasonCode
|
||||
}
|
||||
}
|
||||
|
||||
extension FramePubAck {
|
||||
|
||||
func fixedHeader() -> [UInt8] {
|
||||
|
||||
var header = [UInt8]()
|
||||
header += [FrameType.puback.rawValue]
|
||||
|
||||
return header
|
||||
}
|
||||
|
||||
func variableHeader5() -> [UInt8] {
|
||||
|
||||
// 3.4.2 MSB+LSB
|
||||
var header = msgid.hlBytes
|
||||
// 3.4.2.1 PUBACK Reason Code
|
||||
header += [reasonCode!.rawValue]
|
||||
|
||||
// MQTT 5.0
|
||||
header += beVariableByteInteger(length: self.properties().count)
|
||||
|
||||
return header
|
||||
}
|
||||
|
||||
func payload5() -> [UInt8] { return [] }
|
||||
|
||||
func properties() -> [UInt8] {
|
||||
|
||||
var properties = [UInt8]()
|
||||
|
||||
// 3.4.2.2.2 Reason String
|
||||
if let reasonString = self.reasonString {
|
||||
properties += getMQTTPropertyData(type: CocoaMQTTPropertyName.reasonString.rawValue, value: reasonString.bytesWithLength)
|
||||
}
|
||||
|
||||
// 3.4.2.2.3 User Property
|
||||
if let userProperty = self.userProperties {
|
||||
properties += userProperty.userPropertyBytes
|
||||
}
|
||||
|
||||
return properties
|
||||
}
|
||||
|
||||
func allData() -> [UInt8] {
|
||||
|
||||
var allData = [UInt8]()
|
||||
|
||||
allData += fixedHeader()
|
||||
allData += variableHeader5()
|
||||
allData += properties()
|
||||
allData += payload5()
|
||||
|
||||
return allData
|
||||
}
|
||||
|
||||
func variableHeader() -> [UInt8] { return msgid.hlBytes }
|
||||
|
||||
func payload() -> [UInt8] { return [] }
|
||||
}
|
||||
|
||||
extension FramePubAck: InitialWithBytes {
|
||||
|
||||
init?(packetFixedHeaderType: UInt8, bytes: [UInt8]) {
|
||||
guard packetFixedHeaderType == FrameType.puback.rawValue else {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MQTT 5.0 bytes.count == 4
|
||||
guard bytes.count >= 2 else {
|
||||
return nil
|
||||
}
|
||||
|
||||
if bytes.count > 2 {
|
||||
self.reasonCode = CocoaMQTTPUBACKReasonCode(rawValue: bytes[2])
|
||||
self.pubAckProperties = MqttDecodePubAck()
|
||||
self.pubAckProperties!.decodePubAck(fixedHeader: packetFixedHeaderType, pubAckData: bytes)
|
||||
}
|
||||
|
||||
msgid = UInt16(bytes[0]) << 8 + UInt16(bytes[1])
|
||||
}
|
||||
}
|
||||
|
||||
extension FramePubAck: CustomStringConvertible {
|
||||
var description: String {
|
||||
return "PUBACK(id: \(msgid))"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
//
|
||||
// FramePubCom.swift
|
||||
// CocoaMQTT
|
||||
//
|
||||
// Created by JianBo on 2019/8/7.
|
||||
// Copyright © 2019 emqx.io. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// ublish complete (QoS 2 delivery part 3)
|
||||
/// The PUBCOMP packet is the response to a PUBREL packet. It is the fourth and final packet of the QoS 2 protocol exchange.
|
||||
struct FramePubComp: Frame {
|
||||
|
||||
var packetFixedHeaderType: UInt8 = FrameType.pubcomp.rawValue
|
||||
|
||||
// --- Attributes
|
||||
|
||||
var msgid: UInt16
|
||||
|
||||
// --- Attributes End
|
||||
|
||||
// 3.7.2.1 PUBCOMP Reason Code
|
||||
public var reasonCode: CocoaMQTTPUBCOMPReasonCode?
|
||||
|
||||
// 3.7.2.2 PUBCOMP Properties
|
||||
public var pubCompProperties: MqttDecodePubComp?
|
||||
// 3.7.2.2.2 Reason String
|
||||
public var reasonString: String?
|
||||
// 3.7.2.2.3 User Property
|
||||
public var userProperties: [String: String]?
|
||||
|
||||
/// MQTT 3.1.1
|
||||
init(msgid: UInt16) {
|
||||
self.msgid = msgid
|
||||
}
|
||||
|
||||
/// MQTT 5.0
|
||||
init(msgid: UInt16, reasonCode: CocoaMQTTPUBCOMPReasonCode) {
|
||||
self.msgid = msgid
|
||||
self.reasonCode = reasonCode
|
||||
}
|
||||
}
|
||||
|
||||
extension FramePubComp {
|
||||
|
||||
func fixedHeader() -> [UInt8] {
|
||||
|
||||
var header = [UInt8]()
|
||||
header += [FrameType.pubcomp.rawValue]
|
||||
|
||||
return header
|
||||
}
|
||||
|
||||
func variableHeader5() -> [UInt8] {
|
||||
|
||||
// 3.7.2 MSB+LSB
|
||||
var header = msgid.hlBytes
|
||||
// 3.7.2.1 PUBACK Reason Code
|
||||
header += [reasonCode!.rawValue]
|
||||
|
||||
// MQTT 5.0
|
||||
header += beVariableByteInteger(length: self.properties().count)
|
||||
|
||||
return header
|
||||
}
|
||||
|
||||
func payload5() -> [UInt8] { return [] }
|
||||
|
||||
func properties() -> [UInt8] {
|
||||
|
||||
var properties = [UInt8]()
|
||||
|
||||
// 3.7.2.2.2 Reason String
|
||||
if let reasonString = self.reasonString {
|
||||
properties += getMQTTPropertyData(type: CocoaMQTTPropertyName.reasonString.rawValue, value: reasonString.bytesWithLength)
|
||||
}
|
||||
|
||||
// 3.7.2.2.3 User Property
|
||||
if let userProperty = self.userProperties {
|
||||
properties += userProperty.userPropertyBytes
|
||||
}
|
||||
|
||||
return properties
|
||||
}
|
||||
|
||||
func allData() -> [UInt8] {
|
||||
|
||||
var allData = [UInt8]()
|
||||
|
||||
allData += fixedHeader()
|
||||
allData += variableHeader5()
|
||||
allData += properties()
|
||||
allData += payload5()
|
||||
|
||||
return allData
|
||||
}
|
||||
|
||||
func variableHeader() -> [UInt8] { return msgid.hlBytes }
|
||||
|
||||
func payload() -> [UInt8] { return [] }
|
||||
}
|
||||
|
||||
extension FramePubComp: InitialWithBytes {
|
||||
|
||||
init?(packetFixedHeaderType: UInt8, bytes: [UInt8]) {
|
||||
|
||||
guard packetFixedHeaderType == FrameType.pubcomp.rawValue else {
|
||||
return nil
|
||||
}
|
||||
|
||||
guard bytes.count >= 2 else {
|
||||
return nil
|
||||
}
|
||||
|
||||
msgid = UInt16(bytes[0]) << 8 + UInt16(bytes[1])
|
||||
|
||||
self.pubCompProperties = MqttDecodePubComp()
|
||||
self.pubCompProperties!.decodePubComp(fixedHeader: packetFixedHeaderType, pubAckData: bytes)
|
||||
}
|
||||
}
|
||||
|
||||
extension FramePubComp: CustomStringConvertible {
|
||||
var description: String {
|
||||
return "PUBCOMP(id: \(msgid))"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
//
|
||||
// FramePubRec.swift
|
||||
// CocoaMQTT
|
||||
//
|
||||
// Created by JianBo on 2019/8/7.
|
||||
// Copyright © 2019 emqx.io. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// MQTT PUBREC packet
|
||||
struct FramePubRec: Frame {
|
||||
|
||||
var packetFixedHeaderType: UInt8 = FrameType.pubrec.rawValue
|
||||
|
||||
// --- Attributes
|
||||
|
||||
var msgid: UInt16
|
||||
|
||||
// --- Attributes End
|
||||
|
||||
// 3.5.2.1 PUBREC Reason Code
|
||||
public var reasonCode: CocoaMQTTPUBRECReasonCode?
|
||||
|
||||
// 3.5.2.2 PUBREC Properties
|
||||
public var pubRecProperties: MqttDecodePubRec?
|
||||
// 3.5.2.2.2 Reason String
|
||||
public var reasonString: String?
|
||||
// 3.5.2.2.3 User Property
|
||||
public var userProperties: [String: String]?
|
||||
|
||||
/// MQTT 3.1.1
|
||||
init(msgid: UInt16) {
|
||||
self.msgid = msgid
|
||||
}
|
||||
|
||||
/// MQTT 5.0
|
||||
init(msgid: UInt16, reasonCode: CocoaMQTTPUBRECReasonCode) {
|
||||
self.msgid = msgid
|
||||
self.reasonCode = reasonCode
|
||||
}
|
||||
}
|
||||
|
||||
extension FramePubRec {
|
||||
|
||||
func fixedHeader() -> [UInt8] {
|
||||
|
||||
var header = [UInt8]()
|
||||
header += [FrameType.pubrec.rawValue]
|
||||
|
||||
return header
|
||||
}
|
||||
|
||||
func variableHeader5() -> [UInt8] {
|
||||
|
||||
// 3.5.2 MSB+LSB
|
||||
var header = msgid.hlBytes
|
||||
// 3.5.2.1 PUBACK Reason Code
|
||||
header += [reasonCode!.rawValue]
|
||||
|
||||
// MQTT 5.0
|
||||
header += beVariableByteInteger(length: self.properties().count)
|
||||
|
||||
return header
|
||||
}
|
||||
|
||||
func payload5() -> [UInt8] { return [] }
|
||||
|
||||
func properties() -> [UInt8] {
|
||||
|
||||
var properties = [UInt8]()
|
||||
|
||||
// 3.5.2.2.2 Reason String
|
||||
if let reasonString = self.reasonString {
|
||||
properties += getMQTTPropertyData(type: CocoaMQTTPropertyName.reasonString.rawValue, value: reasonString.bytesWithLength)
|
||||
}
|
||||
|
||||
// 3.5.2.2.3 User Property
|
||||
if let userProperty = self.userProperties {
|
||||
properties += userProperty.userPropertyBytes
|
||||
}
|
||||
|
||||
return properties
|
||||
}
|
||||
|
||||
func allData() -> [UInt8] {
|
||||
|
||||
var allData = [UInt8]()
|
||||
|
||||
allData += fixedHeader()
|
||||
allData += variableHeader5()
|
||||
allData += properties()
|
||||
allData += payload5()
|
||||
|
||||
return allData
|
||||
}
|
||||
|
||||
func variableHeader() -> [UInt8] { return msgid.hlBytes }
|
||||
|
||||
func payload() -> [UInt8] { return [] }
|
||||
}
|
||||
|
||||
extension FramePubRec: InitialWithBytes {
|
||||
|
||||
init?(packetFixedHeaderType: UInt8, bytes: [UInt8]) {
|
||||
|
||||
guard packetFixedHeaderType == FrameType.pubrec.rawValue else {
|
||||
return nil
|
||||
}
|
||||
guard bytes.count >= 2 else {
|
||||
return nil
|
||||
}
|
||||
|
||||
msgid = UInt16(bytes[0]) << 8 + UInt16(bytes[1])
|
||||
|
||||
self.pubRecProperties = MqttDecodePubRec()
|
||||
self.pubRecProperties!.decodePubRec(fixedHeader: packetFixedHeaderType, pubAckData: bytes)
|
||||
}
|
||||
}
|
||||
|
||||
extension FramePubRec: CustomStringConvertible {
|
||||
var description: String {
|
||||
return "PUBREC(id: \(msgid))"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
//
|
||||
// FramePubRel.swift
|
||||
// CocoaMQTT
|
||||
//
|
||||
// Created by JianBo on 2019/8/7.
|
||||
// Copyright © 2019 emqx.io. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// MQTT PUBREL packet
|
||||
/// A PUBREL packet is the response to a PUBREC packet. It is the third packet of the QoS 2 protocol exchange.
|
||||
struct FramePubRel: Frame {
|
||||
|
||||
var packetFixedHeaderType: UInt8 = UInt8(FrameType.pubrel.rawValue + 2)
|
||||
|
||||
// --- Attributes
|
||||
|
||||
var msgid: UInt16
|
||||
|
||||
// --- Attributes End
|
||||
|
||||
// 3.6.2.1 PUBREL Reason Code
|
||||
public var reasonCode: CocoaMQTTPUBRELReasonCode = .success
|
||||
|
||||
// 3.6.2.2 PUBREL Properties
|
||||
public var pubRelProperties: MqttDecodePubRel?
|
||||
// 3.6.2.2.2 Reason String
|
||||
public var reasonString: String?
|
||||
// 3.6.2.2.3 User Property
|
||||
public var userProperties: [String: String]?
|
||||
|
||||
init(msgid: UInt16) {
|
||||
self.msgid = msgid
|
||||
|
||||
qos = .qos1
|
||||
}
|
||||
}
|
||||
|
||||
extension FramePubRel {
|
||||
|
||||
func fixedHeader() -> [UInt8] {
|
||||
|
||||
var header = [UInt8]()
|
||||
header += [FrameType.pubrel.rawValue]
|
||||
|
||||
return header
|
||||
}
|
||||
|
||||
func variableHeader5() -> [UInt8] {
|
||||
|
||||
// 3.6.2 MSB+LSB
|
||||
var header = msgid.hlBytes
|
||||
// 3.6.2.1 PUBACK Reason Code
|
||||
header += [reasonCode.rawValue]
|
||||
// MQTT 5.0
|
||||
header += beVariableByteInteger(length: self.properties().count)
|
||||
|
||||
return header
|
||||
}
|
||||
|
||||
func payload5() -> [UInt8] { return [] }
|
||||
|
||||
func properties() -> [UInt8] {
|
||||
|
||||
var properties = [UInt8]()
|
||||
|
||||
// 3.6.2.2.2 Reason String
|
||||
if let reasonString = self.reasonString {
|
||||
properties += getMQTTPropertyData(type: CocoaMQTTPropertyName.reasonString.rawValue, value: reasonString.bytesWithLength)
|
||||
}
|
||||
|
||||
// 3.6.2.2.3 User Property
|
||||
if let userProperty = self.userProperties {
|
||||
properties += userProperty.userPropertyBytes
|
||||
}
|
||||
|
||||
return properties
|
||||
}
|
||||
|
||||
func allData() -> [UInt8] {
|
||||
|
||||
var allData = [UInt8]()
|
||||
|
||||
allData += fixedHeader()
|
||||
allData += variableHeader5()
|
||||
allData += properties()
|
||||
allData += payload5()
|
||||
|
||||
return allData
|
||||
}
|
||||
|
||||
func variableHeader() -> [UInt8] { return msgid.hlBytes }
|
||||
|
||||
func payload() -> [UInt8] { return [] }
|
||||
}
|
||||
|
||||
extension FramePubRel: InitialWithBytes {
|
||||
|
||||
init?(packetFixedHeaderType: UInt8, bytes: [UInt8]) {
|
||||
guard packetFixedHeaderType == 0x62 else {
|
||||
return nil
|
||||
}
|
||||
guard bytes.count >= 2 else {
|
||||
return nil
|
||||
}
|
||||
|
||||
self.packetFixedHeaderType = packetFixedHeaderType
|
||||
msgid = UInt16(bytes[0]) << 8 + UInt16(bytes[1])
|
||||
|
||||
self.pubRelProperties = MqttDecodePubRel()
|
||||
self.pubRelProperties!.decodePubRel(fixedHeader: packetFixedHeaderType, pubAckData: bytes)
|
||||
}
|
||||
}
|
||||
|
||||
extension FramePubRel: CustomStringConvertible {
|
||||
var description: String {
|
||||
return "PUBREL(id: \(msgid))"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,237 @@
|
|||
//
|
||||
// FramePublish.swift
|
||||
// CocoaMQTT
|
||||
//
|
||||
// Created by JianBo on 2019/8/7.
|
||||
// Copyright © 2019 emqx.io. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
// MQTT PUBLISH Frame
|
||||
struct FramePublish: Frame {
|
||||
|
||||
// 3.3.1.4 Remaining Length
|
||||
public var remainingLength: UInt32?
|
||||
|
||||
// 3.3.2.1 Topic Name
|
||||
public var topicName: String?
|
||||
// 3.3.2.2 Packet Identifier
|
||||
public var packetIdentifier: UInt16?
|
||||
|
||||
// 3.3.2.3 PUBLISH Properties
|
||||
public var publishProperties: MqttPublishProperties?
|
||||
public var publishRecProperties: MqttDecodePublish?
|
||||
|
||||
var packetFixedHeaderType: UInt8 = FrameType.publish.rawValue
|
||||
|
||||
// --- Attributes
|
||||
|
||||
var msgid: UInt16
|
||||
|
||||
var topic: String = ""
|
||||
|
||||
var _payload: [UInt8] = []
|
||||
|
||||
var mqtt5Topic: String = ""
|
||||
|
||||
// --- Attributes End
|
||||
|
||||
init(topic: String, payload: [UInt8], qos: CocoaMQTTQoS = .qos0, msgid: UInt16 = 0) {
|
||||
|
||||
self.topic = topic
|
||||
self._payload = payload
|
||||
self.msgid = msgid
|
||||
self.qos = qos
|
||||
}
|
||||
}
|
||||
|
||||
extension FramePublish {
|
||||
|
||||
func fixedHeader() -> [UInt8] {
|
||||
|
||||
var header = [UInt8]()
|
||||
header += [FrameType.publish.rawValue]
|
||||
|
||||
return header
|
||||
}
|
||||
|
||||
func variableHeader5() -> [UInt8] {
|
||||
|
||||
// 3.3.2.1 Topic Name
|
||||
var header = self.topic.bytesWithLength
|
||||
// 3.3.2.2 Packet Identifier qos1 or qos2
|
||||
if qos > .qos0 {
|
||||
header += msgid.hlBytes
|
||||
// header.append(UInt8(0))
|
||||
// header.append(QoS.rawValue)
|
||||
}
|
||||
|
||||
// MQTT 5.0
|
||||
header += beVariableByteInteger(length: self.properties().count)
|
||||
|
||||
return header
|
||||
}
|
||||
|
||||
func payload5() -> [UInt8] { return _payload }
|
||||
|
||||
func properties() -> [UInt8] {
|
||||
|
||||
// Properties
|
||||
return publishProperties?.properties ?? []
|
||||
}
|
||||
|
||||
func allData() -> [UInt8] {
|
||||
|
||||
var allData = [UInt8]()
|
||||
|
||||
allData += fixedHeader()
|
||||
allData += variableHeader5()
|
||||
allData += properties()
|
||||
allData += payload5()
|
||||
|
||||
return allData
|
||||
}
|
||||
|
||||
func variableHeader() -> [UInt8] {
|
||||
|
||||
var header = topic.bytesWithLength
|
||||
|
||||
if qos > .qos0 {
|
||||
header += msgid.hlBytes
|
||||
}
|
||||
|
||||
return header
|
||||
}
|
||||
|
||||
func payload() -> [UInt8] { return _payload }
|
||||
}
|
||||
|
||||
extension FramePublish: InitialWithBytes {
|
||||
|
||||
init?(packetFixedHeaderType: UInt8, bytes: [UInt8]) {
|
||||
|
||||
guard packetFixedHeaderType & 0xF0 == FrameType.publish.rawValue else {
|
||||
return nil
|
||||
}
|
||||
let recDup = ((packetFixedHeaderType & 0b0000_1000) >> 3) > 0
|
||||
|
||||
guard let recQos = CocoaMQTTQoS(rawValue: (packetFixedHeaderType & 0b0000_0110) >> 1) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
let recRetain = (packetFixedHeaderType & 0b0000_0001) > 0
|
||||
// Reserved
|
||||
var flags: UInt8 = 0
|
||||
|
||||
if recRetain {
|
||||
flags = flags | 0b0000_0001
|
||||
} else {
|
||||
flags = flags | 0b0000_0000
|
||||
}
|
||||
|
||||
if recDup {
|
||||
flags = flags | 0b0011_1000
|
||||
} else {
|
||||
flags = flags | 0b0011_0000
|
||||
}
|
||||
|
||||
switch recQos {
|
||||
case .qos0:
|
||||
flags = flags | 0b0011_0000
|
||||
case .qos1:
|
||||
flags = flags | 0b0011_0010
|
||||
case .qos2:
|
||||
flags = flags | 0b0011_0100
|
||||
case .FAILURE:
|
||||
printDebug("FAILTURE")
|
||||
}
|
||||
self.packetFixedHeaderType = flags
|
||||
|
||||
// Packet Identifier
|
||||
// The Packet Identifier field is only present in PUBLISH packets where the QoS level is 1 or 2.
|
||||
|
||||
// parse topic
|
||||
if bytes.count < 2 {
|
||||
return nil
|
||||
}
|
||||
|
||||
let topicLength = Int(UInt16(bytes[0]) << 8 | UInt16(bytes[1]))
|
||||
let topicStart = 2
|
||||
let topicEnd = topicStart + topicLength
|
||||
|
||||
if bytes.count < topicEnd {
|
||||
return nil
|
||||
}
|
||||
var pos = topicEnd
|
||||
|
||||
// msgid
|
||||
if (packetFixedHeaderType & 0x06) >> 1 == CocoaMQTTQoS.qos0.rawValue {
|
||||
msgid = 0
|
||||
} else {
|
||||
if bytes.count < pos + 2 {
|
||||
return nil
|
||||
}
|
||||
msgid = UInt16(bytes[pos]) << 8 + UInt16(bytes[pos+1])
|
||||
pos += 2
|
||||
}
|
||||
|
||||
var protocolVersion = ""
|
||||
if let storage = CocoaMQTTStorage() {
|
||||
protocolVersion = storage.queryMQTTVersion()
|
||||
}
|
||||
|
||||
if protocolVersion == "5.0" {
|
||||
let data = MqttDecodePublish()
|
||||
data.decodePublish(fixedHeader: packetFixedHeaderType, publishData: bytes)
|
||||
pos = data.mqtt5DataIndex
|
||||
|
||||
if data.propertyLength != 0 {
|
||||
pos += data.propertyLength!
|
||||
}
|
||||
if pos > bytes.count {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MQTT 5.0: Topic Name may be empty only when Topic Alias is present.
|
||||
if data.topic.isEmpty && data.topicAlias == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// MQTT 5.0
|
||||
self.mqtt5Topic = data.topic
|
||||
self.topic = data.topic
|
||||
self.packetIdentifier = data.packetIdentifier
|
||||
if let packetIdentifier = data.packetIdentifier {
|
||||
self.msgid = packetIdentifier
|
||||
}
|
||||
self.publishRecProperties = data
|
||||
|
||||
} else {
|
||||
// MQTT 3.1.1
|
||||
guard topicLength > 0 else {
|
||||
return nil
|
||||
}
|
||||
guard let recTopic = String(bytes: bytes[topicStart..<topicEnd], encoding: .utf8) else {
|
||||
return nil
|
||||
}
|
||||
topic = recTopic
|
||||
}
|
||||
|
||||
// payload
|
||||
if pos == bytes.count {
|
||||
_payload = []
|
||||
} else if pos < bytes.count {
|
||||
_payload = [UInt8](bytes[pos..<bytes.count])
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
extension FramePublish: CustomStringConvertible {
|
||||
var description: String {
|
||||
return "PUBLISH(id: \(msgid), topic: \(topic), payload: \(_payload.summary))"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,163 @@
|
|||
//
|
||||
// FrameSubAck.swift
|
||||
// CocoaMQTT
|
||||
//
|
||||
// Created by JianBo on 2019/8/7.
|
||||
// Copyright © 2019 emqx.io. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// MQTT SUBACK packet
|
||||
struct FrameSubAck: Frame {
|
||||
|
||||
var packetFixedHeaderType: UInt8 = FrameType.suback.rawValue
|
||||
|
||||
// --- Attributes
|
||||
|
||||
var msgid: UInt16
|
||||
|
||||
var grantedQos: [CocoaMQTTQoS]
|
||||
|
||||
// --- Attributes End
|
||||
|
||||
// 3.9.2.1.2 Reason String
|
||||
public var reasonString: String?
|
||||
// 3.9.2.1.3 User Property
|
||||
public var userProperties: [String: String]?
|
||||
// 3.9.3 The order of Reason Codes in the SUBACK packet MUST match the order of Topic Filters in the SUBSCRIBE packet [MQTT-3.9.3-1].
|
||||
public var reasonCodes: [CocoaMQTTSUBACKReasonCode]?
|
||||
|
||||
// 3.9.2.1 SUBACK Properties
|
||||
public var subAckProperties: MqttDecodeSubAck?
|
||||
|
||||
init(msgid: UInt16, grantedQos: [CocoaMQTTQoS]) {
|
||||
self.msgid = msgid
|
||||
self.grantedQos = grantedQos
|
||||
}
|
||||
}
|
||||
|
||||
extension FrameSubAck {
|
||||
|
||||
func fixedHeader() -> [UInt8] {
|
||||
|
||||
var header = [UInt8]()
|
||||
header += [FrameType.suback.rawValue]
|
||||
|
||||
return header
|
||||
}
|
||||
|
||||
func variableHeader5() -> [UInt8] { return msgid.hlBytes }
|
||||
|
||||
func payload5() -> [UInt8] {
|
||||
|
||||
var payload = [UInt8]()
|
||||
|
||||
for qos in grantedQos {
|
||||
payload.append(qos.rawValue)
|
||||
}
|
||||
|
||||
return payload
|
||||
}
|
||||
|
||||
func properties() -> [UInt8] { return [] }
|
||||
|
||||
func allData() -> [UInt8] {
|
||||
|
||||
var allData = [UInt8]()
|
||||
|
||||
allData += fixedHeader()
|
||||
allData += variableHeader5()
|
||||
allData += properties()
|
||||
allData += payload5()
|
||||
|
||||
return allData
|
||||
}
|
||||
|
||||
func variableHeader() -> [UInt8] { return msgid.hlBytes }
|
||||
|
||||
func payload() -> [UInt8] {
|
||||
|
||||
var payload = [UInt8]()
|
||||
|
||||
for qos in grantedQos {
|
||||
payload.append(qos.rawValue)
|
||||
}
|
||||
|
||||
return payload
|
||||
}
|
||||
}
|
||||
|
||||
extension FrameSubAck: InitialWithBytes {
|
||||
|
||||
init?(packetFixedHeaderType: UInt8, bytes: [UInt8]) {
|
||||
|
||||
self.packetFixedHeaderType = packetFixedHeaderType
|
||||
|
||||
var protocolVersion = ""
|
||||
if let storage = CocoaMQTTStorage() {
|
||||
protocolVersion = storage.queryMQTTVersion()
|
||||
}
|
||||
|
||||
if protocolVersion == "5.0" {
|
||||
// the bytes length must bigger than 3
|
||||
guard bytes.count >= 4 else {
|
||||
return nil
|
||||
}
|
||||
|
||||
self.msgid = UInt16(bytes[0]) << 8 + UInt16(bytes[1])
|
||||
self.grantedQos = []
|
||||
self.reasonCodes = [CocoaMQTTSUBACKReasonCode]()
|
||||
|
||||
let propertyLength = decodeVariableByteInteger(data: bytes, offset: 2)
|
||||
let reasonCodesStartIndex = propertyLength.newOffset + propertyLength.res
|
||||
guard reasonCodesStartIndex < bytes.count else {
|
||||
return nil
|
||||
}
|
||||
|
||||
for i in reasonCodesStartIndex ..< bytes.count {
|
||||
guard let reasonCode = CocoaMQTTSUBACKReasonCode(rawValue: bytes[i]) else {
|
||||
return nil
|
||||
}
|
||||
self.reasonCodes! += [reasonCode]
|
||||
|
||||
switch reasonCode {
|
||||
case .grantedQoS0:
|
||||
self.grantedQos.append(.qos0)
|
||||
case .grantedQoS1:
|
||||
self.grantedQos.append(.qos1)
|
||||
case .grantedQoS2:
|
||||
self.grantedQos.append(.qos2)
|
||||
default:
|
||||
self.grantedQos.append(.FAILURE)
|
||||
}
|
||||
}
|
||||
|
||||
self.subAckProperties = MqttDecodeSubAck()
|
||||
self.subAckProperties!.decodeSubAck(fixedHeader: packetFixedHeaderType, pubAckData: bytes)
|
||||
|
||||
} else {
|
||||
// the bytes length must bigger than 3
|
||||
guard bytes.count >= 3 else {
|
||||
return nil
|
||||
}
|
||||
|
||||
self.msgid = UInt16(bytes[0]) << 8 + UInt16(bytes[1])
|
||||
self.grantedQos = []
|
||||
for i in 2 ..< bytes.count {
|
||||
guard let qos = CocoaMQTTQoS(rawValue: bytes[i]) else {
|
||||
return nil
|
||||
}
|
||||
self.grantedQos.append(qos)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
extension FrameSubAck: CustomStringConvertible {
|
||||
var description: String {
|
||||
return "SUBACK(id: \(msgid))"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,174 @@
|
|||
//
|
||||
// FrameSubscribe.swift
|
||||
// CocoaMQTT
|
||||
//
|
||||
// Created by JianBo on 2019/8/7.
|
||||
// Copyright © 2019 emqx.io. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// MQTT SUBSCRIBE Frame
|
||||
struct FrameSubscribe: Frame {
|
||||
|
||||
var packetFixedHeaderType: UInt8 = UInt8(FrameType.subscribe.rawValue + 2)
|
||||
|
||||
// --- Attributes
|
||||
|
||||
var msgid: UInt16?
|
||||
|
||||
var topics: [(String, CocoaMQTTQoS)]?
|
||||
|
||||
// --- Attributes End
|
||||
|
||||
// 3.8.2 SUBSCRIBE Variable Header
|
||||
public var packetIdentifier: UInt16?
|
||||
|
||||
// 3.8.2.1.2 Subscription Identifier
|
||||
public var subscriptionIdentifier: UInt32?
|
||||
|
||||
// 3.8.2.1.3 User Property
|
||||
public var userProperty: [String: String]?
|
||||
|
||||
// 3.8.3 SUBSCRIBE Payload
|
||||
public var topicFilters: [MqttSubscription]?
|
||||
|
||||
/// MQTT 3.1.1
|
||||
init(msgid: UInt16, topic: String, reqos: CocoaMQTTQoS) {
|
||||
self.init(msgid: msgid, topics: [(topic, reqos)])
|
||||
}
|
||||
|
||||
init(msgid: UInt16, topics: [(String, CocoaMQTTQoS)]) {
|
||||
packetFixedHeaderType = FrameType.subscribe.rawValue
|
||||
self.msgid = msgid
|
||||
self.topics = topics
|
||||
|
||||
qos = CocoaMQTTQoS.qos1
|
||||
}
|
||||
|
||||
/// MQTT 5.0
|
||||
init(msgid: UInt16, subscriptionList: [MqttSubscription]) {
|
||||
self.msgid = msgid
|
||||
self.topicFilters = subscriptionList
|
||||
}
|
||||
|
||||
/// MQTT 5.0
|
||||
init(msgid: UInt16, subscriptionList: [MqttSubscription], packetIdentifier: UInt16? = nil, subscriptionIdentifier: UInt32? = nil, userProperty: [String: String] = [:]) {
|
||||
self.msgid = msgid
|
||||
self.topicFilters = subscriptionList
|
||||
if packetIdentifier != nil {
|
||||
self.packetIdentifier = packetIdentifier
|
||||
}
|
||||
if subscriptionIdentifier != nil {
|
||||
self.subscriptionIdentifier = subscriptionIdentifier
|
||||
}
|
||||
if !userProperty.isEmpty {
|
||||
self.userProperty = userProperty
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension FrameSubscribe {
|
||||
|
||||
func fixedHeader() -> [UInt8] {
|
||||
|
||||
var header = [UInt8]()
|
||||
header += [FrameType.subscribe.rawValue]
|
||||
|
||||
return header
|
||||
}
|
||||
|
||||
func variableHeader5() -> [UInt8] {
|
||||
|
||||
// 3.8.2 SUBSCRIBE Variable Header
|
||||
// The Variable Header of the SUBSCRIBE Packet contains the following fields in the order: Packet Identifier, and Properties.
|
||||
|
||||
// MQTT 5.0
|
||||
var header = [UInt8]()
|
||||
header = msgid!.hlBytes
|
||||
header += beVariableByteInteger(length: self.properties().count)
|
||||
|
||||
return header
|
||||
}
|
||||
|
||||
func payload5() -> [UInt8] {
|
||||
|
||||
var payload = [UInt8]()
|
||||
|
||||
for subscription in self.topicFilters! {
|
||||
subscription.subscriptionOptions = true
|
||||
payload += subscription.subscriptionData
|
||||
}
|
||||
|
||||
return payload
|
||||
}
|
||||
|
||||
func properties() -> [UInt8] {
|
||||
|
||||
var properties = [UInt8]()
|
||||
|
||||
// 3.8.2.1.2 Subscription Identifier
|
||||
if let subscriptionIdentifier = self.subscriptionIdentifier,
|
||||
let subscriptionIdentifier = beVariableByteInteger(subscriptionIdentifier) {
|
||||
properties += getMQTTPropertyData(type: CocoaMQTTPropertyName.subscriptionIdentifier.rawValue, value: subscriptionIdentifier)
|
||||
}
|
||||
|
||||
// 3.8.2.1.3 User Property
|
||||
if let userProperty = self.userProperty {
|
||||
properties += userProperty.userPropertyBytes
|
||||
}
|
||||
|
||||
return properties
|
||||
|
||||
}
|
||||
|
||||
func allData() -> [UInt8] {
|
||||
|
||||
var allData = [UInt8]()
|
||||
|
||||
allData += fixedHeader()
|
||||
allData += variableHeader5()
|
||||
allData += properties()
|
||||
allData += payload5()
|
||||
|
||||
return allData
|
||||
}
|
||||
|
||||
func variableHeader() -> [UInt8] { return msgid!.hlBytes }
|
||||
|
||||
func payload() -> [UInt8] {
|
||||
|
||||
var payload = [UInt8]()
|
||||
|
||||
for (topic, qos) in topics! {
|
||||
payload += topic.bytesWithLength
|
||||
payload.append(qos.rawValue)
|
||||
}
|
||||
|
||||
return payload
|
||||
}
|
||||
}
|
||||
|
||||
extension FrameSubscribe: CustomStringConvertible {
|
||||
|
||||
var description: String {
|
||||
var protocolVersion = ""
|
||||
if let storage = CocoaMQTTStorage() {
|
||||
protocolVersion = storage.queryMQTTVersion()
|
||||
}
|
||||
|
||||
if protocolVersion == "5.0" {
|
||||
var desc = ""
|
||||
if let unwrappedList = topicFilters, !unwrappedList.isEmpty {
|
||||
for subscription in unwrappedList {
|
||||
desc += "SUBSCRIBE(id: \(String(describing: msgid)), topics: \(subscription.topic)) "
|
||||
}
|
||||
}
|
||||
return desc
|
||||
} else {
|
||||
return "SUBSCRIBE(id: \(String(describing: msgid)), topics: \(String(describing: topics)))"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
//
|
||||
// FrameUnsubAck.swift
|
||||
// CocoaMQTT
|
||||
//
|
||||
// Created by JianBo on 2019/8/7.
|
||||
// Copyright © 2019 emqx.io. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// MQTT UNSUBACK packet
|
||||
struct FrameUnsubAck: Frame {
|
||||
|
||||
var packetFixedHeaderType: UInt8 = FrameType.unsuback.rawValue
|
||||
|
||||
// --- Attributes
|
||||
|
||||
var msgid: UInt16
|
||||
|
||||
// --- Attributes End
|
||||
|
||||
// 3.10.2.1 UNSUBSCRIBE Properties
|
||||
public var unSubAckProperties: MqttDecodeUnsubAck?
|
||||
// 3.11.2 Property
|
||||
public var userProperty: [String: String]?
|
||||
// 3.11.2.1.2 Reason String
|
||||
public var reasonString: String?
|
||||
|
||||
var _payload: [UInt8] = []
|
||||
|
||||
init(msgid: UInt16, payload: [UInt8]) {
|
||||
self.msgid = msgid
|
||||
self._payload = payload
|
||||
}
|
||||
}
|
||||
|
||||
extension FrameUnsubAck {
|
||||
|
||||
func fixedHeader() -> [UInt8] {
|
||||
|
||||
var header = [UInt8]()
|
||||
header += [FrameType.unsuback.rawValue]
|
||||
|
||||
return header
|
||||
}
|
||||
|
||||
func variableHeader5() -> [UInt8] {
|
||||
|
||||
// 3.11.2 MSB+LSB
|
||||
var header = msgid.hlBytes
|
||||
|
||||
// MQTT 5.0
|
||||
header += beVariableByteInteger(length: self.properties().count)
|
||||
|
||||
return header
|
||||
}
|
||||
|
||||
func payload5() -> [UInt8] { return _payload }
|
||||
|
||||
func properties() -> [UInt8] {
|
||||
|
||||
var properties = [UInt8]()
|
||||
|
||||
// 3.11.2.1.2 Reason String
|
||||
if let reasonString = self.reasonString {
|
||||
properties += getMQTTPropertyData(type: CocoaMQTTPropertyName.reasonString.rawValue, value: reasonString.bytesWithLength)
|
||||
}
|
||||
|
||||
// 3.11.2.1.3 User Property
|
||||
if let userProperty = self.userProperty {
|
||||
properties += userProperty.userPropertyBytes
|
||||
}
|
||||
|
||||
return properties
|
||||
}
|
||||
|
||||
func allData() -> [UInt8] {
|
||||
|
||||
var allData = [UInt8]()
|
||||
|
||||
allData += fixedHeader()
|
||||
allData += variableHeader5()
|
||||
allData += properties()
|
||||
allData += payload5()
|
||||
|
||||
return allData
|
||||
}
|
||||
|
||||
func variableHeader() -> [UInt8] { return msgid.hlBytes }
|
||||
|
||||
func payload() -> [UInt8] { return [] }
|
||||
}
|
||||
|
||||
extension FrameUnsubAck: InitialWithBytes {
|
||||
|
||||
init?(packetFixedHeaderType: UInt8, bytes: [UInt8]) {
|
||||
guard packetFixedHeaderType == FrameType.unsuback.rawValue else {
|
||||
return nil
|
||||
}
|
||||
|
||||
guard bytes.count >= 2 else {
|
||||
return nil
|
||||
}
|
||||
|
||||
msgid = UInt16(bytes[0]) << 8 + UInt16(bytes[1])
|
||||
|
||||
self.unSubAckProperties = MqttDecodeUnsubAck()
|
||||
self.unSubAckProperties!.decodeUnSubAck(fixedHeader: packetFixedHeaderType, pubAckData: bytes)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
extension FrameUnsubAck: CustomStringConvertible {
|
||||
var description: String {
|
||||
return "UNSUBSACK(id: \(msgid))"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
//
|
||||
// FrameUnsubscribe.swift
|
||||
// CocoaMQTT
|
||||
//
|
||||
// Created by JianBo on 2019/8/7.
|
||||
// Copyright © 2019 emqx.io. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// MQTT UNSUBSCRIBE packet
|
||||
struct FrameUnsubscribe: Frame {
|
||||
|
||||
var packetFixedHeaderType: UInt8 = UInt8(FrameType.unsubscribe.rawValue + 2)
|
||||
|
||||
// --- Attributes
|
||||
|
||||
var msgid: UInt16?
|
||||
|
||||
/// MQTT 3.1.1
|
||||
var topics: [String]?
|
||||
|
||||
/// MQTT 5.0
|
||||
var topicFilters: [MqttSubscription]?
|
||||
|
||||
// --- Attribetes end
|
||||
|
||||
// 3.10.2.1.2 User Property
|
||||
public var userProperty: [String: String]?
|
||||
|
||||
/// MQTT 3.1.1
|
||||
init(msgid: UInt16, topics: [String]) {
|
||||
self.msgid = msgid
|
||||
self.topics = topics
|
||||
|
||||
qos = CocoaMQTTQoS.qos1
|
||||
}
|
||||
|
||||
/// MQTT 5.0
|
||||
init(msgid: UInt16, topics: [MqttSubscription]) {
|
||||
self.msgid = msgid
|
||||
self.topicFilters = topics
|
||||
|
||||
qos = CocoaMQTTQoS.qos1
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension FrameUnsubscribe {
|
||||
|
||||
func fixedHeader() -> [UInt8] {
|
||||
|
||||
var header = [UInt8]()
|
||||
header += [FrameType.unsubscribe.rawValue]
|
||||
|
||||
return header
|
||||
}
|
||||
|
||||
func variableHeader5() -> [UInt8] {
|
||||
|
||||
// MQTT 5.0
|
||||
var header = [UInt8]()
|
||||
header = msgid!.hlBytes
|
||||
header += beVariableByteInteger(length: self.properties().count)
|
||||
return header
|
||||
}
|
||||
|
||||
func payload5() -> [UInt8] {
|
||||
|
||||
var payload = [UInt8]()
|
||||
|
||||
for subscription in topicFilters! {
|
||||
subscription.subscriptionOptions = false
|
||||
payload += subscription.subscriptionData
|
||||
}
|
||||
|
||||
return payload
|
||||
}
|
||||
|
||||
func properties() -> [UInt8] {
|
||||
|
||||
var properties = [UInt8]()
|
||||
|
||||
// 3.10.2.1.2 User Property
|
||||
if let userProperty = self.userProperty {
|
||||
properties += userProperty.userPropertyBytes
|
||||
}
|
||||
|
||||
return properties
|
||||
}
|
||||
|
||||
func allData() -> [UInt8] {
|
||||
|
||||
var allData = [UInt8]()
|
||||
|
||||
allData += fixedHeader()
|
||||
allData += variableHeader5()
|
||||
allData += properties()
|
||||
allData += payload5()
|
||||
|
||||
return allData
|
||||
}
|
||||
|
||||
func variableHeader() -> [UInt8] { return msgid!.hlBytes }
|
||||
|
||||
func payload() -> [UInt8] {
|
||||
|
||||
var payload = [UInt8]()
|
||||
|
||||
for t in topics! {
|
||||
payload += t.bytesWithLength
|
||||
}
|
||||
|
||||
return payload
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
extension FrameUnsubscribe: CustomStringConvertible {
|
||||
|
||||
var description: String {
|
||||
var protocolVersion = ""
|
||||
if let storage = CocoaMQTTStorage() {
|
||||
protocolVersion = storage.queryMQTTVersion()
|
||||
}
|
||||
|
||||
if protocolVersion == "5.0" {
|
||||
var desc = ""
|
||||
if let unwrappedList = topicFilters, !unwrappedList.isEmpty {
|
||||
for subscription in unwrappedList {
|
||||
desc += "UNSUBSCRIBE(id: \(String(describing: subscription.topic)), topics: \(subscription.topic)) "
|
||||
}
|
||||
}
|
||||
return desc
|
||||
} else {
|
||||
return "UNSUBSCRIBE(id: \(String(describing: msgid)), topics: \(String(describing: topics)))"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
//
|
||||
// MqttAuthProperties.swift
|
||||
// CocoaMQTT
|
||||
//
|
||||
// Created by liwei wang on 1/9/2021.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public class MqttAuthProperties: NSObject {
|
||||
|
||||
// 3.15.2.2.2 Authentication Method
|
||||
public var authenticationMethod: String?
|
||||
// 3.15.2.2.3 Authentication Data
|
||||
public var authenticationData: [UInt8]?
|
||||
// 3.15.2.2.4 Reason String
|
||||
public var reasonString: String?
|
||||
// 3.15.2.2.5 User Property
|
||||
public var userProperties: [String: String]?
|
||||
|
||||
public var properties: [UInt8] {
|
||||
var properties = [UInt8]()
|
||||
|
||||
// 3.15.2.2.2 Authentication Method
|
||||
if let authenticationMethod = self.authenticationMethod {
|
||||
properties += getMQTTPropertyData(type: CocoaMQTTPropertyName.authenticationMethod.rawValue, value: authenticationMethod.bytesWithLength)
|
||||
}
|
||||
// 3.15.2.2.3 Authentication Data
|
||||
if let authenticationData = self.authenticationData {
|
||||
properties += getMQTTPropertyData(type: CocoaMQTTPropertyName.authenticationData.rawValue, value: authenticationData)
|
||||
}
|
||||
// 3.15.2.2.4 Reason String
|
||||
if let reasonString = self.reasonString {
|
||||
properties += getMQTTPropertyData(type: CocoaMQTTPropertyName.reasonString.rawValue, value: reasonString.bytesWithLength)
|
||||
}
|
||||
// 3.15.2.2.5 User Property
|
||||
if let userProperty = self.userProperties {
|
||||
properties += userProperty.userPropertyBytes
|
||||
}
|
||||
|
||||
return properties
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
//
|
||||
// MqttConnectProperties.swift
|
||||
// CocoaMQTT
|
||||
//
|
||||
// Created by liwei wang on 2021/7/25.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public class MqttConnectProperties: NSObject {
|
||||
|
||||
// 3.1.2.11.1 Property Length
|
||||
// public var propertyLength: UInt8?
|
||||
// 3.1.2.11.2 Session Expiry Interval
|
||||
public var sessionExpiryInterval: UInt32?
|
||||
// 3.1.2.11.3 Receive Maximum
|
||||
public var receiveMaximum: UInt16?
|
||||
// 3.1.2.11.4 Maximum Packet Size
|
||||
public var maximumPacketSize: UInt32?
|
||||
// 3.1.2.11.5 Topic Alias Maximum
|
||||
public var topicAliasMaximum: UInt16?
|
||||
// 3.1.2.11.6 Request Response Information
|
||||
public var requestResponseInformation: UInt8?
|
||||
// 3.1.2.11.7 Request Problem Information
|
||||
public var requestProblemInfomation: UInt8?
|
||||
// 3.1.2.11.8 User Property
|
||||
public var userProperties: [String: String]?
|
||||
// 3.1.2.11.9 Authentication Method
|
||||
public var authenticationMethod: String?
|
||||
// 3.1.2.11.10 Authentication Data
|
||||
public var authenticationData: [UInt8]?
|
||||
|
||||
public var properties: [UInt8] {
|
||||
var properties = [UInt8]()
|
||||
|
||||
// 3.1.2.11.2 Session Expiry Interval
|
||||
if let sessionExpiryInterval = self.sessionExpiryInterval {
|
||||
properties += getMQTTPropertyData(type: CocoaMQTTPropertyName.sessionExpiryInterval.rawValue, value: sessionExpiryInterval.byteArrayLittleEndian)
|
||||
}
|
||||
|
||||
// 3.1.2.11.3 Receive Maximum
|
||||
if let receiveMaximum = self.receiveMaximum {
|
||||
properties += getMQTTPropertyData(type: CocoaMQTTPropertyName.receiveMaximum.rawValue, value: receiveMaximum.hlBytes)
|
||||
}
|
||||
|
||||
// 3.1.2.11.4 Maximum Packet Size
|
||||
if let maximumPacketSize = self.maximumPacketSize {
|
||||
properties += getMQTTPropertyData(type: CocoaMQTTPropertyName.maximumPacketSize.rawValue, value: maximumPacketSize.byteArrayLittleEndian)
|
||||
}
|
||||
|
||||
// 3.1.2.11.5 Topic Alias Maximum
|
||||
if let topicAliasMaximum = self.topicAliasMaximum {
|
||||
properties += getMQTTPropertyData(type: CocoaMQTTPropertyName.topicAliasMaximum.rawValue, value: topicAliasMaximum.hlBytes)
|
||||
}
|
||||
|
||||
// 3.1.2.11.6 Request Response Information
|
||||
if let requestResponseInformation = self.requestResponseInformation {
|
||||
properties += getMQTTPropertyData(type: CocoaMQTTPropertyName.requestResponseInformation.rawValue, value: [requestResponseInformation])
|
||||
}
|
||||
// 3.1.2.11.7 Request Problem Information
|
||||
if let requestProblemInfomation = self.requestProblemInfomation {
|
||||
properties += getMQTTPropertyData(type: CocoaMQTTPropertyName.requestProblemInformation.rawValue, value: [requestProblemInfomation])
|
||||
}
|
||||
// 3.1.2.11.8 User Property
|
||||
if let userProperty = self.userProperties {
|
||||
properties += userProperty.userPropertyBytes
|
||||
}
|
||||
// 3.1.2.11.9 Authentication Method
|
||||
if let authenticationMethod = self.authenticationMethod {
|
||||
properties += getMQTTPropertyData(type: CocoaMQTTPropertyName.authenticationMethod.rawValue, value: authenticationMethod.bytesWithLength)
|
||||
}
|
||||
// 3.1.2.11.10 Authentication Data
|
||||
if let authenticationData = self.authenticationData {
|
||||
properties += authenticationData
|
||||
}
|
||||
|
||||
return properties
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,237 @@
|
|||
//
|
||||
// MqttDecodeConnAck.swift
|
||||
// CocoaMQTT
|
||||
//
|
||||
// Created by liwei wang on 2021/7/26.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public class MqttDecodeConnAck: NSObject {
|
||||
|
||||
// var connackData: [UInt8]
|
||||
//
|
||||
// init(connackData: [UInt8]) {
|
||||
// connackData = connackData
|
||||
// }
|
||||
|
||||
// 3.2.2.3 CONNACK Properties
|
||||
// 3.2.2.3.1 Property Length
|
||||
public var propertyLength: Int?
|
||||
// 3.2.2.3.2 Session Expiry Interval
|
||||
public var sessionExpiryInterval: UInt32?
|
||||
// 3.2.2.3.3 Receive Maximum
|
||||
public var receiveMaximum: UInt16?
|
||||
// 3.2.2.3.4 Maximum QoS
|
||||
public var maximumQoS: CocoaMQTTQoS?
|
||||
// 3.2.2.3.5 Retain Available
|
||||
public var retainAvailable: Bool?
|
||||
// 3.2.2.3.6 Maximum Packet Size
|
||||
public var maximumPacketSize: UInt32?
|
||||
// 3.2.2.3.7 Assigned Client Identifier
|
||||
public var assignedClientIdentifier: String?
|
||||
// 3.2.2.3.8 Topic Alias Maximum
|
||||
public var topicAliasMaximum: UInt16?
|
||||
// 3.2.2.3.9 Reason String
|
||||
public var reasonString: String?
|
||||
// 3.2.2.3.10 User Property
|
||||
public var userProperty: [String: String]?
|
||||
// 3.2.2.3.11 Wildcard Subscription Available
|
||||
public var wildcardSubscriptionAvailable: Bool?
|
||||
// 3.2.2.3.12 Subscription Identifiers Available
|
||||
public var subscriptionIdentifiersAvailable: Bool?
|
||||
// 3.2.2.3.13 Shared Subscription Available
|
||||
public var sharedSubscriptionAvailable: Bool?
|
||||
// 3.2.2.3.14 Server Keep Alive
|
||||
public var serverKeepAlive: UInt16?
|
||||
// 3.2.2.3.15 Response Information
|
||||
public var responseInformation: String?
|
||||
// 3.2.2.3.16 Server Reference
|
||||
public var serverReference: String?
|
||||
// 3.2.2.3.17 Authentication Method
|
||||
public var authenticationMethod: String?
|
||||
// 3.2.2.3.18 Authentication Data
|
||||
public var authenticationData = [UInt8]()
|
||||
|
||||
public func properties(connackData: [UInt8]) {
|
||||
// 3.2.2.3 CONNACK Properties
|
||||
var index = 2 // sessPresent 0 reasonCode 1
|
||||
let propertyLengthVariableByteInteger = decodeVariableByteInteger(data: connackData, offset: index)
|
||||
propertyLength = propertyLengthVariableByteInteger.res
|
||||
index = propertyLengthVariableByteInteger.newOffset
|
||||
let occupyIndex = index
|
||||
|
||||
var protocolVersion = ""
|
||||
if let storage = CocoaMQTTStorage() {
|
||||
protocolVersion = storage.queryMQTTVersion()
|
||||
}
|
||||
|
||||
if protocolVersion == "5.0" {
|
||||
// properties
|
||||
while index - occupyIndex < propertyLength! {
|
||||
let resVariableByteInteger = decodeVariableByteInteger(data: connackData, offset: index)
|
||||
index = resVariableByteInteger.newOffset
|
||||
let propertyNameByte = resVariableByteInteger.res
|
||||
guard let propertyName = CocoaMQTTPropertyName(rawValue: UInt8(propertyNameByte)) else {
|
||||
break
|
||||
}
|
||||
|
||||
switch propertyName.rawValue {
|
||||
|
||||
case CocoaMQTTPropertyName.sessionExpiryInterval.rawValue:
|
||||
|
||||
let comRes = integerCompute(data: connackData, formatType: formatInt.formatUint32.rawValue, offset: index)
|
||||
sessionExpiryInterval = UInt32(comRes!.res)
|
||||
index = comRes!.newOffset
|
||||
|
||||
case CocoaMQTTPropertyName.receiveMaximum.rawValue:
|
||||
|
||||
let comRes = integerCompute(data: connackData, formatType: formatInt.formatUint16.rawValue, offset: index)
|
||||
receiveMaximum = UInt16(comRes!.res)
|
||||
index = comRes!.newOffset
|
||||
|
||||
case CocoaMQTTPropertyName.maximumQoS.rawValue:
|
||||
if index > connackData.count {
|
||||
break
|
||||
}
|
||||
if connackData[index] & 0x01 > 0 {
|
||||
maximumQoS = .qos0
|
||||
} else {
|
||||
maximumQoS = .qos1
|
||||
}
|
||||
|
||||
index += 1
|
||||
|
||||
case CocoaMQTTPropertyName.retainAvailable.rawValue:
|
||||
if index > connackData.count {
|
||||
break
|
||||
}
|
||||
if connackData[index] & 0x01 > 0 {
|
||||
retainAvailable = true
|
||||
} else {
|
||||
retainAvailable = false
|
||||
}
|
||||
|
||||
index += 1
|
||||
|
||||
case CocoaMQTTPropertyName.maximumPacketSize.rawValue:
|
||||
|
||||
let comRes = integerCompute(data: connackData, formatType: formatInt.formatUint32.rawValue, offset: index)
|
||||
maximumPacketSize = UInt32(comRes!.res)
|
||||
index = comRes!.newOffset
|
||||
|
||||
case CocoaMQTTPropertyName.assignedClientIdentifier.rawValue:
|
||||
guard let result = unsignedByteToString(data: connackData, offset: index) else {
|
||||
break
|
||||
}
|
||||
assignedClientIdentifier = result.resStr
|
||||
index = result.newOffset
|
||||
|
||||
case CocoaMQTTPropertyName.topicAliasMaximum.rawValue:
|
||||
|
||||
let comRes = integerCompute(data: connackData, formatType: formatInt.formatUint16.rawValue, offset: index)
|
||||
topicAliasMaximum = UInt16(comRes!.res)
|
||||
index = comRes!.newOffset
|
||||
|
||||
case CocoaMQTTPropertyName.reasonString.rawValue:
|
||||
guard let result = unsignedByteToString(data: connackData, offset: index) else {
|
||||
break
|
||||
}
|
||||
reasonString = result.resStr
|
||||
index = result.newOffset
|
||||
|
||||
case CocoaMQTTPropertyName.userProperty.rawValue:
|
||||
var key: String?
|
||||
var value: String?
|
||||
guard let keyRes = unsignedByteToString(data: connackData, offset: index) else {
|
||||
break
|
||||
}
|
||||
key = keyRes.resStr
|
||||
index = keyRes.newOffset
|
||||
|
||||
guard let valRes = unsignedByteToString(data: connackData, offset: index) else {
|
||||
break
|
||||
}
|
||||
value = valRes.resStr
|
||||
index = valRes.newOffset
|
||||
|
||||
userProperty![key!] = value
|
||||
|
||||
case CocoaMQTTPropertyName.wildcardSubscriptionAvailable.rawValue:
|
||||
if index > connackData.count {
|
||||
break
|
||||
}
|
||||
if connackData[index] & 0x01 > 0 {
|
||||
wildcardSubscriptionAvailable = true
|
||||
} else {
|
||||
wildcardSubscriptionAvailable = false
|
||||
}
|
||||
index += 1
|
||||
|
||||
case CocoaMQTTPropertyName.subscriptionIdentifiersAvailable.rawValue:
|
||||
if index > connackData.count {
|
||||
break
|
||||
}
|
||||
if connackData[index] & 0x01 > 0 {
|
||||
subscriptionIdentifiersAvailable = true
|
||||
} else {
|
||||
subscriptionIdentifiersAvailable = false
|
||||
}
|
||||
index += 1
|
||||
|
||||
case CocoaMQTTPropertyName.sharedSubscriptionAvailable.rawValue:
|
||||
if index > connackData.count {
|
||||
break
|
||||
}
|
||||
if connackData[index] & 0x01 > 0 {
|
||||
sharedSubscriptionAvailable = true
|
||||
} else {
|
||||
sharedSubscriptionAvailable = false
|
||||
}
|
||||
index += 1
|
||||
|
||||
case CocoaMQTTPropertyName.serverKeepAlive.rawValue:
|
||||
|
||||
let comRes = integerCompute(data: connackData, formatType: formatInt.formatUint16.rawValue, offset: index)
|
||||
serverKeepAlive = UInt16(comRes!.res)
|
||||
index = comRes!.newOffset
|
||||
|
||||
case CocoaMQTTPropertyName.responseInformation.rawValue:
|
||||
guard let valRes = unsignedByteToString(data: connackData, offset: index) else {
|
||||
break
|
||||
}
|
||||
responseInformation = valRes.resStr
|
||||
index = valRes.newOffset
|
||||
|
||||
case CocoaMQTTPropertyName.serverReference.rawValue:
|
||||
guard let valRes = unsignedByteToString(data: connackData, offset: index) else {
|
||||
break
|
||||
}
|
||||
serverReference = valRes.resStr
|
||||
index = valRes.newOffset
|
||||
|
||||
case CocoaMQTTPropertyName.authenticationMethod.rawValue:
|
||||
guard let valRes = unsignedByteToString(data: connackData, offset: index) else {
|
||||
break
|
||||
}
|
||||
authenticationMethod = valRes.resStr
|
||||
index = valRes.newOffset
|
||||
|
||||
case CocoaMQTTPropertyName.authenticationData.rawValue:
|
||||
guard let valRes = unsignedByteToBinary(data: connackData, offset: index) else {
|
||||
break
|
||||
}
|
||||
authenticationData = valRes.resStr
|
||||
index = valRes.newOffset
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
//
|
||||
// MqttDecodePuback.swift
|
||||
// CocoaMQTT
|
||||
//
|
||||
// Created by liwei wang on 2021/8/3.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public class MqttDecodePubAck: NSObject {
|
||||
|
||||
var totalCount = 0
|
||||
var dataIndex = 0
|
||||
var propertyLength: Int = 0
|
||||
|
||||
public var reasonCode: CocoaMQTTPUBACKReasonCode?
|
||||
public var msgid: UInt16 = 0
|
||||
public var reasonString: String?
|
||||
public var userProperty: [String: String]?
|
||||
|
||||
public func decodePubAck(fixedHeader: UInt8, pubAckData: [UInt8]) {
|
||||
totalCount = pubAckData.count
|
||||
dataIndex = 0
|
||||
// msgid
|
||||
let msgidResult = integerCompute(data: pubAckData, formatType: formatInt.formatUint16.rawValue, offset: dataIndex)
|
||||
msgid = UInt16(msgidResult!.res)
|
||||
dataIndex = msgidResult!.newOffset
|
||||
|
||||
// 3.4.2.1 PUBACK Reason Code
|
||||
|
||||
// The Reason Code and Property Length can be omitted if the Reason Code is 0x00 (Success) and there are no Properties. In this case the PUBACK has a Remaining Length of 2.
|
||||
if dataIndex + 1 > pubAckData.count {
|
||||
return
|
||||
}
|
||||
|
||||
guard let ack = CocoaMQTTPUBACKReasonCode(rawValue: pubAckData[dataIndex]) else {
|
||||
return
|
||||
}
|
||||
reasonCode = ack
|
||||
dataIndex += 1
|
||||
|
||||
var protocolVersion = ""
|
||||
if let storage = CocoaMQTTStorage() {
|
||||
protocolVersion = storage.queryMQTTVersion()
|
||||
}
|
||||
|
||||
if protocolVersion == "5.0" {
|
||||
// 3.4.2.2 PUBACK Properties
|
||||
// 3.4.2.2.1 Property Length
|
||||
let propertyLengthVariableByteInteger = decodeVariableByteInteger(data: pubAckData, offset: dataIndex)
|
||||
propertyLength = propertyLengthVariableByteInteger.res
|
||||
dataIndex = propertyLengthVariableByteInteger.newOffset
|
||||
|
||||
let occupyIndex = dataIndex
|
||||
|
||||
while dataIndex < occupyIndex + propertyLength {
|
||||
|
||||
let resVariableByteInteger = decodeVariableByteInteger(data: pubAckData, offset: dataIndex)
|
||||
dataIndex = resVariableByteInteger.newOffset
|
||||
let propertyNameByte = resVariableByteInteger.res
|
||||
guard let propertyName = CocoaMQTTPropertyName(rawValue: UInt8(propertyNameByte)) else {
|
||||
break
|
||||
}
|
||||
|
||||
switch propertyName.rawValue {
|
||||
// 3.4.2.2.2 Reason String
|
||||
case CocoaMQTTPropertyName.reasonString.rawValue:
|
||||
guard let result = unsignedByteToString(data: pubAckData, offset: dataIndex) else {
|
||||
break
|
||||
}
|
||||
reasonString = result.resStr
|
||||
dataIndex = result.newOffset
|
||||
|
||||
// 3.4.2.2.3 User Property
|
||||
case CocoaMQTTPropertyName.userProperty.rawValue:
|
||||
var key: String?
|
||||
var value: String?
|
||||
guard let keyRes = unsignedByteToString(data: pubAckData, offset: dataIndex) else {
|
||||
break
|
||||
}
|
||||
key = keyRes.resStr
|
||||
dataIndex = keyRes.newOffset
|
||||
|
||||
guard let valRes = unsignedByteToString(data: pubAckData, offset: dataIndex) else {
|
||||
break
|
||||
}
|
||||
value = valRes.resStr
|
||||
dataIndex = valRes.newOffset
|
||||
|
||||
userProperty![key!] = value
|
||||
|
||||
default:
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
//
|
||||
// MqttDecodePubComp.swift
|
||||
// CocoaMQTT
|
||||
//
|
||||
// Created by liwei wang on 2021/8/9.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public class MqttDecodePubComp: NSObject {
|
||||
|
||||
var totalCount = 0
|
||||
var dataIndex = 0
|
||||
var propertyLength: Int = 0
|
||||
|
||||
public var reasonCode: CocoaMQTTPUBCOMPReasonCode?
|
||||
public var msgid: UInt16 = 0
|
||||
public var reasonString: String?
|
||||
public var userProperty: [String: String]?
|
||||
|
||||
public func decodePubComp(fixedHeader: UInt8, pubAckData: [UInt8]) {
|
||||
totalCount = pubAckData.count
|
||||
dataIndex = 0
|
||||
// msgid
|
||||
let msgidResult = integerCompute(data: pubAckData, formatType: formatInt.formatUint16.rawValue, offset: dataIndex)
|
||||
msgid = UInt16(msgidResult!.res)
|
||||
dataIndex = msgidResult!.newOffset
|
||||
|
||||
// 3.6.2.1 PUBREL Reason Code
|
||||
|
||||
// The Reason Code and Property Length can be omitted if the Reason Code is 0x00 (Success) and there are no Properties. In this case the PUBACK has a Remaining Length of 2.
|
||||
if dataIndex + 1 > pubAckData.count {
|
||||
return
|
||||
}
|
||||
|
||||
guard let ack = CocoaMQTTPUBCOMPReasonCode(rawValue: pubAckData[dataIndex]) else {
|
||||
return
|
||||
}
|
||||
reasonCode = ack
|
||||
dataIndex += 1
|
||||
|
||||
var protocolVersion = ""
|
||||
if let storage = CocoaMQTTStorage() {
|
||||
protocolVersion = storage.queryMQTTVersion()
|
||||
}
|
||||
|
||||
if protocolVersion == "5.0" {
|
||||
// 3.6.2.2 PUBREL Properties
|
||||
// 3.6.2.2.1 Property Length
|
||||
let propertyLengthVariableByteInteger = decodeVariableByteInteger(data: pubAckData, offset: dataIndex)
|
||||
propertyLength = propertyLengthVariableByteInteger.res
|
||||
dataIndex = propertyLengthVariableByteInteger.newOffset
|
||||
|
||||
let occupyIndex = dataIndex
|
||||
|
||||
while dataIndex < occupyIndex + propertyLength {
|
||||
|
||||
let resVariableByteInteger = decodeVariableByteInteger(data: pubAckData, offset: dataIndex)
|
||||
dataIndex = resVariableByteInteger.newOffset
|
||||
let propertyNameByte = resVariableByteInteger.res
|
||||
guard let propertyName = CocoaMQTTPropertyName(rawValue: UInt8(propertyNameByte)) else {
|
||||
break
|
||||
}
|
||||
|
||||
switch propertyName.rawValue {
|
||||
// 3.6.2.2.2 Reason String
|
||||
case CocoaMQTTPropertyName.reasonString.rawValue:
|
||||
guard let result = unsignedByteToString(data: pubAckData, offset: dataIndex) else {
|
||||
break
|
||||
}
|
||||
reasonString = result.resStr
|
||||
dataIndex = result.newOffset
|
||||
|
||||
// 3.6.2.2.3 User Property
|
||||
var key: String?
|
||||
var value: String?
|
||||
guard let keyRes = unsignedByteToString(data: pubAckData, offset: dataIndex) else {
|
||||
break
|
||||
}
|
||||
key = keyRes.resStr
|
||||
dataIndex = keyRes.newOffset
|
||||
|
||||
guard let valRes = unsignedByteToString(data: pubAckData, offset: dataIndex) else {
|
||||
break
|
||||
}
|
||||
value = valRes.resStr
|
||||
dataIndex = valRes.newOffset
|
||||
|
||||
userProperty![key!] = value
|
||||
|
||||
default:
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
//
|
||||
// MqttDecodePubRec.swift
|
||||
// CocoaMQTT
|
||||
//
|
||||
// Created by liwei wang on 2021/8/4.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public class MqttDecodePubRec: NSObject {
|
||||
|
||||
var totalCount = 0
|
||||
var dataIndex = 0
|
||||
var propertyLength: Int = 0
|
||||
|
||||
public var reasonCode: CocoaMQTTPUBACKReasonCode?
|
||||
public var msgid: UInt16 = 0
|
||||
public var reasonString: String?
|
||||
public var userProperty: [String: String]?
|
||||
|
||||
public func decodePubRec(fixedHeader: UInt8, pubAckData: [UInt8]) {
|
||||
totalCount = pubAckData.count
|
||||
dataIndex = 0
|
||||
// msgid
|
||||
let msgidResult = integerCompute(data: pubAckData, formatType: formatInt.formatUint16.rawValue, offset: dataIndex)
|
||||
msgid = UInt16(msgidResult!.res)
|
||||
dataIndex = msgidResult!.newOffset
|
||||
|
||||
// 3.5.2.1 PUBREC Reason Code
|
||||
|
||||
// The Reason Code and Property Length can be omitted if the Reason Code is 0x00 (Success) and there are no Properties. In this case the PUBACK has a Remaining Length of 2.
|
||||
if dataIndex + 1 > pubAckData.count {
|
||||
return
|
||||
}
|
||||
|
||||
guard let ack = CocoaMQTTPUBACKReasonCode(rawValue: pubAckData[dataIndex]) else {
|
||||
return
|
||||
}
|
||||
reasonCode = ack
|
||||
dataIndex += 1
|
||||
|
||||
var protocolVersion = ""
|
||||
if let storage = CocoaMQTTStorage() {
|
||||
protocolVersion = storage.queryMQTTVersion()
|
||||
}
|
||||
|
||||
if protocolVersion == "5.0" {
|
||||
// 3.5.2.2 PUBACK Properties
|
||||
// 3.5.2.2.1 Property Length
|
||||
let propertyLengthVariableByteInteger = decodeVariableByteInteger(data: pubAckData, offset: dataIndex)
|
||||
propertyLength = propertyLengthVariableByteInteger.res
|
||||
dataIndex = propertyLengthVariableByteInteger.newOffset
|
||||
|
||||
let occupyIndex = dataIndex
|
||||
|
||||
while dataIndex < occupyIndex + propertyLength {
|
||||
|
||||
let resVariableByteInteger = decodeVariableByteInteger(data: pubAckData, offset: dataIndex)
|
||||
dataIndex = resVariableByteInteger.newOffset
|
||||
let propertyNameByte = resVariableByteInteger.res
|
||||
guard let propertyName = CocoaMQTTPropertyName(rawValue: UInt8(propertyNameByte)) else {
|
||||
break
|
||||
}
|
||||
|
||||
switch propertyName.rawValue {
|
||||
// 3.5.2.2.2 Reason String
|
||||
case CocoaMQTTPropertyName.reasonString.rawValue:
|
||||
guard let result = unsignedByteToString(data: pubAckData, offset: dataIndex) else {
|
||||
break
|
||||
}
|
||||
reasonString = result.resStr
|
||||
dataIndex = result.newOffset
|
||||
|
||||
// 3.5.2.2.3 User Property
|
||||
case CocoaMQTTPropertyName.userProperty.rawValue:
|
||||
var key: String?
|
||||
var value: String?
|
||||
guard let keyRes = unsignedByteToString(data: pubAckData, offset: dataIndex) else {
|
||||
break
|
||||
}
|
||||
key = keyRes.resStr
|
||||
dataIndex = keyRes.newOffset
|
||||
|
||||
guard let valRes = unsignedByteToString(data: pubAckData, offset: dataIndex) else {
|
||||
break
|
||||
}
|
||||
value = valRes.resStr
|
||||
dataIndex = valRes.newOffset
|
||||
|
||||
userProperty![key!] = value
|
||||
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
//
|
||||
// MqttDecodePubRel.swift
|
||||
// CocoaMQTT
|
||||
//
|
||||
// Created by liwei wang on 2021/8/9.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public class MqttDecodePubRel: NSObject {
|
||||
|
||||
var totalCount = 0
|
||||
var dataIndex = 0
|
||||
var propertyLength: Int = 0
|
||||
|
||||
public var reasonCode: CocoaMQTTPUBRELReasonCode?
|
||||
public var msgid: UInt16 = 0
|
||||
public var reasonString: String?
|
||||
public var userProperty: [String: String]?
|
||||
|
||||
public func decodePubRel(fixedHeader: UInt8, pubAckData: [UInt8]) {
|
||||
totalCount = pubAckData.count
|
||||
dataIndex = 0
|
||||
// msgid
|
||||
let msgidResult = integerCompute(data: pubAckData, formatType: formatInt.formatUint16.rawValue, offset: dataIndex)
|
||||
msgid = UInt16(msgidResult!.res)
|
||||
dataIndex = msgidResult!.newOffset
|
||||
|
||||
// 3.6.2.1 PUBREL Reason Code
|
||||
|
||||
// The Reason Code and Property Length can be omitted if the Reason Code is 0x00 (Success) and there are no Properties. In this case the PUBACK has a Remaining Length of 2.
|
||||
if dataIndex + 1 > pubAckData.count {
|
||||
return
|
||||
}
|
||||
|
||||
guard let ack = CocoaMQTTPUBRELReasonCode(rawValue: pubAckData[dataIndex]) else {
|
||||
return
|
||||
}
|
||||
reasonCode = ack
|
||||
dataIndex += 1
|
||||
|
||||
var protocolVersion = ""
|
||||
if let storage = CocoaMQTTStorage() {
|
||||
protocolVersion = storage.queryMQTTVersion()
|
||||
}
|
||||
|
||||
if protocolVersion == "5.0" {
|
||||
// 3.6.2.2 PUBREL Properties
|
||||
// 3.6.2.2.1 Property Length
|
||||
let propertyLengthVariableByteInteger = decodeVariableByteInteger(data: pubAckData, offset: dataIndex)
|
||||
propertyLength = propertyLengthVariableByteInteger.res
|
||||
dataIndex = propertyLengthVariableByteInteger.newOffset
|
||||
|
||||
let occupyIndex = dataIndex
|
||||
|
||||
while dataIndex < occupyIndex + propertyLength {
|
||||
|
||||
let resVariableByteInteger = decodeVariableByteInteger(data: pubAckData, offset: dataIndex)
|
||||
dataIndex = resVariableByteInteger.newOffset
|
||||
let propertyNameByte = resVariableByteInteger.res
|
||||
guard let propertyName = CocoaMQTTPropertyName(rawValue: UInt8(propertyNameByte)) else {
|
||||
break
|
||||
}
|
||||
|
||||
switch propertyName.rawValue {
|
||||
// 3.6.2.2.2 Reason String
|
||||
case CocoaMQTTPropertyName.reasonString.rawValue:
|
||||
guard let result = unsignedByteToString(data: pubAckData, offset: dataIndex) else {
|
||||
break
|
||||
}
|
||||
reasonString = result.resStr
|
||||
dataIndex = result.newOffset
|
||||
|
||||
// 3.6.2.2.3 User Property
|
||||
var key: String?
|
||||
var value: String?
|
||||
guard let keyRes = unsignedByteToString(data: pubAckData, offset: dataIndex) else {
|
||||
break
|
||||
}
|
||||
key = keyRes.resStr
|
||||
dataIndex = keyRes.newOffset
|
||||
|
||||
guard let valRes = unsignedByteToString(data: pubAckData, offset: dataIndex) else {
|
||||
break
|
||||
}
|
||||
value = valRes.resStr
|
||||
dataIndex = valRes.newOffset
|
||||
|
||||
userProperty![key!] = value
|
||||
|
||||
default:
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,171 @@
|
|||
//
|
||||
// MqttDecodePublish.swift
|
||||
// CocoaMQTT
|
||||
//
|
||||
// Created by liwei wang on 2021/7/27.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public class MqttDecodePublish: NSObject {
|
||||
|
||||
// 3.3.2.3 PUBLISH Properties
|
||||
// 3.3.2.3.1 Property Length
|
||||
public var propertyLength: Int?
|
||||
// 3.3.2.3.2 Payload Format Indicator
|
||||
public var payloadFormatIndicator: PayloadFormatIndicator?
|
||||
// 3.3.2.3.3 Message Expiry Interval
|
||||
public var messageExpiryInterval: UInt32?
|
||||
// 3.3.2.3.4 Topic Alias
|
||||
public var topicAlias: UInt16?
|
||||
// 3.3.2.3.5 Response Topic
|
||||
public var responseTopic: String?
|
||||
// 3.3.2.3.6 Correlation Data
|
||||
public var correlationData: [UInt8]?
|
||||
// 3.3.2.3.7 Property
|
||||
public var userProperty: [String: String]?
|
||||
// 3.3.2.3.8 Subscription Identifier
|
||||
public var subscriptionIdentifier: Int = 0
|
||||
// 3.3.2.3.9 Content Type
|
||||
public var contentType: String?
|
||||
|
||||
// public var applicationMessage: [UInt8]?
|
||||
|
||||
// 3.3.2.1 Topic Name
|
||||
public var topic: String = ""
|
||||
// 3.3.2.2 Packet Identifier
|
||||
public var packetIdentifier: UInt16?
|
||||
public var mqtt5DataIndex = 0
|
||||
|
||||
public func decodePublish(fixedHeader: UInt8, publishData: [UInt8]) {
|
||||
// Topic Name
|
||||
// 3.3.2.1 Topic Name
|
||||
var dataIndex = 0
|
||||
guard let result = unsignedByteToString(data: publishData, offset: dataIndex) else {
|
||||
return
|
||||
}
|
||||
self.topic = result.resStr
|
||||
dataIndex = result.newOffset
|
||||
mqtt5DataIndex = dataIndex
|
||||
|
||||
printDebug("topic = \(topic)")
|
||||
|
||||
guard let recQos = CocoaMQTTQoS(rawValue: (fixedHeader & 0b0000_0110) >> 1) else {
|
||||
return
|
||||
}
|
||||
|
||||
// 3.3.2.2 Packet Identifier
|
||||
// Packet Identifier
|
||||
if recQos == .qos1 || recQos == .qos2 {
|
||||
let IdentifierResult = integerCompute(data: publishData, formatType: formatInt.formatUint16.rawValue, offset: dataIndex)
|
||||
packetIdentifier = UInt16(IdentifierResult!.res)
|
||||
dataIndex = IdentifierResult!.newOffset
|
||||
}
|
||||
|
||||
var protocolVersion = ""
|
||||
if let storage = CocoaMQTTStorage() {
|
||||
protocolVersion = storage.queryMQTTVersion()
|
||||
}
|
||||
|
||||
if protocolVersion == "5.0" {
|
||||
// 3.3.2.3.1 Property Length
|
||||
// propertyLength
|
||||
let propertyLengthVariableByteInteger = decodeVariableByteInteger(data: publishData, offset: dataIndex)
|
||||
propertyLength = propertyLengthVariableByteInteger.res
|
||||
dataIndex = propertyLengthVariableByteInteger.newOffset
|
||||
mqtt5DataIndex = propertyLengthVariableByteInteger.newOffset
|
||||
|
||||
let occupyIndex = dataIndex
|
||||
|
||||
while dataIndex < occupyIndex + (propertyLength ?? 0) {
|
||||
|
||||
let resVariableByteInteger = decodeVariableByteInteger(data: publishData, offset: dataIndex)
|
||||
dataIndex = resVariableByteInteger.newOffset
|
||||
let propertyNameByte = resVariableByteInteger.res
|
||||
guard let propertyName = CocoaMQTTPropertyName(rawValue: UInt8(propertyNameByte)) else {
|
||||
break
|
||||
}
|
||||
switch propertyName.rawValue {
|
||||
// 3.3.2.3.2 Payload Format Indicator
|
||||
case CocoaMQTTPropertyName.payloadFormatIndicator.rawValue:
|
||||
if publishData[dataIndex] & 0b0000_0001 > 0 {
|
||||
payloadFormatIndicator = .utf8
|
||||
} else {
|
||||
payloadFormatIndicator = .unspecified
|
||||
}
|
||||
dataIndex += 1
|
||||
|
||||
// 3.3.2.3.3 Message Expiry Interval
|
||||
case CocoaMQTTPropertyName.willExpiryInterval.rawValue:
|
||||
let comRes = integerCompute(data: publishData, formatType: formatInt.formatUint32.rawValue, offset: dataIndex)
|
||||
messageExpiryInterval = UInt32(comRes!.res)
|
||||
dataIndex = comRes!.newOffset
|
||||
|
||||
// 3.3.2.3.4 Topic Alias
|
||||
case CocoaMQTTPropertyName.topicAlias.rawValue:
|
||||
let comRes = integerCompute(data: publishData, formatType: formatInt.formatUint16.rawValue, offset: dataIndex)
|
||||
topicAlias = UInt16(comRes!.res)
|
||||
dataIndex = comRes!.newOffset
|
||||
|
||||
// 3.3.2.3.5 Response Topic
|
||||
case CocoaMQTTPropertyName.responseTopic.rawValue:
|
||||
guard let result = unsignedByteToString(data: publishData, offset: dataIndex) else {
|
||||
break
|
||||
}
|
||||
responseTopic = result.resStr
|
||||
dataIndex = result.newOffset
|
||||
|
||||
// 3.3.2.3.6 Correlation Data
|
||||
case CocoaMQTTPropertyName.correlationData.rawValue:
|
||||
guard let result = unsignedByteToBinary(data: publishData, offset: dataIndex) else {
|
||||
break
|
||||
}
|
||||
correlationData = result.resStr
|
||||
dataIndex = result.newOffset
|
||||
|
||||
// 3.3.2.3.7 User Property
|
||||
case CocoaMQTTPropertyName.userProperty.rawValue:
|
||||
var key: String?
|
||||
var value: String?
|
||||
guard let keyRes = unsignedByteToString(data: publishData, offset: dataIndex) else {
|
||||
break
|
||||
}
|
||||
key = keyRes.resStr
|
||||
dataIndex = keyRes.newOffset
|
||||
|
||||
guard let valRes = unsignedByteToString(data: publishData, offset: dataIndex) else {
|
||||
break
|
||||
}
|
||||
value = valRes.resStr
|
||||
dataIndex = valRes.newOffset
|
||||
|
||||
if userProperty == nil {
|
||||
userProperty = [:]
|
||||
}
|
||||
|
||||
userProperty![key!] = value
|
||||
|
||||
// 3.3.2.3.8 Subscription Identifier
|
||||
case CocoaMQTTPropertyName.subscriptionIdentifier.rawValue:
|
||||
let valRes = decodeVariableByteInteger(data: publishData, offset: dataIndex)
|
||||
subscriptionIdentifier = valRes.res
|
||||
dataIndex = valRes.newOffset
|
||||
|
||||
// 3.3.2.3.9 Content Type
|
||||
case CocoaMQTTPropertyName.contentType.rawValue:
|
||||
guard let valRes = unsignedByteToString(data: publishData, offset: dataIndex) else {
|
||||
break
|
||||
}
|
||||
contentType = valRes.resStr
|
||||
dataIndex = valRes.newOffset
|
||||
|
||||
default:
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,100 @@
|
|||
//
|
||||
// MqttDecodeSubAck.swift
|
||||
// CocoaMQTT
|
||||
//
|
||||
// Created by liwei wang on 2021/8/12.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public class MqttDecodeSubAck: NSObject {
|
||||
|
||||
var totalCount = 0
|
||||
var dataIndex = 0
|
||||
var propertyLength: Int = 0
|
||||
|
||||
public var reasonCodes: [CocoaMQTTSUBACKReasonCode] = []
|
||||
// public var reasonCode: CocoaMQTTSUBACKReasonCode?
|
||||
public var msgid: UInt16 = 0
|
||||
public var reasonString: String?
|
||||
public var userProperty: [String: String]?
|
||||
|
||||
public func decodeSubAck(fixedHeader: UInt8, pubAckData: [UInt8]) {
|
||||
totalCount = pubAckData.count
|
||||
dataIndex = 0
|
||||
// msgid
|
||||
let msgidResult = integerCompute(data: pubAckData, formatType: formatInt.formatUint16.rawValue, offset: dataIndex)
|
||||
msgid = UInt16(msgidResult!.res)
|
||||
dataIndex = msgidResult!.newOffset
|
||||
|
||||
var protocolVersion = ""
|
||||
if let storage = CocoaMQTTStorage() {
|
||||
protocolVersion = storage.queryMQTTVersion()
|
||||
}
|
||||
|
||||
if protocolVersion == "5.0" {
|
||||
// 3.9.2.1 SUBACK Properties
|
||||
// 3.9.2.1.1 Property Length
|
||||
let propertyLengthVariableByteInteger = decodeVariableByteInteger(data: pubAckData, offset: dataIndex)
|
||||
propertyLength = propertyLengthVariableByteInteger.res
|
||||
dataIndex = propertyLengthVariableByteInteger.newOffset
|
||||
let occupyIndex = dataIndex
|
||||
|
||||
while dataIndex < occupyIndex + propertyLength {
|
||||
let resVariableByteInteger = decodeVariableByteInteger(data: pubAckData, offset: dataIndex)
|
||||
dataIndex = resVariableByteInteger.newOffset
|
||||
let propertyNameByte = resVariableByteInteger.res
|
||||
guard let propertyName = CocoaMQTTPropertyName(rawValue: UInt8(propertyNameByte)) else {
|
||||
break
|
||||
}
|
||||
|
||||
switch propertyName.rawValue {
|
||||
// 3.9.2.1.2 Reason String
|
||||
case CocoaMQTTPropertyName.reasonString.rawValue:
|
||||
guard let result = unsignedByteToString(data: pubAckData, offset: dataIndex) else {
|
||||
break
|
||||
}
|
||||
reasonString = result.resStr
|
||||
dataIndex = result.newOffset
|
||||
|
||||
// 3.9.2.1.3 User Property
|
||||
case CocoaMQTTPropertyName.userProperty.rawValue:
|
||||
var key: String?
|
||||
var value: String?
|
||||
if userProperty == nil {
|
||||
userProperty = [:]
|
||||
}
|
||||
|
||||
guard let keyRes = unsignedByteToString(data: pubAckData, offset: dataIndex) else {
|
||||
break
|
||||
}
|
||||
key = keyRes.resStr
|
||||
dataIndex = keyRes.newOffset
|
||||
|
||||
guard let valRes = unsignedByteToString(data: pubAckData, offset: dataIndex) else {
|
||||
break
|
||||
}
|
||||
value = valRes.resStr
|
||||
dataIndex = valRes.newOffset
|
||||
|
||||
userProperty![key!] = value
|
||||
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if dataIndex < totalCount {
|
||||
while dataIndex < totalCount {
|
||||
guard let reasonCode = CocoaMQTTSUBACKReasonCode(rawValue: pubAckData[dataIndex]) else {
|
||||
return
|
||||
}
|
||||
reasonCodes.append(reasonCode)
|
||||
dataIndex += 1
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
//
|
||||
// MqttDecodeUnsubAck.swift
|
||||
// CocoaMQTT
|
||||
//
|
||||
// Created by liwei wang on 2021/8/16.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public class MqttDecodeUnsubAck: NSObject {
|
||||
|
||||
var totalCount = 0
|
||||
var dataIndex = 0
|
||||
var propertyLength: Int = 0
|
||||
|
||||
public var reasonCodes: [CocoaMQTTUNSUBACKReasonCode] = []
|
||||
|
||||
public var msgid: UInt16 = 0
|
||||
public var reasonString: String?
|
||||
public var userProperty: [String: String]?
|
||||
|
||||
public func decodeUnSubAck(fixedHeader: UInt8, pubAckData: [UInt8]) {
|
||||
totalCount = pubAckData.count
|
||||
dataIndex = 0
|
||||
// msgid
|
||||
let msgidResult = integerCompute(data: pubAckData, formatType: formatInt.formatUint16.rawValue, offset: dataIndex)
|
||||
msgid = UInt16(msgidResult!.res)
|
||||
dataIndex = msgidResult!.newOffset
|
||||
|
||||
var protocolVersion = ""
|
||||
if let storage = CocoaMQTTStorage() {
|
||||
protocolVersion = storage.queryMQTTVersion()
|
||||
}
|
||||
|
||||
if protocolVersion == "5.0" {
|
||||
// 3.11.2.1 UNSUBACK Properties
|
||||
// 3.11.2.1.1 Property Length
|
||||
let propertyLengthVariableByteInteger = decodeVariableByteInteger(data: pubAckData, offset: dataIndex)
|
||||
propertyLength = propertyLengthVariableByteInteger.res
|
||||
dataIndex = propertyLengthVariableByteInteger.newOffset
|
||||
let occupyIndex = dataIndex
|
||||
|
||||
while dataIndex < occupyIndex + propertyLength {
|
||||
let resVariableByteInteger = decodeVariableByteInteger(data: pubAckData, offset: dataIndex)
|
||||
dataIndex = resVariableByteInteger.newOffset
|
||||
let propertyNameByte = resVariableByteInteger.res
|
||||
guard let propertyName = CocoaMQTTPropertyName(rawValue: UInt8(propertyNameByte)) else {
|
||||
break
|
||||
}
|
||||
|
||||
switch propertyName.rawValue {
|
||||
// 3.11.2.1.2 Reason String
|
||||
case CocoaMQTTPropertyName.reasonString.rawValue:
|
||||
guard let result = unsignedByteToString(data: pubAckData, offset: dataIndex) else {
|
||||
break
|
||||
}
|
||||
reasonString = result.resStr
|
||||
dataIndex = result.newOffset
|
||||
|
||||
// 3.11.2.1.3 User Property
|
||||
case CocoaMQTTPropertyName.userProperty.rawValue:
|
||||
var key: String?
|
||||
var value: String?
|
||||
guard let keyRes = unsignedByteToString(data: pubAckData, offset: dataIndex) else {
|
||||
break
|
||||
}
|
||||
key = keyRes.resStr
|
||||
dataIndex = keyRes.newOffset
|
||||
|
||||
guard let valRes = unsignedByteToString(data: pubAckData, offset: dataIndex) else {
|
||||
break
|
||||
}
|
||||
value = valRes.resStr
|
||||
dataIndex = valRes.newOffset
|
||||
|
||||
userProperty![key!] = value
|
||||
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if dataIndex < totalCount {
|
||||
while dataIndex < totalCount {
|
||||
guard let reasonCode = CocoaMQTTUNSUBACKReasonCode(rawValue: pubAckData[dataIndex]) else {
|
||||
return
|
||||
}
|
||||
reasonCodes.append(reasonCode)
|
||||
dataIndex += 1
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
//
|
||||
// MqttPublishProperties.swift
|
||||
// CocoaMQTT
|
||||
//
|
||||
// Created by liwei wang on 2021/7/27.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public class MqttPublishProperties: NSObject {
|
||||
|
||||
// 3.3.2.3 PUBLISH Properties
|
||||
// 3.3.2.3.1 Property Length
|
||||
public var propertyLength: Int?
|
||||
// 3.3.2.3.2 Payload Format Indicator
|
||||
public var payloadFormatIndicator: PayloadFormatIndicator?
|
||||
// 3.3.2.3.3 Message Expiry Interval
|
||||
public var messageExpiryInterval: UInt32?
|
||||
// 3.3.2.3.4 Topic Alias
|
||||
public var topicAlias: UInt16?
|
||||
// 3.3.2.3.5 Response Topic
|
||||
public var responseTopic: String?
|
||||
// 3.3.2.3.6 Correlation Data
|
||||
public var correlationData: [UInt8]?
|
||||
// 3.3.2.3.7 Property
|
||||
public var userProperty: [String: String]?
|
||||
// 3.3.2.3.8 Subscription Identifier
|
||||
public var subscriptionIdentifier: UInt32?
|
||||
// 3.3.2.3.9 Content Type
|
||||
public var contentType: String?
|
||||
|
||||
public init(
|
||||
propertyLength: Int? = nil,
|
||||
payloadFormatIndicator: PayloadFormatIndicator? = nil,
|
||||
messageExpiryInterval: UInt32? = nil,
|
||||
topicAlias: UInt16? = nil,
|
||||
responseTopic: String? = nil,
|
||||
correlation: String? = nil,
|
||||
userProperty: [String: String]? = nil,
|
||||
subscriptionIdentifier: UInt32? = nil,
|
||||
contentType: String? = nil
|
||||
) {
|
||||
self.propertyLength = propertyLength
|
||||
self.payloadFormatIndicator = payloadFormatIndicator
|
||||
self.messageExpiryInterval = messageExpiryInterval
|
||||
self.topicAlias = topicAlias
|
||||
self.responseTopic = responseTopic
|
||||
self.correlationData = correlation?.bytesWithLength
|
||||
self.userProperty = userProperty
|
||||
self.subscriptionIdentifier = subscriptionIdentifier
|
||||
self.contentType = contentType
|
||||
}
|
||||
|
||||
public var properties: [UInt8] {
|
||||
var properties = [UInt8]()
|
||||
|
||||
// 3.3.2.3.2 Payload Format Indicator
|
||||
if let payloadFormatIndicator = self.payloadFormatIndicator {
|
||||
properties += getMQTTPropertyData(type: CocoaMQTTPropertyName.payloadFormatIndicator.rawValue, value: [payloadFormatIndicator.rawValue])
|
||||
}
|
||||
// 3.3.2.3.3 Message Expiry Interval
|
||||
if let messageExpiryInterval = self.messageExpiryInterval {
|
||||
properties += getMQTTPropertyData(type: CocoaMQTTPropertyName.willExpiryInterval.rawValue, value: messageExpiryInterval.byteArrayLittleEndian)
|
||||
}
|
||||
// 3.3.2.3.4 Topic Alias
|
||||
if let topicAlias = self.topicAlias {
|
||||
properties += getMQTTPropertyData(type: CocoaMQTTPropertyName.topicAlias.rawValue, value: topicAlias.hlBytes)
|
||||
}
|
||||
// 3.3.2.3.5 Response Topic
|
||||
if let responseTopic = self.responseTopic {
|
||||
properties += getMQTTPropertyData(type: CocoaMQTTPropertyName.responseTopic.rawValue, value: responseTopic.bytesWithLength)
|
||||
}
|
||||
// 3.3.2.3.6 Correlation Data
|
||||
if let correlationData = self.correlationData {
|
||||
properties += getMQTTPropertyData(type: CocoaMQTTPropertyName.correlationData.rawValue, value: correlationData)
|
||||
}
|
||||
// 3.3.2.3.7 Property Length User Property
|
||||
if let userProperty = self.userProperty {
|
||||
properties += userProperty.userPropertyBytes
|
||||
}
|
||||
// 3.3.2.3.8 Subscription Identifier
|
||||
if let subscriptionIdentifier = self.subscriptionIdentifier,
|
||||
let subscriptionIdentifier = beVariableByteInteger(subscriptionIdentifier) {
|
||||
properties += getMQTTPropertyData(type: CocoaMQTTPropertyName.subscriptionIdentifier.rawValue, value: subscriptionIdentifier)
|
||||
}
|
||||
// 3.3.2.3.9 Content Type
|
||||
if let contentType = self.contentType {
|
||||
properties += getMQTTPropertyData(type: CocoaMQTTPropertyName.contentType.rawValue, value: contentType.bytesWithLength)
|
||||
}
|
||||
|
||||
return properties
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
//
|
||||
// MqttSubscription.swift
|
||||
// CocoaMQTT
|
||||
//
|
||||
// Created by liwei wang on 2021/7/15.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// 3.8.3.1 Subscription Options
|
||||
public class MqttSubscription {
|
||||
|
||||
public var topic: String
|
||||
public var qos = CocoaMQTTQoS.qos1
|
||||
public var noLocal: Bool = false
|
||||
public var retainAsPublished: Bool = false
|
||||
public var retainHandling: CocoaRetainHandlingOption = .sendOnSubscribe
|
||||
public var subscriptionOptions: Bool = false
|
||||
|
||||
public init(topic: String) {
|
||||
self.topic = topic
|
||||
}
|
||||
|
||||
public init(topic: String, qos: CocoaMQTTQoS) {
|
||||
self.topic = topic
|
||||
self.qos = qos
|
||||
}
|
||||
|
||||
var subscriptionData: [UInt8] {
|
||||
var data = [UInt8]()
|
||||
|
||||
data += topic.bytesWithLength
|
||||
|
||||
var options: Int = 0
|
||||
switch self.qos {
|
||||
case .qos0:
|
||||
options = options | 0b0000_0000
|
||||
case .qos1:
|
||||
options = options | 0b0000_0001
|
||||
case .qos2:
|
||||
options = options | 0b0000_0010
|
||||
default:
|
||||
printDebug("topicFilter qos failure")
|
||||
}
|
||||
|
||||
switch self.noLocal {
|
||||
case true:
|
||||
options = options | 0b0000_0100
|
||||
case false:
|
||||
options = options | 0b0000_0000
|
||||
}
|
||||
|
||||
switch self.retainAsPublished {
|
||||
case true:
|
||||
options = options | 0b0000_1000
|
||||
case false:
|
||||
options = options | 0b0000_0000
|
||||
}
|
||||
|
||||
switch self.retainHandling {
|
||||
case CocoaRetainHandlingOption.none:
|
||||
options = options | 0b0010_0000
|
||||
case CocoaRetainHandlingOption.sendOnlyWhenSubscribeIsNew:
|
||||
options = options | 0b0001_0000
|
||||
case CocoaRetainHandlingOption.sendOnSubscribe:
|
||||
options = options | 0b0000_0000
|
||||
}
|
||||
|
||||
if subscriptionOptions {
|
||||
data += [UInt8(options)]
|
||||
}
|
||||
|
||||
return data
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
//
|
||||
// Copyright © 2022. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// A thread-safe dictionary
|
||||
public class ThreadSafeDictionary<K: Hashable, V>: Collection {
|
||||
private var dictionary: [K: V]
|
||||
private let concurrentQueue: DispatchQueue
|
||||
|
||||
public var startIndex: Dictionary<K, V>.Index {
|
||||
concurrentQueue.sync {
|
||||
return self.dictionary.startIndex
|
||||
}
|
||||
}
|
||||
|
||||
public var endIndex: Dictionary<K, V>.Index {
|
||||
concurrentQueue.sync {
|
||||
return self.dictionary.endIndex
|
||||
}
|
||||
}
|
||||
|
||||
public init(label: String, dict: [K: V] = [K: V]()) {
|
||||
self.dictionary = dict
|
||||
concurrentQueue = DispatchQueue(label: label, attributes: .concurrent)
|
||||
}
|
||||
|
||||
public func index(after i: Dictionary<K, V>.Index) -> Dictionary<K, V>.Index {
|
||||
concurrentQueue.sync {
|
||||
self.dictionary.index(after: i)
|
||||
}
|
||||
}
|
||||
|
||||
public subscript(key: K) -> V? {
|
||||
get {
|
||||
concurrentQueue.sync {
|
||||
self.dictionary[key]
|
||||
}
|
||||
}
|
||||
set(newValue) {
|
||||
concurrentQueue.async(flags: .barrier) {[weak self] in
|
||||
self?.dictionary[key] = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public subscript(index: Dictionary<K, V>.Index) -> Dictionary<K, V>.Element {
|
||||
concurrentQueue.sync {
|
||||
self.dictionary[index]
|
||||
}
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
public func removeValue(forKey key: K) -> V? {
|
||||
concurrentQueue.sync(flags: .barrier) {
|
||||
self.dictionary.removeValue(forKey: key)
|
||||
}
|
||||
}
|
||||
|
||||
public func removeAll() {
|
||||
concurrentQueue.async(flags: .barrier) {[weak self] in
|
||||
self?.dictionary.removeAll()
|
||||
}
|
||||
}
|
||||
|
||||
public func snapshot() -> [K: V] {
|
||||
concurrentQueue.sync {
|
||||
dictionary
|
||||
}
|
||||
}
|
||||
|
||||
public func replace(with newDictionary: [K: V]) {
|
||||
concurrentQueue.sync(flags: .barrier) {
|
||||
dictionary = newDictionary
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
import Foundation
|
||||
|
||||
/// A thread-safe property wrapper that uses a concurrent dispatch queue for atomic operations.
|
||||
///
|
||||
/// This wrapper provides both synchronous and asynchronous access to a wrapped value in a
|
||||
/// thread-safe manner using `DispatchQueue` with `.concurrent` attributes and `.barrier` writes.
|
||||
///
|
||||
/// - Important: Although this wrapper provides atomicity for access, it does not make the wrapped
|
||||
/// value itself thread-safe if the type is not thread-safe. Avoid wrapping types that manage
|
||||
/// internal shared state without their own synchronization.
|
||||
///
|
||||
/// - Example:
|
||||
/// ```swift
|
||||
/// @ConcurrentAtomic var counter: Int = 0
|
||||
/// counter += 1
|
||||
/// print(counter)
|
||||
/// ```
|
||||
@propertyWrapper
|
||||
public class ConcurrentAtomic<T> {
|
||||
private var _value: T
|
||||
private let queue: DispatchQueue
|
||||
|
||||
/// Provides synchronous thread-safe access to the wrapped value.
|
||||
///
|
||||
/// - Note: Reads use `queue.sync` and can happen concurrently.
|
||||
/// - Warning: Writes are done asynchronously with `.barrier`, so a following read may
|
||||
/// not reflect the new value immediately.
|
||||
public var wrappedValue: T {
|
||||
get {
|
||||
queue.sync { _value }
|
||||
}
|
||||
set {
|
||||
queue.async(flags: .barrier) {
|
||||
self._value = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the property wrapper instance itself for advanced usage, including mutation and transformation APIs.
|
||||
public var projectedValue: ConcurrentAtomic<T> { self }
|
||||
|
||||
/// Initializes the property wrapper with an initial value and a custom queue label.
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - wrappedValue: The initial value to store.
|
||||
/// - label: A debug label for the underlying `DispatchQueue`. Default is `"ConcurrentAtomic.Queue"`.
|
||||
public init(wrappedValue: T, label: String = "ConcurrentAtomic.Queue") {
|
||||
self._value = wrappedValue
|
||||
self.queue = DispatchQueue(label: label, attributes: .concurrent)
|
||||
}
|
||||
|
||||
/// Synchronously sets a new value in a thread-safe manner.
|
||||
///
|
||||
/// This uses `.barrier` to ensure the new value is fully written before continuing.
|
||||
///
|
||||
/// - Parameter newValue: The new value to be written.
|
||||
public func setSync(_ newValue: T) {
|
||||
queue.sync(flags: .barrier) {
|
||||
self._value = newValue
|
||||
}
|
||||
}
|
||||
|
||||
/// Asynchronously mutates the wrapped value using a transform closure.
|
||||
///
|
||||
/// The mutation is performed with `.barrier`, ensuring it does not overlap with other reads or writes.
|
||||
///
|
||||
/// - Parameter transform: A closure that receives `inout` access to the wrapped value.
|
||||
public func mutate(_ transform: @escaping (inout T) -> Void) {
|
||||
queue.async(flags: .barrier) {
|
||||
transform(&self._value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -11,6 +11,10 @@ PODS:
|
|||
- CocoaLumberjack/Core (3.9.1)
|
||||
- CocoaLumberjack/Swift (3.9.1):
|
||||
- CocoaLumberjack/Core
|
||||
- CocoaMQTT (2.2.5):
|
||||
- CocoaMQTT/Core (= 2.2.5)
|
||||
- CocoaMQTT/Core (2.2.5):
|
||||
- MqttCocoaAsyncSocket (~> 1.0.8)
|
||||
- Differentiator (5.0.0)
|
||||
- GTCommonSDK (3.2.3.0):
|
||||
- ZXSDK
|
||||
|
|
@ -89,6 +93,7 @@ PODS:
|
|||
- Moya/Core (= 15.0.0)
|
||||
- Moya/Core (15.0.0):
|
||||
- Alamofire (~> 5.0)
|
||||
- MqttCocoaAsyncSocket (1.0.8)
|
||||
- ObjectMapper (4.4.2)
|
||||
- "OpenIMSDK (3.8.3+hotfix.3.1)":
|
||||
- MJExtension
|
||||
|
|
@ -174,6 +179,7 @@ DEPENDENCIES:
|
|||
- AMapLocation
|
||||
- AMapSearch
|
||||
- CocoaLumberjack/Swift
|
||||
- CocoaMQTT
|
||||
- GYSDK
|
||||
- HXPHPicker
|
||||
- IQKeyboardManagerSwift
|
||||
|
|
@ -215,6 +221,7 @@ SPEC REPOS:
|
|||
- AMapLocation
|
||||
- AMapSearch
|
||||
- CocoaLumberjack
|
||||
- CocoaMQTT
|
||||
- Differentiator
|
||||
- GTCommonSDK
|
||||
- GYSDK
|
||||
|
|
@ -236,6 +243,7 @@ SPEC REPOS:
|
|||
- MJExtension
|
||||
- MJRefresh
|
||||
- Moya
|
||||
- MqttCocoaAsyncSocket
|
||||
- ObjectMapper
|
||||
- OpenIMSDK
|
||||
- OpenIMSDKCore
|
||||
|
|
@ -277,6 +285,7 @@ SPEC CHECKSUMS:
|
|||
AMapLocation: 6e44f50b044dc54c6b3dcb1dee5ffd6de2689e41
|
||||
AMapSearch: 29224a399b56b17da1540e4312fc4d9dc37342bb
|
||||
CocoaLumberjack: e4ba3b414dfca8c1916c6303d37f63b3a95134c6
|
||||
CocoaMQTT: 1e2fa493d57045d66fb1eba2bb5ffb8d77039c60
|
||||
Differentiator: e8497ceab83c1b10ca233716d547b9af21b9344d
|
||||
GTCommonSDK: 238c6735add91e654f8564638854686ee3dcb4ac
|
||||
GYSDK: db5f4b3aae8df06201be5e786076211ec11791be
|
||||
|
|
@ -299,6 +308,7 @@ SPEC CHECKSUMS:
|
|||
MJExtension: e97d164cb411aa9795cf576093a1fa208b4a8dd8
|
||||
MJRefresh: ff9e531227924c84ce459338414550a05d2aea78
|
||||
Moya: 138f0573e53411fb3dc17016add0b748dfbd78ee
|
||||
MqttCocoaAsyncSocket: 77d3b74f76228dd5a05d1f9526eab101d415b30c
|
||||
ObjectMapper: e6e4d91ff7f2861df7aecc536c92d8363f4c9677
|
||||
OpenIMSDK: c1880296ead605bcac2a289ad0aef9170fe6e6df
|
||||
OpenIMSDKCore: 75caba22be8ee484011f367711178312bd64a17d
|
||||
|
|
@ -323,6 +333,6 @@ SPEC CHECKSUMS:
|
|||
YYImage: 1e1b62a9997399593e4b9c4ecfbbabbf1d3f3b54
|
||||
ZXSDK: 786338c0a18e98e03eda00699c3bfd2700b97117
|
||||
|
||||
PODFILE CHECKSUM: 6ba8ada83ccc565c9bdcc418e9a936818640882c
|
||||
PODFILE CHECKSUM: 861114200722c29adba98535aef6b0494d123b44
|
||||
|
||||
COCOAPODS: 1.16.2
|
||||
|
|
|
|||
|
|
@ -0,0 +1,35 @@
|
|||
This library is in the public domain.
|
||||
However, not all organizations are allowed to use such a license.
|
||||
For example, Germany doesn't recognize the Public Domain and one is not allowed to use libraries under such license (or similar).
|
||||
|
||||
Thus, the library is now dual licensed,
|
||||
and one is allowed to choose which license they would like to use.
|
||||
|
||||
##################################################
|
||||
License Option #1 :
|
||||
##################################################
|
||||
|
||||
Public Domain
|
||||
|
||||
##################################################
|
||||
License Option #2 :
|
||||
##################################################
|
||||
|
||||
Software License Agreement (BSD License)
|
||||
|
||||
Copyright (c) 2017, Deusty, LLC
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use of this software in source and binary forms,
|
||||
with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above
|
||||
copyright notice, this list of conditions and the
|
||||
following disclaimer.
|
||||
|
||||
* Neither the name of Deusty LLC nor the names of its
|
||||
contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior
|
||||
written permission of Deusty LLC.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
# CocoaAsyncSocket
|
||||
[](https://travis-ci.org/robbiehanson/CocoaAsyncSocket) [](http://cocoadocs.org/docsets/CocoaAsyncSocket) [](https://github.com/Carthage/Carthage) [](http://cocoapods.org/?q=CocoaAsyncSocket) [](https://en.wikipedia.org/wiki/Public_domain)
|
||||
|
||||
|
||||
CocoaAsyncSocket provides easy-to-use and powerful asynchronous socket libraries for macOS, iOS, and tvOS. The classes are described below.
|
||||
|
||||
## Installation
|
||||
|
||||
#### CocoaPods
|
||||
|
||||
Install using [CocoaPods](https://cocoapods.org) by adding this line to your Podfile:
|
||||
|
||||
````ruby
|
||||
use_frameworks! # Add this if you are targeting iOS 8+ or using Swift
|
||||
pod 'CocoaAsyncSocket'
|
||||
````
|
||||
|
||||
#### Carthage
|
||||
|
||||
CocoaAsyncSocket is [Carthage](https://github.com/Carthage/Carthage) compatible. To include it add the following line to your `Cartfile`
|
||||
|
||||
```bash
|
||||
github "robbiehanson/CocoaAsyncSocket" "master"
|
||||
```
|
||||
|
||||
The project is currently configured to build for **iOS**, **tvOS** and **Mac**. After building with carthage the resultant frameworks will be stored in:
|
||||
|
||||
* `Carthage/Build/iOS/CocoaAsyncSocket.framework`
|
||||
* `Carthage/Build/tvOS/CocoaAsyncSocket.framework`
|
||||
* `Carthage/Build/Mac/CocoaAsyncSocket.framework`
|
||||
|
||||
Select the correct framework(s) and drag it into your project.
|
||||
|
||||
#### Swift Package Manager
|
||||
|
||||
Simply add the package dependency to your Package.swift and depend on "CocoaAsyncSocket" in the necessary targets:
|
||||
```swift
|
||||
dependencies: [
|
||||
.package(url: "https://github.com/robbiehanson/CocoaAsyncSocket", from: "7.6.4")
|
||||
]
|
||||
```
|
||||
|
||||
#### Manual
|
||||
|
||||
You can also include it into your project by adding the source files directly, but you should probably be using a dependency manager to keep up to date.
|
||||
|
||||
### Importing
|
||||
|
||||
Using Objective-C:
|
||||
|
||||
```obj-c
|
||||
// When using Clang Modules:
|
||||
@import MqttCocoaAsyncSocket;
|
||||
|
||||
// or when not:
|
||||
#import "GCDAsyncSocket.h" // for TCP
|
||||
#import "GCDAsyncUdpSocket.h" // for UDP
|
||||
```
|
||||
|
||||
Using Swift:
|
||||
|
||||
```swift
|
||||
import MqttCocoaAsyncSocket
|
||||
```
|
||||
|
||||
## TCP
|
||||
|
||||
**GCDAsyncSocket** is a TCP/IP socket networking library built atop Grand Central Dispatch. Here are the key features available:
|
||||
|
||||
- Native Objective-C, fully self-contained in one class.<br/>
|
||||
_No need to muck around with sockets or streams. This class handles everything for you._
|
||||
|
||||
- Full delegate support<br/>
|
||||
_Errors, connections, read completions, write completions, progress, and disconnections all result in a call to your delegate method._
|
||||
|
||||
- Queued non-blocking reads and writes, with optional timeouts.<br/>
|
||||
_You tell it what to read or write, and it handles everything for you. Queueing, buffering, and searching for termination sequences within the stream - all handled for you automatically._
|
||||
|
||||
- Automatic socket acceptance.<br/>
|
||||
_Spin up a server socket, tell it to accept connections, and it will call you with new instances of itself for each connection._
|
||||
|
||||
- Support for TCP streams over IPv4 and IPv6.<br/>
|
||||
_Automatically connect to IPv4 or IPv6 hosts. Automatically accept incoming connections over both IPv4 and IPv6 with a single instance of this class. No more worrying about multiple sockets._
|
||||
|
||||
- Support for TLS / SSL<br/>
|
||||
_Secure your socket with ease using just a single method call. Available for both client and server sockets._
|
||||
|
||||
- Fully GCD based and Thread-Safe<br/>
|
||||
_It runs entirely within its own GCD dispatch_queue, and is completely thread-safe. Further, the delegate methods are all invoked asynchronously onto a dispatch_queue of your choosing. This means parallel operation of your socket code, and your delegate/processing code._
|
||||
|
||||
## UDP
|
||||
|
||||
**GCDAsyncUdpSocket** is a UDP/IP socket networking library built atop Grand Central Dispatch. Here are the key features available:
|
||||
|
||||
- Native Objective-C, fully self-contained in one class.<br/>
|
||||
_No need to muck around with low-level sockets. This class handles everything for you._
|
||||
|
||||
- Full delegate support.<br/>
|
||||
_Errors, send completions, receive completions, and disconnections all result in a call to your delegate method._
|
||||
|
||||
- Queued non-blocking send and receive operations, with optional timeouts.<br/>
|
||||
_You tell it what to send or receive, and it handles everything for you. Queueing, buffering, waiting and checking errno - all handled for you automatically._
|
||||
|
||||
- Support for IPv4 and IPv6.<br/>
|
||||
_Automatically send/recv using IPv4 and/or IPv6. No more worrying about multiple sockets._
|
||||
|
||||
- Fully GCD based and Thread-Safe<br/>
|
||||
_It runs entirely within its own GCD dispatch_queue, and is completely thread-safe. Further, the delegate methods are all invoked asynchronously onto a dispatch_queue of your choosing. This means parallel operation of your socket code, and your delegate/processing code._
|
||||
|
||||
***
|
||||
|
||||
For those new(ish) to networking, it's recommended you **[read the wiki](https://github.com/robbiehanson/CocoaAsyncSocket/wiki)**.<br/>_Sockets might not work exactly like you think they do..._
|
||||
|
||||
**Still got questions?** Try the **[CocoaAsyncSocket Mailing List](https://groups.google.com/group/cocoaasyncsocket)**.
|
||||
***
|
||||
|
||||
Love the project? Wanna buy me a ☕️ ? (or a 🍺 😀 ):
|
||||
|
||||
[](https://onename.com/robbiehanson)
|
||||
[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=2M8C699FQ8AW2)
|
||||
|
||||
58
Pods/Pods.xcodeproj/xcuserdata/yanghong.xcuserdatad/xcschemes/CocoaMQTT.xcscheme
generated
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1600"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "4EB168A830EFA6136FA93357D1F1511A"
|
||||
BuildableName = "CocoaMQTT.framework"
|
||||
BlueprintName = "CocoaMQTT"
|
||||
ReferencedContainer = "container:Pods.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
58
Pods/Pods.xcodeproj/xcuserdata/yanghong.xcuserdatad/xcschemes/MqttCocoaAsyncSocket.xcscheme
generated
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1600"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "B017CC75E2704C686AA0A2203247D9C7"
|
||||
BuildableName = "MqttCocoaAsyncSocket.framework"
|
||||
BlueprintName = "MqttCocoaAsyncSocket"
|
||||
ReferencedContainer = "container:Pods.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
|
|
@ -49,6 +49,11 @@
|
|||
<key>isShown</key>
|
||||
<false/>
|
||||
</dict>
|
||||
<key>CocoaMQTT.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
</dict>
|
||||
<key>Differentiator.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
|
|
@ -214,6 +219,11 @@
|
|||
<key>isShown</key>
|
||||
<false/>
|
||||
</dict>
|
||||
<key>MqttCocoaAsyncSocket.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
<false/>
|
||||
</dict>
|
||||
<key>ObjectMapper-Privacy.xcscheme</key>
|
||||
<dict>
|
||||
<key>isShown</key>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>${PODS_DEVELOPMENT_LANGUAGE}</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>${PRODUCT_NAME}</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2.2.5</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>${CURRENT_PROJECT_VERSION}</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
@interface PodsDummy_CocoaMQTT : NSObject
|
||||
@end
|
||||
@implementation PodsDummy_CocoaMQTT
|
||||
@end
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
#ifdef __OBJC__
|
||||
#import <UIKit/UIKit.h>
|
||||
#else
|
||||
#ifndef FOUNDATION_EXPORT
|
||||
#if defined(__cplusplus)
|
||||
#define FOUNDATION_EXPORT extern "C"
|
||||
#else
|
||||
#define FOUNDATION_EXPORT extern
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
#ifdef __OBJC__
|
||||
#import <UIKit/UIKit.h>
|
||||
#else
|
||||
#ifndef FOUNDATION_EXPORT
|
||||
#if defined(__cplusplus)
|
||||
#define FOUNDATION_EXPORT extern "C"
|
||||
#else
|
||||
#define FOUNDATION_EXPORT extern
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
FOUNDATION_EXPORT double CocoaMQTTVersionNumber;
|
||||
FOUNDATION_EXPORT const unsigned char CocoaMQTTVersionString[];
|
||||
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
|
||||
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/CocoaMQTT
|
||||
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/MqttCocoaAsyncSocket"
|
||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
|
||||
LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift
|
||||
OTHER_LDFLAGS = $(inherited) -framework "CFNetwork" -framework "MqttCocoaAsyncSocket" -framework "Security"
|
||||
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
|
||||
PODS_BUILD_DIR = ${BUILD_DIR}
|
||||
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|
||||
PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE}
|
||||
PODS_ROOT = ${SRCROOT}
|
||||
PODS_TARGET_SRCROOT = ${PODS_ROOT}/CocoaMQTT
|
||||
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
|
||||
SKIP_INSTALL = YES
|
||||
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
framework module CocoaMQTT {
|
||||
umbrella header "CocoaMQTT-umbrella.h"
|
||||
|
||||
export *
|
||||
module * { export * }
|
||||
}
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
|
||||
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/CocoaMQTT
|
||||
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/MqttCocoaAsyncSocket"
|
||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
|
||||
LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift
|
||||
OTHER_LDFLAGS = $(inherited) -framework "CFNetwork" -framework "MqttCocoaAsyncSocket" -framework "Security"
|
||||
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
|
||||
PODS_BUILD_DIR = ${BUILD_DIR}
|
||||
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|
||||
PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE}
|
||||
PODS_ROOT = ${SRCROOT}
|
||||
PODS_TARGET_SRCROOT = ${PODS_ROOT}/CocoaMQTT
|
||||
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
|
||||
SKIP_INSTALL = YES
|
||||
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
|
||||
26
Pods/Target Support Files/MqttCocoaAsyncSocket/MqttCocoaAsyncSocket-Info.plist
generated
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>${PODS_DEVELOPMENT_LANGUAGE}</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>${PRODUCT_NAME}</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0.8</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>${CURRENT_PROJECT_VERSION}</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
@interface PodsDummy_MqttCocoaAsyncSocket : NSObject
|
||||
@end
|
||||
@implementation PodsDummy_MqttCocoaAsyncSocket
|
||||
@end
|
||||
12
Pods/Target Support Files/MqttCocoaAsyncSocket/MqttCocoaAsyncSocket-prefix.pch
generated
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
#ifdef __OBJC__
|
||||
#import <UIKit/UIKit.h>
|
||||
#else
|
||||
#ifndef FOUNDATION_EXPORT
|
||||
#if defined(__cplusplus)
|
||||
#define FOUNDATION_EXPORT extern "C"
|
||||
#else
|
||||
#define FOUNDATION_EXPORT extern
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
18
Pods/Target Support Files/MqttCocoaAsyncSocket/MqttCocoaAsyncSocket-umbrella.h
generated
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
#ifdef __OBJC__
|
||||
#import <UIKit/UIKit.h>
|
||||
#else
|
||||
#ifndef FOUNDATION_EXPORT
|
||||
#if defined(__cplusplus)
|
||||
#define FOUNDATION_EXPORT extern "C"
|
||||
#else
|
||||
#define FOUNDATION_EXPORT extern
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#import "MGCDAsyncSocket.h"
|
||||
#import "MGCDAsyncUdpSocket.h"
|
||||
|
||||
FOUNDATION_EXPORT double MqttCocoaAsyncSocketVersionNumber;
|
||||
FOUNDATION_EXPORT const unsigned char MqttCocoaAsyncSocketVersionString[];
|
||||
|
||||
13
Pods/Target Support Files/MqttCocoaAsyncSocket/MqttCocoaAsyncSocket.debug.xcconfig
generated
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
|
||||
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/MqttCocoaAsyncSocket
|
||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
|
||||
OTHER_LDFLAGS = $(inherited) -framework "CFNetwork" -framework "Security"
|
||||
PODS_BUILD_DIR = ${BUILD_DIR}
|
||||
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|
||||
PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE}
|
||||
PODS_ROOT = ${SRCROOT}
|
||||
PODS_TARGET_SRCROOT = ${PODS_ROOT}/MqttCocoaAsyncSocket
|
||||
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
|
||||
SKIP_INSTALL = YES
|
||||
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
|
||||
6
Pods/Target Support Files/MqttCocoaAsyncSocket/MqttCocoaAsyncSocket.modulemap
generated
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
framework module MqttCocoaAsyncSocket {
|
||||
umbrella header "MqttCocoaAsyncSocket-umbrella.h"
|
||||
|
||||
export *
|
||||
module * { export * }
|
||||
}
|
||||
13
Pods/Target Support Files/MqttCocoaAsyncSocket/MqttCocoaAsyncSocket.release.xcconfig
generated
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
|
||||
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/MqttCocoaAsyncSocket
|
||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
|
||||
OTHER_LDFLAGS = $(inherited) -framework "CFNetwork" -framework "Security"
|
||||
PODS_BUILD_DIR = ${BUILD_DIR}
|
||||
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|
||||
PODS_DEVELOPMENT_LANGUAGE = ${DEVELOPMENT_LANGUAGE}
|
||||
PODS_ROOT = ${SRCROOT}
|
||||
PODS_TARGET_SRCROOT = ${PODS_ROOT}/MqttCocoaAsyncSocket
|
||||
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
|
||||
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
|
||||
SKIP_INSTALL = YES
|
||||
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
|
||||
|
|
@ -87,6 +87,12 @@ Redistribution and use in source and binary forms, with or without modification,
|
|||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
## CocoaMQTT
|
||||
|
||||
This project is dual licensed under the Eclipse Public License 1.0 and the
|
||||
Eclipse Distribution License 1.0 as described in the epl-v10 and edl-v10 files.
|
||||
|
||||
|
||||
## Differentiator
|
||||
|
||||
MIT License
|
||||
|
|
@ -885,6 +891,16 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|||
SOFTWARE.
|
||||
|
||||
|
||||
## MqttCocoaAsyncSocket
|
||||
|
||||
Public Domain License
|
||||
|
||||
The MqttCocoaAsyncSocket project is in the public domain.
|
||||
|
||||
The original TCP version (AsyncSocket) was created by Dustin Voss in January 2003.
|
||||
Updated and maintained by Deusty LLC and the Apple development community.
|
||||
|
||||
|
||||
## ObjectMapper
|
||||
|
||||
The MIT License (MIT)
|
||||
|
|
|
|||
|
|
@ -140,6 +140,18 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|||
<key>Type</key>
|
||||
<string>PSGroupSpecifier</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>FooterText</key>
|
||||
<string>This project is dual licensed under the Eclipse Public License 1.0 and the
|
||||
Eclipse Distribution License 1.0 as described in the epl-v10 and edl-v10 files.
|
||||
</string>
|
||||
<key>License</key>
|
||||
<string>MIT</string>
|
||||
<key>Title</key>
|
||||
<string>CocoaMQTT</string>
|
||||
<key>Type</key>
|
||||
<string>PSGroupSpecifier</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>FooterText</key>
|
||||
<string>MIT License
|
||||
|
|
@ -1058,6 +1070,22 @@ SOFTWARE.
|
|||
<key>Type</key>
|
||||
<string>PSGroupSpecifier</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>FooterText</key>
|
||||
<string>Public Domain License
|
||||
|
||||
The MqttCocoaAsyncSocket project is in the public domain.
|
||||
|
||||
The original TCP version (AsyncSocket) was created by Dustin Voss in January 2003.
|
||||
Updated and maintained by Deusty LLC and the Apple development community.
|
||||
</string>
|
||||
<key>License</key>
|
||||
<string>public domain</string>
|
||||
<key>Title</key>
|
||||
<string>MqttCocoaAsyncSocket</string>
|
||||
<key>Type</key>
|
||||
<string>PSGroupSpecifier</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>FooterText</key>
|
||||
<string>The MIT License (MIT)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
${PODS_ROOT}/Target Support Files/Pods-QuickLocation/Pods-QuickLocation-frameworks.sh
|
||||
${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework
|
||||
${BUILT_PRODUCTS_DIR}/CocoaLumberjack/CocoaLumberjack.framework
|
||||
${BUILT_PRODUCTS_DIR}/CocoaMQTT/CocoaMQTT.framework
|
||||
${BUILT_PRODUCTS_DIR}/Differentiator/Differentiator.framework
|
||||
${BUILT_PRODUCTS_DIR}/HXPHPicker/HXPHPicker.framework
|
||||
${BUILT_PRODUCTS_DIR}/IQKeyboardCore/IQKeyboardCore.framework
|
||||
|
|
@ -19,6 +20,7 @@ ${BUILT_PRODUCTS_DIR}/MJRefresh/MJRefresh.framework
|
|||
${BUILT_PRODUCTS_DIR}/MarqueeLabel/MarqueeLabel.framework
|
||||
${BUILT_PRODUCTS_DIR}/Masonry/Masonry.framework
|
||||
${BUILT_PRODUCTS_DIR}/Moya/Moya.framework
|
||||
${BUILT_PRODUCTS_DIR}/MqttCocoaAsyncSocket/MqttCocoaAsyncSocket.framework
|
||||
${BUILT_PRODUCTS_DIR}/ObjectMapper/ObjectMapper.framework
|
||||
${BUILT_PRODUCTS_DIR}/Popover/Popover.framework
|
||||
${BUILT_PRODUCTS_DIR}/RxCocoa/RxCocoa.framework
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CocoaLumberjack.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CocoaMQTT.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Differentiator.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/HXPHPicker.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/IQKeyboardCore.framework
|
||||
|
|
@ -18,6 +19,7 @@ ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MJRefresh.framework
|
|||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MarqueeLabel.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Masonry.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Moya.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MqttCocoaAsyncSocket.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ObjectMapper.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Popover.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxCocoa.framework
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
${PODS_ROOT}/Target Support Files/Pods-QuickLocation/Pods-QuickLocation-frameworks.sh
|
||||
${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework
|
||||
${BUILT_PRODUCTS_DIR}/CocoaLumberjack/CocoaLumberjack.framework
|
||||
${BUILT_PRODUCTS_DIR}/CocoaMQTT/CocoaMQTT.framework
|
||||
${BUILT_PRODUCTS_DIR}/Differentiator/Differentiator.framework
|
||||
${BUILT_PRODUCTS_DIR}/HXPHPicker/HXPHPicker.framework
|
||||
${BUILT_PRODUCTS_DIR}/IQKeyboardCore/IQKeyboardCore.framework
|
||||
|
|
@ -19,6 +20,7 @@ ${BUILT_PRODUCTS_DIR}/MJRefresh/MJRefresh.framework
|
|||
${BUILT_PRODUCTS_DIR}/MarqueeLabel/MarqueeLabel.framework
|
||||
${BUILT_PRODUCTS_DIR}/Masonry/Masonry.framework
|
||||
${BUILT_PRODUCTS_DIR}/Moya/Moya.framework
|
||||
${BUILT_PRODUCTS_DIR}/MqttCocoaAsyncSocket/MqttCocoaAsyncSocket.framework
|
||||
${BUILT_PRODUCTS_DIR}/ObjectMapper/ObjectMapper.framework
|
||||
${BUILT_PRODUCTS_DIR}/Popover/Popover.framework
|
||||
${BUILT_PRODUCTS_DIR}/RxCocoa/RxCocoa.framework
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CocoaLumberjack.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CocoaMQTT.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Differentiator.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/HXPHPicker.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/IQKeyboardCore.framework
|
||||
|
|
@ -18,6 +19,7 @@ ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MJRefresh.framework
|
|||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MarqueeLabel.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Masonry.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Moya.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MqttCocoaAsyncSocket.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ObjectMapper.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Popover.framework
|
||||
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxCocoa.framework
|
||||
|
|
|
|||
|
|
@ -178,6 +178,7 @@ code_sign_if_enabled() {
|
|||
if [[ "$CONFIGURATION" == "Debug" ]]; then
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/CocoaLumberjack/CocoaLumberjack.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/CocoaMQTT/CocoaMQTT.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/Differentiator/Differentiator.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/HXPHPicker/HXPHPicker.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/IQKeyboardCore/IQKeyboardCore.framework"
|
||||
|
|
@ -196,6 +197,7 @@ if [[ "$CONFIGURATION" == "Debug" ]]; then
|
|||
install_framework "${BUILT_PRODUCTS_DIR}/MarqueeLabel/MarqueeLabel.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/Masonry/Masonry.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/Moya/Moya.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/MqttCocoaAsyncSocket/MqttCocoaAsyncSocket.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/ObjectMapper/ObjectMapper.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/Popover/Popover.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/RxCocoa/RxCocoa.framework"
|
||||
|
|
@ -221,6 +223,7 @@ fi
|
|||
if [[ "$CONFIGURATION" == "Release" ]]; then
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/CocoaLumberjack/CocoaLumberjack.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/CocoaMQTT/CocoaMQTT.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/Differentiator/Differentiator.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/HXPHPicker/HXPHPicker.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/IQKeyboardCore/IQKeyboardCore.framework"
|
||||
|
|
@ -239,6 +242,7 @@ if [[ "$CONFIGURATION" == "Release" ]]; then
|
|||
install_framework "${BUILT_PRODUCTS_DIR}/MarqueeLabel/MarqueeLabel.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/Masonry/Masonry.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/Moya/Moya.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/MqttCocoaAsyncSocket/MqttCocoaAsyncSocket.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/ObjectMapper/ObjectMapper.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/Popover/Popover.framework"
|
||||
install_framework "${BUILT_PRODUCTS_DIR}/RxCocoa/RxCocoa.framework"
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
|
|||
ARCHS = $(ARCHS_STANDARD)
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = NO
|
||||
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/CocoaLumberjack" "${PODS_CONFIGURATION_BUILD_DIR}/Differentiator" "${PODS_CONFIGURATION_BUILD_DIR}/HXPHPicker" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardCore" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardNotification" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardReturnManager" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbar" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbarManager" "${PODS_CONFIGURATION_BUILD_DIR}/IQTextInputViewNotification" "${PODS_CONFIGURATION_BUILD_DIR}/IQTextView" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "${PODS_CONFIGURATION_BUILD_DIR}/KingfisherWebP" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" "${PODS_CONFIGURATION_BUILD_DIR}/MJExtension" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" "${PODS_CONFIGURATION_BUILD_DIR}/MarqueeLabel" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry" "${PODS_CONFIGURATION_BUILD_DIR}/Moya" "${PODS_CONFIGURATION_BUILD_DIR}/ObjectMapper" "${PODS_CONFIGURATION_BUILD_DIR}/OpenIMSDK" "${PODS_CONFIGURATION_BUILD_DIR}/Popover" "${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa" "${PODS_CONFIGURATION_BUILD_DIR}/RxDataSources" "${PODS_CONFIGURATION_BUILD_DIR}/RxRelay" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwift" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwiftExt" "${PODS_CONFIGURATION_BUILD_DIR}/SDCycleScrollView" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage" "${PODS_CONFIGURATION_BUILD_DIR}/SGQRCode" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftDate" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftKeychainWrapper" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyUserDefaults" "${PODS_CONFIGURATION_BUILD_DIR}/TagListView" "${PODS_CONFIGURATION_BUILD_DIR}/URLNavigator" "${PODS_CONFIGURATION_BUILD_DIR}/YBImageBrowser" "${PODS_CONFIGURATION_BUILD_DIR}/YYImage" "${PODS_CONFIGURATION_BUILD_DIR}/libwebp" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios" "${PODS_ROOT}/AlipaySDK-iOS" "${PODS_ROOT}/OpenIMSDKCore/Framework" "${PODS_ROOT}/WechatOpenSDK-XCFramework" "${PODS_ROOT}/YYImage/Vendor" "${PODS_XCFRAMEWORKS_BUILD_DIR}/AlipaySDK-iOS" "${PODS_XCFRAMEWORKS_BUILD_DIR}/OpenIMSDKCore" "${PODS_XCFRAMEWORKS_BUILD_DIR}/WechatOpenSDK-XCFramework"
|
||||
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/CocoaLumberjack" "${PODS_CONFIGURATION_BUILD_DIR}/CocoaMQTT" "${PODS_CONFIGURATION_BUILD_DIR}/Differentiator" "${PODS_CONFIGURATION_BUILD_DIR}/HXPHPicker" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardCore" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardNotification" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardReturnManager" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbar" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbarManager" "${PODS_CONFIGURATION_BUILD_DIR}/IQTextInputViewNotification" "${PODS_CONFIGURATION_BUILD_DIR}/IQTextView" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "${PODS_CONFIGURATION_BUILD_DIR}/KingfisherWebP" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" "${PODS_CONFIGURATION_BUILD_DIR}/MJExtension" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" "${PODS_CONFIGURATION_BUILD_DIR}/MarqueeLabel" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry" "${PODS_CONFIGURATION_BUILD_DIR}/Moya" "${PODS_CONFIGURATION_BUILD_DIR}/MqttCocoaAsyncSocket" "${PODS_CONFIGURATION_BUILD_DIR}/ObjectMapper" "${PODS_CONFIGURATION_BUILD_DIR}/OpenIMSDK" "${PODS_CONFIGURATION_BUILD_DIR}/Popover" "${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa" "${PODS_CONFIGURATION_BUILD_DIR}/RxDataSources" "${PODS_CONFIGURATION_BUILD_DIR}/RxRelay" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwift" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwiftExt" "${PODS_CONFIGURATION_BUILD_DIR}/SDCycleScrollView" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage" "${PODS_CONFIGURATION_BUILD_DIR}/SGQRCode" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftDate" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftKeychainWrapper" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyUserDefaults" "${PODS_CONFIGURATION_BUILD_DIR}/TagListView" "${PODS_CONFIGURATION_BUILD_DIR}/URLNavigator" "${PODS_CONFIGURATION_BUILD_DIR}/YBImageBrowser" "${PODS_CONFIGURATION_BUILD_DIR}/YYImage" "${PODS_CONFIGURATION_BUILD_DIR}/libwebp" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios" "${PODS_ROOT}/AlipaySDK-iOS" "${PODS_ROOT}/OpenIMSDKCore/Framework" "${PODS_ROOT}/WechatOpenSDK-XCFramework" "${PODS_ROOT}/YYImage/Vendor" "${PODS_XCFRAMEWORKS_BUILD_DIR}/AlipaySDK-iOS" "${PODS_XCFRAMEWORKS_BUILD_DIR}/OpenIMSDKCore" "${PODS_XCFRAMEWORKS_BUILD_DIR}/WechatOpenSDK-XCFramework"
|
||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
|
||||
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/CocoaLumberjack/CocoaLumberjack.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Differentiator/Differentiator.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/HXPHPicker/HXPHPicker.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardCore/IQKeyboardCore.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift/IQKeyboardManagerSwift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardNotification/IQKeyboardNotification.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardReturnManager/IQKeyboardReturnManager.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbar/IQKeyboardToolbar.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbarManager/IQKeyboardToolbarManager.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQTextInputViewNotification/IQTextInputViewNotification.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQTextView/IQTextView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher/Kingfisher.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/KingfisherWebP/KingfisherWebP.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD/MBProgressHUD.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MJExtension/MJExtension.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh/MJRefresh.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MarqueeLabel/MarqueeLabel.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry/Masonry.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Moya/Moya.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/ObjectMapper/ObjectMapper.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/OpenIMSDK/OpenIMSDK.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Popover/Popover.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa/RxCocoa.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxDataSources/RxDataSources.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxRelay/RxRelay.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwift/RxSwift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwiftExt/RxSwiftExt.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDCycleScrollView/SDCycleScrollView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage/SDWebImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SGQRCode/SGQRCode.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit/SnapKit.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftDate/SwiftDate.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftKeychainWrapper/SwiftKeychainWrapper.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON/SwiftyJSON.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyUserDefaults/SwiftyUserDefaults.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/TagListView/TagListView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/URLNavigator/URLNavigator.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YBImageBrowser/YBImageBrowser.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYImage/YYImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/libwebp/libwebp.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios/Lottie.framework/Headers"
|
||||
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/CocoaLumberjack/CocoaLumberjack.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/CocoaMQTT/CocoaMQTT.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Differentiator/Differentiator.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/HXPHPicker/HXPHPicker.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardCore/IQKeyboardCore.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift/IQKeyboardManagerSwift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardNotification/IQKeyboardNotification.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardReturnManager/IQKeyboardReturnManager.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbar/IQKeyboardToolbar.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbarManager/IQKeyboardToolbarManager.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQTextInputViewNotification/IQTextInputViewNotification.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQTextView/IQTextView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher/Kingfisher.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/KingfisherWebP/KingfisherWebP.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD/MBProgressHUD.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MJExtension/MJExtension.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh/MJRefresh.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MarqueeLabel/MarqueeLabel.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry/Masonry.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Moya/Moya.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MqttCocoaAsyncSocket/MqttCocoaAsyncSocket.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/ObjectMapper/ObjectMapper.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/OpenIMSDK/OpenIMSDK.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Popover/Popover.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa/RxCocoa.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxDataSources/RxDataSources.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxRelay/RxRelay.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwift/RxSwift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwiftExt/RxSwiftExt.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDCycleScrollView/SDCycleScrollView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage/SDWebImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SGQRCode/SGQRCode.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit/SnapKit.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftDate/SwiftDate.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftKeychainWrapper/SwiftKeychainWrapper.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON/SwiftyJSON.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyUserDefaults/SwiftyUserDefaults.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/TagListView/TagListView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/URLNavigator/URLNavigator.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YBImageBrowser/YBImageBrowser.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYImage/YYImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/libwebp/libwebp.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios/Lottie.framework/Headers"
|
||||
LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/Frameworks' '@loader_path/Frameworks'
|
||||
LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift $(SDKROOT)/usr/lib/swift
|
||||
OTHER_LDFLAGS = $(inherited) -ObjC -l"c++" -l"resolv" -l"sqlite3.0" -l"swiftCoreGraphics" -l"z" -framework "AVFoundation" -framework "Accelerate" -framework "AdSupport" -framework "Alamofire" -framework "AlipaySDK" -framework "AssetsLibrary" -framework "CFNetwork" -framework "CocoaLumberjack" -framework "Combine" -framework "CoreFoundation" -framework "CoreGraphics" -framework "CoreLocation" -framework "CoreMotion" -framework "CoreTelephony" -framework "CoreText" -framework "Differentiator" -framework "ExternalAccessory" -framework "Foundation" -framework "GLKit" -framework "HXPHPicker" -framework "IQKeyboardCore" -framework "IQKeyboardManagerSwift" -framework "IQKeyboardNotification" -framework "IQKeyboardReturnManager" -framework "IQKeyboardToolbar" -framework "IQKeyboardToolbarManager" -framework "IQTextInputViewNotification" -framework "IQTextView" -framework "ImageIO" -framework "Kingfisher" -framework "KingfisherWebP" -framework "Lottie" -framework "MBProgressHUD" -framework "MJExtension" -framework "MJRefresh" -framework "MarqueeLabel" -framework "Masonry" -framework "MobileCoreServices" -framework "Moya" -framework "ObjectMapper" -framework "OpenGLES" -framework "OpenIMCore" -framework "OpenIMSDK" -framework "Photos" -framework "PhotosUI" -framework "Popover" -framework "QuartzCore" -framework "RxCocoa" -framework "RxDataSources" -framework "RxRelay" -framework "RxSwift" -framework "RxSwiftExt" -framework "SDCycleScrollView" -framework "SDWebImage" -framework "SGQRCode" -framework "Security" -framework "SnapKit" -framework "SwiftDate" -framework "SwiftKeychainWrapper" -framework "SwiftyJSON" -framework "SwiftyUserDefaults" -framework "SystemConfiguration" -framework "TagListView" -framework "UIKit" -framework "URLNavigator" -framework "WebKit" -framework "WechatOpenSDK" -framework "YBImageBrowser" -framework "YYImage" -framework "libwebp" -weak_framework "AppTrackingTransparency" -weak_framework "Combine" -weak_framework "Network" -weak_framework "SwiftUI"
|
||||
OTHER_MODULE_VERIFIER_FLAGS = $(inherited) "-F${PODS_CONFIGURATION_BUILD_DIR}/AMap3DMap" "-F${PODS_CONFIGURATION_BUILD_DIR}/AMapFoundation" "-F${PODS_CONFIGURATION_BUILD_DIR}/AMapLocation" "-F${PODS_CONFIGURATION_BUILD_DIR}/AMapSearch" "-F${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "-F${PODS_CONFIGURATION_BUILD_DIR}/AlipaySDK-iOS" "-F${PODS_CONFIGURATION_BUILD_DIR}/CocoaLumberjack" "-F${PODS_CONFIGURATION_BUILD_DIR}/Differentiator" "-F${PODS_CONFIGURATION_BUILD_DIR}/GTCommonSDK" "-F${PODS_CONFIGURATION_BUILD_DIR}/GYSDK" "-F${PODS_CONFIGURATION_BUILD_DIR}/HXPHPicker" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardCore" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardNotification" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardReturnManager" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbar" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbarManager" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQTextInputViewNotification" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQTextView" "-F${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "-F${PODS_CONFIGURATION_BUILD_DIR}/KingfisherWebP" "-F${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" "-F${PODS_CONFIGURATION_BUILD_DIR}/MJExtension" "-F${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" "-F${PODS_CONFIGURATION_BUILD_DIR}/MarqueeLabel" "-F${PODS_CONFIGURATION_BUILD_DIR}/Masonry" "-F${PODS_CONFIGURATION_BUILD_DIR}/Moya" "-F${PODS_CONFIGURATION_BUILD_DIR}/ObjectMapper" "-F${PODS_CONFIGURATION_BUILD_DIR}/OpenIMSDK" "-F${PODS_CONFIGURATION_BUILD_DIR}/OpenIMSDKCore" "-F${PODS_CONFIGURATION_BUILD_DIR}/Popover" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxDataSources" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxRelay" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxSwift" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxSwiftExt" "-F${PODS_CONFIGURATION_BUILD_DIR}/SDCycleScrollView" "-F${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage" "-F${PODS_CONFIGURATION_BUILD_DIR}/SGQRCode" "-F${PODS_CONFIGURATION_BUILD_DIR}/SnapKit" "-F${PODS_CONFIGURATION_BUILD_DIR}/SwiftDate" "-F${PODS_CONFIGURATION_BUILD_DIR}/SwiftKeychainWrapper" "-F${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON" "-F${PODS_CONFIGURATION_BUILD_DIR}/SwiftyUserDefaults" "-F${PODS_CONFIGURATION_BUILD_DIR}/TagListView" "-F${PODS_CONFIGURATION_BUILD_DIR}/URLNavigator" "-F${PODS_CONFIGURATION_BUILD_DIR}/WechatOpenSDK-XCFramework" "-F${PODS_CONFIGURATION_BUILD_DIR}/YBImageBrowser" "-F${PODS_CONFIGURATION_BUILD_DIR}/YYImage" "-F${PODS_CONFIGURATION_BUILD_DIR}/ZXSDK" "-F${PODS_CONFIGURATION_BUILD_DIR}/libwebp" "-F${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios"
|
||||
OTHER_LDFLAGS = $(inherited) -ObjC -l"c++" -l"resolv" -l"sqlite3.0" -l"swiftCoreGraphics" -l"z" -framework "AVFoundation" -framework "Accelerate" -framework "AdSupport" -framework "Alamofire" -framework "AlipaySDK" -framework "AssetsLibrary" -framework "CFNetwork" -framework "CocoaLumberjack" -framework "CocoaMQTT" -framework "Combine" -framework "CoreFoundation" -framework "CoreGraphics" -framework "CoreLocation" -framework "CoreMotion" -framework "CoreTelephony" -framework "CoreText" -framework "Differentiator" -framework "ExternalAccessory" -framework "Foundation" -framework "GLKit" -framework "HXPHPicker" -framework "IQKeyboardCore" -framework "IQKeyboardManagerSwift" -framework "IQKeyboardNotification" -framework "IQKeyboardReturnManager" -framework "IQKeyboardToolbar" -framework "IQKeyboardToolbarManager" -framework "IQTextInputViewNotification" -framework "IQTextView" -framework "ImageIO" -framework "Kingfisher" -framework "KingfisherWebP" -framework "Lottie" -framework "MBProgressHUD" -framework "MJExtension" -framework "MJRefresh" -framework "MarqueeLabel" -framework "Masonry" -framework "MobileCoreServices" -framework "Moya" -framework "MqttCocoaAsyncSocket" -framework "ObjectMapper" -framework "OpenGLES" -framework "OpenIMCore" -framework "OpenIMSDK" -framework "Photos" -framework "PhotosUI" -framework "Popover" -framework "QuartzCore" -framework "RxCocoa" -framework "RxDataSources" -framework "RxRelay" -framework "RxSwift" -framework "RxSwiftExt" -framework "SDCycleScrollView" -framework "SDWebImage" -framework "SGQRCode" -framework "Security" -framework "SnapKit" -framework "SwiftDate" -framework "SwiftKeychainWrapper" -framework "SwiftyJSON" -framework "SwiftyUserDefaults" -framework "SystemConfiguration" -framework "TagListView" -framework "UIKit" -framework "URLNavigator" -framework "WebKit" -framework "WechatOpenSDK" -framework "YBImageBrowser" -framework "YYImage" -framework "libwebp" -weak_framework "AppTrackingTransparency" -weak_framework "Combine" -weak_framework "Network" -weak_framework "SwiftUI"
|
||||
OTHER_MODULE_VERIFIER_FLAGS = $(inherited) "-F${PODS_CONFIGURATION_BUILD_DIR}/AMap3DMap" "-F${PODS_CONFIGURATION_BUILD_DIR}/AMapFoundation" "-F${PODS_CONFIGURATION_BUILD_DIR}/AMapLocation" "-F${PODS_CONFIGURATION_BUILD_DIR}/AMapSearch" "-F${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "-F${PODS_CONFIGURATION_BUILD_DIR}/AlipaySDK-iOS" "-F${PODS_CONFIGURATION_BUILD_DIR}/CocoaLumberjack" "-F${PODS_CONFIGURATION_BUILD_DIR}/CocoaMQTT" "-F${PODS_CONFIGURATION_BUILD_DIR}/Differentiator" "-F${PODS_CONFIGURATION_BUILD_DIR}/GTCommonSDK" "-F${PODS_CONFIGURATION_BUILD_DIR}/GYSDK" "-F${PODS_CONFIGURATION_BUILD_DIR}/HXPHPicker" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardCore" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardNotification" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardReturnManager" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbar" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbarManager" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQTextInputViewNotification" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQTextView" "-F${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "-F${PODS_CONFIGURATION_BUILD_DIR}/KingfisherWebP" "-F${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" "-F${PODS_CONFIGURATION_BUILD_DIR}/MJExtension" "-F${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" "-F${PODS_CONFIGURATION_BUILD_DIR}/MarqueeLabel" "-F${PODS_CONFIGURATION_BUILD_DIR}/Masonry" "-F${PODS_CONFIGURATION_BUILD_DIR}/Moya" "-F${PODS_CONFIGURATION_BUILD_DIR}/MqttCocoaAsyncSocket" "-F${PODS_CONFIGURATION_BUILD_DIR}/ObjectMapper" "-F${PODS_CONFIGURATION_BUILD_DIR}/OpenIMSDK" "-F${PODS_CONFIGURATION_BUILD_DIR}/OpenIMSDKCore" "-F${PODS_CONFIGURATION_BUILD_DIR}/Popover" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxDataSources" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxRelay" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxSwift" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxSwiftExt" "-F${PODS_CONFIGURATION_BUILD_DIR}/SDCycleScrollView" "-F${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage" "-F${PODS_CONFIGURATION_BUILD_DIR}/SGQRCode" "-F${PODS_CONFIGURATION_BUILD_DIR}/SnapKit" "-F${PODS_CONFIGURATION_BUILD_DIR}/SwiftDate" "-F${PODS_CONFIGURATION_BUILD_DIR}/SwiftKeychainWrapper" "-F${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON" "-F${PODS_CONFIGURATION_BUILD_DIR}/SwiftyUserDefaults" "-F${PODS_CONFIGURATION_BUILD_DIR}/TagListView" "-F${PODS_CONFIGURATION_BUILD_DIR}/URLNavigator" "-F${PODS_CONFIGURATION_BUILD_DIR}/WechatOpenSDK-XCFramework" "-F${PODS_CONFIGURATION_BUILD_DIR}/YBImageBrowser" "-F${PODS_CONFIGURATION_BUILD_DIR}/YYImage" "-F${PODS_CONFIGURATION_BUILD_DIR}/ZXSDK" "-F${PODS_CONFIGURATION_BUILD_DIR}/libwebp" "-F${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios"
|
||||
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
|
||||
PODS_BUILD_DIR = ${BUILD_DIR}
|
||||
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES
|
|||
ARCHS = $(ARCHS_STANDARD)
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
|
||||
ENABLE_USER_SCRIPT_SANDBOXING = NO
|
||||
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/CocoaLumberjack" "${PODS_CONFIGURATION_BUILD_DIR}/Differentiator" "${PODS_CONFIGURATION_BUILD_DIR}/HXPHPicker" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardCore" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardNotification" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardReturnManager" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbar" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbarManager" "${PODS_CONFIGURATION_BUILD_DIR}/IQTextInputViewNotification" "${PODS_CONFIGURATION_BUILD_DIR}/IQTextView" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "${PODS_CONFIGURATION_BUILD_DIR}/KingfisherWebP" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" "${PODS_CONFIGURATION_BUILD_DIR}/MJExtension" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" "${PODS_CONFIGURATION_BUILD_DIR}/MarqueeLabel" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry" "${PODS_CONFIGURATION_BUILD_DIR}/Moya" "${PODS_CONFIGURATION_BUILD_DIR}/ObjectMapper" "${PODS_CONFIGURATION_BUILD_DIR}/OpenIMSDK" "${PODS_CONFIGURATION_BUILD_DIR}/Popover" "${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa" "${PODS_CONFIGURATION_BUILD_DIR}/RxDataSources" "${PODS_CONFIGURATION_BUILD_DIR}/RxRelay" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwift" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwiftExt" "${PODS_CONFIGURATION_BUILD_DIR}/SDCycleScrollView" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage" "${PODS_CONFIGURATION_BUILD_DIR}/SGQRCode" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftDate" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftKeychainWrapper" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyUserDefaults" "${PODS_CONFIGURATION_BUILD_DIR}/TagListView" "${PODS_CONFIGURATION_BUILD_DIR}/URLNavigator" "${PODS_CONFIGURATION_BUILD_DIR}/YBImageBrowser" "${PODS_CONFIGURATION_BUILD_DIR}/YYImage" "${PODS_CONFIGURATION_BUILD_DIR}/libwebp" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios" "${PODS_ROOT}/AlipaySDK-iOS" "${PODS_ROOT}/OpenIMSDKCore/Framework" "${PODS_ROOT}/WechatOpenSDK-XCFramework" "${PODS_ROOT}/YYImage/Vendor" "${PODS_XCFRAMEWORKS_BUILD_DIR}/AlipaySDK-iOS" "${PODS_XCFRAMEWORKS_BUILD_DIR}/OpenIMSDKCore" "${PODS_XCFRAMEWORKS_BUILD_DIR}/WechatOpenSDK-XCFramework"
|
||||
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "${PODS_CONFIGURATION_BUILD_DIR}/CocoaLumberjack" "${PODS_CONFIGURATION_BUILD_DIR}/CocoaMQTT" "${PODS_CONFIGURATION_BUILD_DIR}/Differentiator" "${PODS_CONFIGURATION_BUILD_DIR}/HXPHPicker" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardCore" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardNotification" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardReturnManager" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbar" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbarManager" "${PODS_CONFIGURATION_BUILD_DIR}/IQTextInputViewNotification" "${PODS_CONFIGURATION_BUILD_DIR}/IQTextView" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "${PODS_CONFIGURATION_BUILD_DIR}/KingfisherWebP" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" "${PODS_CONFIGURATION_BUILD_DIR}/MJExtension" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" "${PODS_CONFIGURATION_BUILD_DIR}/MarqueeLabel" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry" "${PODS_CONFIGURATION_BUILD_DIR}/Moya" "${PODS_CONFIGURATION_BUILD_DIR}/MqttCocoaAsyncSocket" "${PODS_CONFIGURATION_BUILD_DIR}/ObjectMapper" "${PODS_CONFIGURATION_BUILD_DIR}/OpenIMSDK" "${PODS_CONFIGURATION_BUILD_DIR}/Popover" "${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa" "${PODS_CONFIGURATION_BUILD_DIR}/RxDataSources" "${PODS_CONFIGURATION_BUILD_DIR}/RxRelay" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwift" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwiftExt" "${PODS_CONFIGURATION_BUILD_DIR}/SDCycleScrollView" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage" "${PODS_CONFIGURATION_BUILD_DIR}/SGQRCode" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftDate" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftKeychainWrapper" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyUserDefaults" "${PODS_CONFIGURATION_BUILD_DIR}/TagListView" "${PODS_CONFIGURATION_BUILD_DIR}/URLNavigator" "${PODS_CONFIGURATION_BUILD_DIR}/YBImageBrowser" "${PODS_CONFIGURATION_BUILD_DIR}/YYImage" "${PODS_CONFIGURATION_BUILD_DIR}/libwebp" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios" "${PODS_ROOT}/AlipaySDK-iOS" "${PODS_ROOT}/OpenIMSDKCore/Framework" "${PODS_ROOT}/WechatOpenSDK-XCFramework" "${PODS_ROOT}/YYImage/Vendor" "${PODS_XCFRAMEWORKS_BUILD_DIR}/AlipaySDK-iOS" "${PODS_XCFRAMEWORKS_BUILD_DIR}/OpenIMSDKCore" "${PODS_XCFRAMEWORKS_BUILD_DIR}/WechatOpenSDK-XCFramework"
|
||||
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
|
||||
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/CocoaLumberjack/CocoaLumberjack.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Differentiator/Differentiator.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/HXPHPicker/HXPHPicker.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardCore/IQKeyboardCore.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift/IQKeyboardManagerSwift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardNotification/IQKeyboardNotification.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardReturnManager/IQKeyboardReturnManager.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbar/IQKeyboardToolbar.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbarManager/IQKeyboardToolbarManager.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQTextInputViewNotification/IQTextInputViewNotification.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQTextView/IQTextView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher/Kingfisher.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/KingfisherWebP/KingfisherWebP.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD/MBProgressHUD.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MJExtension/MJExtension.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh/MJRefresh.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MarqueeLabel/MarqueeLabel.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry/Masonry.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Moya/Moya.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/ObjectMapper/ObjectMapper.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/OpenIMSDK/OpenIMSDK.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Popover/Popover.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa/RxCocoa.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxDataSources/RxDataSources.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxRelay/RxRelay.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwift/RxSwift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwiftExt/RxSwiftExt.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDCycleScrollView/SDCycleScrollView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage/SDWebImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SGQRCode/SGQRCode.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit/SnapKit.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftDate/SwiftDate.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftKeychainWrapper/SwiftKeychainWrapper.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON/SwiftyJSON.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyUserDefaults/SwiftyUserDefaults.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/TagListView/TagListView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/URLNavigator/URLNavigator.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YBImageBrowser/YBImageBrowser.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYImage/YYImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/libwebp/libwebp.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios/Lottie.framework/Headers"
|
||||
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/CocoaLumberjack/CocoaLumberjack.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/CocoaMQTT/CocoaMQTT.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Differentiator/Differentiator.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/HXPHPicker/HXPHPicker.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardCore/IQKeyboardCore.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift/IQKeyboardManagerSwift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardNotification/IQKeyboardNotification.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardReturnManager/IQKeyboardReturnManager.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbar/IQKeyboardToolbar.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbarManager/IQKeyboardToolbarManager.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQTextInputViewNotification/IQTextInputViewNotification.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/IQTextView/IQTextView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher/Kingfisher.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/KingfisherWebP/KingfisherWebP.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD/MBProgressHUD.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MJExtension/MJExtension.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh/MJRefresh.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MarqueeLabel/MarqueeLabel.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry/Masonry.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Moya/Moya.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MqttCocoaAsyncSocket/MqttCocoaAsyncSocket.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/ObjectMapper/ObjectMapper.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/OpenIMSDK/OpenIMSDK.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Popover/Popover.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa/RxCocoa.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxDataSources/RxDataSources.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxRelay/RxRelay.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwift/RxSwift.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/RxSwiftExt/RxSwiftExt.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDCycleScrollView/SDCycleScrollView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage/SDWebImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SGQRCode/SGQRCode.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SnapKit/SnapKit.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftDate/SwiftDate.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftKeychainWrapper/SwiftKeychainWrapper.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON/SwiftyJSON.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/SwiftyUserDefaults/SwiftyUserDefaults.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/TagListView/TagListView.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/URLNavigator/URLNavigator.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YBImageBrowser/YBImageBrowser.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYImage/YYImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/libwebp/libwebp.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios/Lottie.framework/Headers"
|
||||
LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/Frameworks' '@loader_path/Frameworks'
|
||||
LIBRARY_SEARCH_PATHS = $(inherited) "${TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" /usr/lib/swift $(SDKROOT)/usr/lib/swift
|
||||
OTHER_LDFLAGS = $(inherited) -ObjC -l"c++" -l"resolv" -l"sqlite3.0" -l"swiftCoreGraphics" -l"z" -framework "AVFoundation" -framework "Accelerate" -framework "AdSupport" -framework "Alamofire" -framework "AlipaySDK" -framework "AssetsLibrary" -framework "CFNetwork" -framework "CocoaLumberjack" -framework "Combine" -framework "CoreFoundation" -framework "CoreGraphics" -framework "CoreLocation" -framework "CoreMotion" -framework "CoreTelephony" -framework "CoreText" -framework "Differentiator" -framework "ExternalAccessory" -framework "Foundation" -framework "GLKit" -framework "HXPHPicker" -framework "IQKeyboardCore" -framework "IQKeyboardManagerSwift" -framework "IQKeyboardNotification" -framework "IQKeyboardReturnManager" -framework "IQKeyboardToolbar" -framework "IQKeyboardToolbarManager" -framework "IQTextInputViewNotification" -framework "IQTextView" -framework "ImageIO" -framework "Kingfisher" -framework "KingfisherWebP" -framework "Lottie" -framework "MBProgressHUD" -framework "MJExtension" -framework "MJRefresh" -framework "MarqueeLabel" -framework "Masonry" -framework "MobileCoreServices" -framework "Moya" -framework "ObjectMapper" -framework "OpenGLES" -framework "OpenIMCore" -framework "OpenIMSDK" -framework "Photos" -framework "PhotosUI" -framework "Popover" -framework "QuartzCore" -framework "RxCocoa" -framework "RxDataSources" -framework "RxRelay" -framework "RxSwift" -framework "RxSwiftExt" -framework "SDCycleScrollView" -framework "SDWebImage" -framework "SGQRCode" -framework "Security" -framework "SnapKit" -framework "SwiftDate" -framework "SwiftKeychainWrapper" -framework "SwiftyJSON" -framework "SwiftyUserDefaults" -framework "SystemConfiguration" -framework "TagListView" -framework "UIKit" -framework "URLNavigator" -framework "WebKit" -framework "WechatOpenSDK" -framework "YBImageBrowser" -framework "YYImage" -framework "libwebp" -weak_framework "AppTrackingTransparency" -weak_framework "Combine" -weak_framework "Network" -weak_framework "SwiftUI"
|
||||
OTHER_MODULE_VERIFIER_FLAGS = $(inherited) "-F${PODS_CONFIGURATION_BUILD_DIR}/AMap3DMap" "-F${PODS_CONFIGURATION_BUILD_DIR}/AMapFoundation" "-F${PODS_CONFIGURATION_BUILD_DIR}/AMapLocation" "-F${PODS_CONFIGURATION_BUILD_DIR}/AMapSearch" "-F${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "-F${PODS_CONFIGURATION_BUILD_DIR}/AlipaySDK-iOS" "-F${PODS_CONFIGURATION_BUILD_DIR}/CocoaLumberjack" "-F${PODS_CONFIGURATION_BUILD_DIR}/Differentiator" "-F${PODS_CONFIGURATION_BUILD_DIR}/GTCommonSDK" "-F${PODS_CONFIGURATION_BUILD_DIR}/GYSDK" "-F${PODS_CONFIGURATION_BUILD_DIR}/HXPHPicker" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardCore" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardNotification" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardReturnManager" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbar" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbarManager" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQTextInputViewNotification" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQTextView" "-F${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "-F${PODS_CONFIGURATION_BUILD_DIR}/KingfisherWebP" "-F${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" "-F${PODS_CONFIGURATION_BUILD_DIR}/MJExtension" "-F${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" "-F${PODS_CONFIGURATION_BUILD_DIR}/MarqueeLabel" "-F${PODS_CONFIGURATION_BUILD_DIR}/Masonry" "-F${PODS_CONFIGURATION_BUILD_DIR}/Moya" "-F${PODS_CONFIGURATION_BUILD_DIR}/ObjectMapper" "-F${PODS_CONFIGURATION_BUILD_DIR}/OpenIMSDK" "-F${PODS_CONFIGURATION_BUILD_DIR}/OpenIMSDKCore" "-F${PODS_CONFIGURATION_BUILD_DIR}/Popover" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxDataSources" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxRelay" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxSwift" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxSwiftExt" "-F${PODS_CONFIGURATION_BUILD_DIR}/SDCycleScrollView" "-F${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage" "-F${PODS_CONFIGURATION_BUILD_DIR}/SGQRCode" "-F${PODS_CONFIGURATION_BUILD_DIR}/SnapKit" "-F${PODS_CONFIGURATION_BUILD_DIR}/SwiftDate" "-F${PODS_CONFIGURATION_BUILD_DIR}/SwiftKeychainWrapper" "-F${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON" "-F${PODS_CONFIGURATION_BUILD_DIR}/SwiftyUserDefaults" "-F${PODS_CONFIGURATION_BUILD_DIR}/TagListView" "-F${PODS_CONFIGURATION_BUILD_DIR}/URLNavigator" "-F${PODS_CONFIGURATION_BUILD_DIR}/WechatOpenSDK-XCFramework" "-F${PODS_CONFIGURATION_BUILD_DIR}/YBImageBrowser" "-F${PODS_CONFIGURATION_BUILD_DIR}/YYImage" "-F${PODS_CONFIGURATION_BUILD_DIR}/ZXSDK" "-F${PODS_CONFIGURATION_BUILD_DIR}/libwebp" "-F${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios"
|
||||
OTHER_LDFLAGS = $(inherited) -ObjC -l"c++" -l"resolv" -l"sqlite3.0" -l"swiftCoreGraphics" -l"z" -framework "AVFoundation" -framework "Accelerate" -framework "AdSupport" -framework "Alamofire" -framework "AlipaySDK" -framework "AssetsLibrary" -framework "CFNetwork" -framework "CocoaLumberjack" -framework "CocoaMQTT" -framework "Combine" -framework "CoreFoundation" -framework "CoreGraphics" -framework "CoreLocation" -framework "CoreMotion" -framework "CoreTelephony" -framework "CoreText" -framework "Differentiator" -framework "ExternalAccessory" -framework "Foundation" -framework "GLKit" -framework "HXPHPicker" -framework "IQKeyboardCore" -framework "IQKeyboardManagerSwift" -framework "IQKeyboardNotification" -framework "IQKeyboardReturnManager" -framework "IQKeyboardToolbar" -framework "IQKeyboardToolbarManager" -framework "IQTextInputViewNotification" -framework "IQTextView" -framework "ImageIO" -framework "Kingfisher" -framework "KingfisherWebP" -framework "Lottie" -framework "MBProgressHUD" -framework "MJExtension" -framework "MJRefresh" -framework "MarqueeLabel" -framework "Masonry" -framework "MobileCoreServices" -framework "Moya" -framework "MqttCocoaAsyncSocket" -framework "ObjectMapper" -framework "OpenGLES" -framework "OpenIMCore" -framework "OpenIMSDK" -framework "Photos" -framework "PhotosUI" -framework "Popover" -framework "QuartzCore" -framework "RxCocoa" -framework "RxDataSources" -framework "RxRelay" -framework "RxSwift" -framework "RxSwiftExt" -framework "SDCycleScrollView" -framework "SDWebImage" -framework "SGQRCode" -framework "Security" -framework "SnapKit" -framework "SwiftDate" -framework "SwiftKeychainWrapper" -framework "SwiftyJSON" -framework "SwiftyUserDefaults" -framework "SystemConfiguration" -framework "TagListView" -framework "UIKit" -framework "URLNavigator" -framework "WebKit" -framework "WechatOpenSDK" -framework "YBImageBrowser" -framework "YYImage" -framework "libwebp" -weak_framework "AppTrackingTransparency" -weak_framework "Combine" -weak_framework "Network" -weak_framework "SwiftUI"
|
||||
OTHER_MODULE_VERIFIER_FLAGS = $(inherited) "-F${PODS_CONFIGURATION_BUILD_DIR}/AMap3DMap" "-F${PODS_CONFIGURATION_BUILD_DIR}/AMapFoundation" "-F${PODS_CONFIGURATION_BUILD_DIR}/AMapLocation" "-F${PODS_CONFIGURATION_BUILD_DIR}/AMapSearch" "-F${PODS_CONFIGURATION_BUILD_DIR}/Alamofire" "-F${PODS_CONFIGURATION_BUILD_DIR}/AlipaySDK-iOS" "-F${PODS_CONFIGURATION_BUILD_DIR}/CocoaLumberjack" "-F${PODS_CONFIGURATION_BUILD_DIR}/CocoaMQTT" "-F${PODS_CONFIGURATION_BUILD_DIR}/Differentiator" "-F${PODS_CONFIGURATION_BUILD_DIR}/GTCommonSDK" "-F${PODS_CONFIGURATION_BUILD_DIR}/GYSDK" "-F${PODS_CONFIGURATION_BUILD_DIR}/HXPHPicker" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardCore" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardManagerSwift" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardNotification" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardReturnManager" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbar" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQKeyboardToolbarManager" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQTextInputViewNotification" "-F${PODS_CONFIGURATION_BUILD_DIR}/IQTextView" "-F${PODS_CONFIGURATION_BUILD_DIR}/Kingfisher" "-F${PODS_CONFIGURATION_BUILD_DIR}/KingfisherWebP" "-F${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" "-F${PODS_CONFIGURATION_BUILD_DIR}/MJExtension" "-F${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" "-F${PODS_CONFIGURATION_BUILD_DIR}/MarqueeLabel" "-F${PODS_CONFIGURATION_BUILD_DIR}/Masonry" "-F${PODS_CONFIGURATION_BUILD_DIR}/Moya" "-F${PODS_CONFIGURATION_BUILD_DIR}/MqttCocoaAsyncSocket" "-F${PODS_CONFIGURATION_BUILD_DIR}/ObjectMapper" "-F${PODS_CONFIGURATION_BUILD_DIR}/OpenIMSDK" "-F${PODS_CONFIGURATION_BUILD_DIR}/OpenIMSDKCore" "-F${PODS_CONFIGURATION_BUILD_DIR}/Popover" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxCocoa" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxDataSources" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxRelay" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxSwift" "-F${PODS_CONFIGURATION_BUILD_DIR}/RxSwiftExt" "-F${PODS_CONFIGURATION_BUILD_DIR}/SDCycleScrollView" "-F${PODS_CONFIGURATION_BUILD_DIR}/SDWebImage" "-F${PODS_CONFIGURATION_BUILD_DIR}/SGQRCode" "-F${PODS_CONFIGURATION_BUILD_DIR}/SnapKit" "-F${PODS_CONFIGURATION_BUILD_DIR}/SwiftDate" "-F${PODS_CONFIGURATION_BUILD_DIR}/SwiftKeychainWrapper" "-F${PODS_CONFIGURATION_BUILD_DIR}/SwiftyJSON" "-F${PODS_CONFIGURATION_BUILD_DIR}/SwiftyUserDefaults" "-F${PODS_CONFIGURATION_BUILD_DIR}/TagListView" "-F${PODS_CONFIGURATION_BUILD_DIR}/URLNavigator" "-F${PODS_CONFIGURATION_BUILD_DIR}/WechatOpenSDK-XCFramework" "-F${PODS_CONFIGURATION_BUILD_DIR}/YBImageBrowser" "-F${PODS_CONFIGURATION_BUILD_DIR}/YYImage" "-F${PODS_CONFIGURATION_BUILD_DIR}/ZXSDK" "-F${PODS_CONFIGURATION_BUILD_DIR}/libwebp" "-F${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios"
|
||||
OTHER_SWIFT_FLAGS = $(inherited) -D COCOAPODS
|
||||
PODS_BUILD_DIR = ${BUILD_DIR}
|
||||
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
|
||||
|
|
|
|||
|
|
@ -188,6 +188,9 @@
|
|||
30C4C01D2FDBF557009215C1 /* RemoveMemberViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30C4C01C2FDBF557009215C1 /* RemoveMemberViewModel.swift */; };
|
||||
30C4C0202FDC0EC5009215C1 /* GroupInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30C4C01F2FDC0EC5009215C1 /* GroupInfoView.swift */; };
|
||||
30C4C0222FDC0ED3009215C1 /* GroupInfoVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30C4C0212FDC0ED3009215C1 /* GroupInfoVC.swift */; };
|
||||
30D87CDB2FDFA9EE00E958FD /* MQTTService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30D87CDA2FDFA9EE00E958FD /* MQTTService.swift */; };
|
||||
30D87CDD2FDFF07500E958FD /* InteractionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30D87CDC2FDFF07500E958FD /* InteractionView.swift */; };
|
||||
30D87CDF2FDFF1A100E958FD /* QuickMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30D87CDE2FDFF1A100E958FD /* QuickMessageView.swift */; };
|
||||
30DC18522FD009CD0041DCD1 /* VipExpenseModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30DC18512FD009CD0041DCD1 /* VipExpenseModel.swift */; };
|
||||
30DC18542FD00C4A0041DCD1 /* VipRechargeVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30DC18532FD00C4A0041DCD1 /* VipRechargeVM.swift */; };
|
||||
30DC185A2FD11E7A0041DCD1 /* WebOperations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30DC18562FD11E7A0041DCD1 /* WebOperations.swift */; };
|
||||
|
|
@ -420,6 +423,9 @@
|
|||
30C4C01C2FDBF557009215C1 /* RemoveMemberViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoveMemberViewModel.swift; sourceTree = "<group>"; };
|
||||
30C4C01F2FDC0EC5009215C1 /* GroupInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupInfoView.swift; sourceTree = "<group>"; };
|
||||
30C4C0212FDC0ED3009215C1 /* GroupInfoVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupInfoVC.swift; sourceTree = "<group>"; };
|
||||
30D87CDA2FDFA9EE00E958FD /* MQTTService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MQTTService.swift; sourceTree = "<group>"; };
|
||||
30D87CDC2FDFF07500E958FD /* InteractionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InteractionView.swift; sourceTree = "<group>"; };
|
||||
30D87CDE2FDFF1A100E958FD /* QuickMessageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickMessageView.swift; sourceTree = "<group>"; };
|
||||
30DC18512FD009CD0041DCD1 /* VipExpenseModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VipExpenseModel.swift; sourceTree = "<group>"; };
|
||||
30DC18532FD00C4A0041DCD1 /* VipRechargeVM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VipRechargeVM.swift; sourceTree = "<group>"; };
|
||||
30DC18552FD11E7A0041DCD1 /* NavigationTitleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationTitleView.swift; sourceTree = "<group>"; };
|
||||
|
|
@ -472,6 +478,11 @@
|
|||
path = lotties;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
30D87CEF2FDFF52100E958FD /* TTGTagCollectionView */ = {
|
||||
isa = PBXFileSystemSynchronizedRootGroup;
|
||||
path = TTGTagCollectionView;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXFileSystemSynchronizedRootGroup section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
|
|
@ -765,6 +776,7 @@
|
|||
305A75282FCA8C7000227D26 /* Notification */,
|
||||
305A752A2FCA8C7000227D26 /* Theme */,
|
||||
305A752C2FCA8C7000227D26 /* URL */,
|
||||
30D87CD52FDF9F1900E958FD /* MQTT */,
|
||||
);
|
||||
path = Manager;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -852,6 +864,8 @@
|
|||
305A76292FCA8C7000227D26 /* HomeViewController.swift */,
|
||||
305A798B2FCAB99300227D26 /* HomeViewModel.swift */,
|
||||
30A7A9102FCAEE3D00105780 /* GroupListPopView.swift */,
|
||||
30D87CDE2FDFF1A100E958FD /* QuickMessageView.swift */,
|
||||
30D87CDC2FDFF07500E958FD /* InteractionView.swift */,
|
||||
);
|
||||
path = Home;
|
||||
sourceTree = "<group>";
|
||||
|
|
@ -1032,6 +1046,7 @@
|
|||
305A767D2FCA8C7000227D26 /* UIKit */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
30D87CEF2FDFF52100E958FD /* TTGTagCollectionView */,
|
||||
305A76482FCA8C7000227D26 /* Alert */,
|
||||
305A76502FCA8C7000227D26 /* EmptyDataSet */,
|
||||
305A76542FCA8C7000227D26 /* HUD */,
|
||||
|
|
@ -1174,6 +1189,14 @@
|
|||
path = GroupInfo;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
30D87CD52FDF9F1900E958FD /* MQTT */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
30D87CDA2FDFA9EE00E958FD /* MQTTService.swift */,
|
||||
);
|
||||
path = MQTT;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
30DC18582FD11E7A0041DCD1 /* Controller */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
|
@ -1313,6 +1336,7 @@
|
|||
);
|
||||
fileSystemSynchronizedGroups = (
|
||||
3070777D2FD2A214004C37CC /* lotties */,
|
||||
30D87CEF2FDFF52100E958FD /* TTGTagCollectionView */,
|
||||
);
|
||||
name = QuickLocation;
|
||||
productName = QuickLocation;
|
||||
|
|
@ -1457,6 +1481,7 @@
|
|||
30C4C0222FDC0ED3009215C1 /* GroupInfoVC.swift in Sources */,
|
||||
305A76902FCA8C7000227D26 /* UserAPI.swift in Sources */,
|
||||
30EFF3DE2FDA982C00EB35D4 /* EmergencyContactAddVC.swift in Sources */,
|
||||
30D87CDF2FDFF1A100E958FD /* QuickMessageView.swift in Sources */,
|
||||
305A76912FCA8C7000227D26 /* Constant.swift in Sources */,
|
||||
305A76922FCA8C7000227D26 /* LogUtils.swift in Sources */,
|
||||
305A76932FCA8C7000227D26 /* AddImageCell.swift in Sources */,
|
||||
|
|
@ -1620,6 +1645,7 @@
|
|||
305A76FB2FCA8C7000227D26 /* EmptyDataSet.swift in Sources */,
|
||||
305A76FC2FCA8C7000227D26 /* EmptyDataSetDelegate.swift in Sources */,
|
||||
305A76FD2FCA8C7000227D26 /* EmptyDataSetSource.swift in Sources */,
|
||||
30D87CDB2FDFA9EE00E958FD /* MQTTService.swift in Sources */,
|
||||
30EFF3CD2FDA668A00EB35D4 /* MyProfileView.swift in Sources */,
|
||||
305A76FE2FCA8C7000227D26 /* EmptyDataSetView.swift in Sources */,
|
||||
30C4C0192FDBF094009215C1 /* RemoveMemberVC.swift in Sources */,
|
||||
|
|
@ -1630,6 +1656,7 @@
|
|||
305A77032FCA8C7000227D26 /* ProgressHUD.swift in Sources */,
|
||||
305A77042FCA8C7000227D26 /* MXParallaxHeader.m in Sources */,
|
||||
305A77052FCA8C7000227D26 /* MXScrollView.m in Sources */,
|
||||
30D87CDD2FDFF07500E958FD /* InteractionView.swift in Sources */,
|
||||
30BAB8652FCD718A00C33B5C /* JoinGroupView.swift in Sources */,
|
||||
305A77062FCA8C7000227D26 /* MXScrollViewController.m in Sources */,
|
||||
305A77072FCA8C7000227D26 /* Helper.swift in Sources */,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "Group_1572@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "Group_1572@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 712 B |
|
After Width: | Height: | Size: 1.0 KiB |
22
QuickLocation/Assets.xcassets/Home/interaction_header.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "interaction_header@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "interaction_header@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
QuickLocation/Assets.xcassets/Home/interaction_header.imageset/interaction_header@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
QuickLocation/Assets.xcassets/Home/interaction_header.imageset/interaction_header@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 6.8 KiB |
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "Group_1951@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "Group_1951@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 3.6 KiB |
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"filename" : "Group_1947@2x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"filename" : "Group_1947@3x.png",
|
||||
"idiom" : "universal",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
|
@ -79,6 +79,11 @@ final class MainTabBarController: UITabBarController {
|
|||
guard let nav = selectedViewController as? UINavigationController else { return }
|
||||
customTabBar.isHidden = nav.viewControllers.count > 1
|
||||
}
|
||||
|
||||
/// 强制显示/隐藏 tabBar(供底部面板使用)
|
||||
func setTabBarHidden(_ hidden: Bool) {
|
||||
customTabBar.isHidden = hidden
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - QuickLocationTabBarDelegate
|
||||
|
|
|
|||
|
|
@ -0,0 +1,257 @@
|
|||
//
|
||||
// MQTTService.swift
|
||||
// QuickLocation
|
||||
//
|
||||
// Created by 八条 on 2026/6/12.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CocoaMQTT
|
||||
import UIKit
|
||||
import _LocationEssentials
|
||||
|
||||
// MARK: - MQTT 模型
|
||||
|
||||
/// 位置上报类型
|
||||
enum MqttType: String, Codable {
|
||||
case track = "track" // 追踪(获取)和更新用户状态
|
||||
case disconnect = "disconnect"
|
||||
case message = "message"
|
||||
case join = "join" // 加入群
|
||||
case leave = "leave" // 离开群
|
||||
case dismiss = "dismiss" // 解散群
|
||||
case kick = "kick" // 踢人
|
||||
case signIn = "signin" // 签到
|
||||
case sos = "sos" // 求助
|
||||
case needTrack = "needtrack" // 追踪上报
|
||||
case emote = "emote" // 接收表情
|
||||
}
|
||||
|
||||
/// 单点位置
|
||||
struct Points: Codable {
|
||||
let lat: Double
|
||||
let lon: Double
|
||||
let addr: String
|
||||
let time: Int64 // 毫秒时间戳
|
||||
let speed: Double
|
||||
let bearing: Double
|
||||
let altitude: Double
|
||||
let accuracy: Double
|
||||
}
|
||||
|
||||
/// 位置上报数据(发送)
|
||||
struct MqttLocation: Codable {
|
||||
let battery: String
|
||||
let points: [Points]
|
||||
}
|
||||
|
||||
/// MQTT 收到的位置消息(接收解析用)
|
||||
struct MqttIncomingMessage: Decodable {
|
||||
let type: String?
|
||||
let data: MqttIncomingData?
|
||||
let extra: String?
|
||||
}
|
||||
|
||||
struct MqttIncomingData: Decodable {
|
||||
let points: [Points]?
|
||||
let battery: String?
|
||||
}
|
||||
|
||||
// MARK: - MQTTService
|
||||
|
||||
/// MQTT 5.0 服务,管理连接、订阅和消息收发
|
||||
final class MQTTService: NSObject {
|
||||
|
||||
static let shared = MQTTService()
|
||||
|
||||
private var mqtt: CocoaMQTT5?
|
||||
private var isConnected = false
|
||||
|
||||
// MARK: - 连接状态回调
|
||||
var onConnected: (() -> Void)?
|
||||
var onDisconnected: (() -> Void)?
|
||||
|
||||
/// 全局接收回调(所有未匹配 topicCallback 的消息)
|
||||
var onMessageReceived: ((CocoaMQTT5Message, UInt16, MqttPublishProperties) -> Void)?
|
||||
|
||||
/// 按 topic 订阅的回调(优先匹配)
|
||||
private var topicCallbacks: [String: (CocoaMQTT5Message) -> Void] = [:]
|
||||
|
||||
// MARK: - 配置
|
||||
private var host: String { "emqx.batiao8.com" }
|
||||
private var port: UInt16 { 1883 }
|
||||
/// 当前 clientID,切换用户时可更新后重连
|
||||
private(set) var clientID: String = ""
|
||||
private var userName = "batiao"
|
||||
private var password = "Batiao12B"
|
||||
private var topic = "smartdrive/"
|
||||
|
||||
override private init() {}
|
||||
|
||||
// MARK: - 连接
|
||||
func connect() {
|
||||
guard !isConnected else { return }
|
||||
|
||||
if clientID.isEmpty {
|
||||
clientID = "smartdrive_\(AppContextManager.shared.userId)"
|
||||
}
|
||||
let mqtt = CocoaMQTT5(clientID: clientID, host: host, port: port)
|
||||
mqtt.username = userName
|
||||
mqtt.password = password
|
||||
mqtt.keepAlive = 60
|
||||
mqtt.autoReconnect = true
|
||||
mqtt.autoReconnectTimeInterval = 5
|
||||
mqtt.delegate = self
|
||||
mqtt.logLevel = .warning
|
||||
|
||||
self.mqtt = mqtt
|
||||
_ = mqtt.connect()
|
||||
}
|
||||
|
||||
// MARK: - 断开
|
||||
func disconnect() {
|
||||
mqtt?.disconnect()
|
||||
isConnected = false
|
||||
}
|
||||
|
||||
// MARK: - 切换用户
|
||||
/// 更新 clientID 并重连(切换用户后调用)
|
||||
func updateClientID(_ newID: String) {
|
||||
clientID = newID
|
||||
disconnect()
|
||||
connect()
|
||||
}
|
||||
|
||||
// MARK: - 订阅主题
|
||||
/// - Parameters:
|
||||
/// - topic: 主题
|
||||
/// - qos: 服务质量
|
||||
/// - callback: 可选,该 topic 的专用回调,收到消息时优先于此回调
|
||||
func subscribe(topic: String, qos: CocoaMQTTQoS = .qos1, callback: ((CocoaMQTT5Message) -> Void)? = nil) {
|
||||
let subscription = MqttSubscription(topic: topic, qos: qos)
|
||||
mqtt?.subscribe([subscription])
|
||||
if let cb = callback {
|
||||
topicCallbacks[topic] = cb
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - 取消订阅
|
||||
func unsubscribe(topic: String) {
|
||||
mqtt?.unsubscribe(topic)
|
||||
topicCallbacks.removeValue(forKey: topic)
|
||||
}
|
||||
|
||||
/// 订阅指定圈子的所有成员位置 topic: smartdrive/<memberId>
|
||||
func subscribeGroupMembers(_ memberIds: [String]) {
|
||||
for id in memberIds {
|
||||
subscribe(topic: "\(topic)\(id)")
|
||||
}
|
||||
}
|
||||
|
||||
/// 取消订阅上一批成员
|
||||
func unsubscribeGroupMembers(_ memberIds: [String]) {
|
||||
for id in memberIds {
|
||||
mqtt?.unsubscribe("\(topic)\(id)")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - 发布消息
|
||||
@discardableResult
|
||||
func publish(topic: String, message: String, qos: CocoaMQTTQoS = .qos1) -> Int {
|
||||
let properties = MqttPublishProperties()
|
||||
return mqtt?.publish(topic, withString: message, qos: qos, DUP: false, retained: false, properties: properties) ?? -1
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
func publish(topic: String, data: Data, qos: CocoaMQTTQoS = .qos1) -> Int {
|
||||
let properties = MqttPublishProperties()
|
||||
let message = CocoaMQTT5Message(topic: topic, payload: [UInt8](data))
|
||||
return mqtt?.publish(message, DUP: false, retained: false, properties: properties) ?? -1
|
||||
}
|
||||
|
||||
// MARK: - 位置上报
|
||||
/// 构建并上报位置数据(格式与 Android 一致)
|
||||
func reportLocation(lat: Double, lon: Double, addr: String,
|
||||
speed: CLLocationSpeed, bearing: CLLocationDirection,
|
||||
altitude: CLLocationDistance, accuracy: CLLocationAccuracy) {
|
||||
let battery = UIDevice.current.batteryLevel > 0
|
||||
? Int(UIDevice.current.batteryLevel * 100)
|
||||
: 0
|
||||
|
||||
let point = Points(
|
||||
lat: lat, lon: lon, addr: addr,
|
||||
time: Int64(Date().timeIntervalSince1970 * 1000),
|
||||
speed: speed, bearing: bearing,
|
||||
altitude: altitude, accuracy: accuracy
|
||||
)
|
||||
let location = MqttLocation(battery: battery.string, points: [point])
|
||||
|
||||
guard let jsonData = try? JSONEncoder().encode(location),
|
||||
let dataDict = try? JSONSerialization.jsonObject(with: jsonData) as? [String: Any]
|
||||
else { return }
|
||||
|
||||
// 外层包装,data 是 JSON 对象而非字符串
|
||||
let payload: [String: Any] = [
|
||||
"type": MqttType.track.rawValue,
|
||||
"extra": "",
|
||||
"data": dataDict
|
||||
]
|
||||
|
||||
publish(topic: "\(topic)\(AppContextManager.shared.userId)", message: payload.toJsonString())
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - CocoaMQTT5Delegate
|
||||
extension MQTTService: CocoaMQTT5Delegate {
|
||||
|
||||
func mqtt5(_ mqtt5: CocoaMQTT5, didConnectAck ack: CocoaMQTTCONNACKReasonCode, connAckData: MqttDecodeConnAck?) {
|
||||
isConnected = true
|
||||
print("MQTT5 connected: \(ack)")
|
||||
onConnected?()
|
||||
}
|
||||
|
||||
func mqtt5(_ mqtt5: CocoaMQTT5, didPublishMessage message: CocoaMQTT5Message, id: UInt16) {
|
||||
print("MQTT5 published: \(message.topic)")
|
||||
}
|
||||
|
||||
func mqtt5(_ mqtt5: CocoaMQTT5, didPublishAck id: UInt16, pubAckData: MqttDecodePubAck?) {
|
||||
print("MQTT5 publish ack: \(id)")
|
||||
}
|
||||
|
||||
func mqtt5(_ mqtt5: CocoaMQTT5, didPublishRec id: UInt16, pubRecData: MqttDecodePubRec?) {}
|
||||
|
||||
func mqtt5(_ mqtt5: CocoaMQTT5, didReceiveMessage message: CocoaMQTT5Message, id: UInt16, publishData: MqttDecodePublish?) {
|
||||
// 优先 topic 专用回调
|
||||
if let cb = topicCallbacks[message.topic] {
|
||||
cb(message)
|
||||
return
|
||||
}
|
||||
// 没有专用回调时走全局回调
|
||||
if let payload = message.string {
|
||||
print("MQTT5 received on \(message.topic): \(payload)")
|
||||
}
|
||||
onMessageReceived?(message, id, MqttPublishProperties())
|
||||
}
|
||||
|
||||
func mqtt5(_ mqtt5: CocoaMQTT5, didSubscribeTopics success: NSDictionary, failed: [String], subAckData: MqttDecodeSubAck?) {
|
||||
print("MQTT5 subscribe success: \(success), failed: \(failed)")
|
||||
}
|
||||
|
||||
func mqtt5(_ mqtt5: CocoaMQTT5, didUnsubscribeTopics topics: [String], unsubAckData: MqttDecodeUnsubAck?) {
|
||||
print("MQTT5 unsubscribe: \(topics)")
|
||||
}
|
||||
|
||||
func mqtt5(_ mqtt5: CocoaMQTT5, didReceiveDisconnectReasonCode reasonCode: CocoaMQTTDISCONNECTReasonCode) {}
|
||||
|
||||
func mqtt5(_ mqtt5: CocoaMQTT5, didReceiveAuthReasonCode reasonCode: CocoaMQTTAUTHReasonCode) {}
|
||||
|
||||
func mqtt5DidPing(_ mqtt5: CocoaMQTT5) {}
|
||||
|
||||
func mqtt5DidReceivePong(_ mqtt5: CocoaMQTT5) {}
|
||||
|
||||
func mqtt5DidDisconnect(_ mqtt5: CocoaMQTT5, withError err: Error?) {
|
||||
isConnected = false
|
||||
print("MQTT5 disconnected: \(err?.localizedDescription ?? "")")
|
||||
onDisconnected?()
|
||||
}
|
||||
}
|
||||
|
|
@ -185,6 +185,8 @@ struct GroupMemberModel: Mappable, Equatable {
|
|||
}
|
||||
/// 是否在线
|
||||
var is_online: Bool = false
|
||||
/// 最后在线文本(MQTT 计算)
|
||||
var lastUpdateText: String = ""
|
||||
/// 设备电量
|
||||
var battery: String = ""
|
||||
/// 最后在线时间
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@
|
|||
#import "MXScrollView.h"
|
||||
#import "MXScrollViewController.h"
|
||||
|
||||
#import "TTGTextTagCollectionView.h"
|
||||
|
||||
// Pop
|
||||
#import "DLViewTransition.h"
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ class GroupInfoVC: BaseViewController {
|
|||
|
||||
rootView.groupIcon.image = model.groupIcon
|
||||
rootView.groupNameLab.text = model.name
|
||||
|
||||
rootView.setupData(model)
|
||||
rootView.applyBtn.rx.tap.subscribe(onNext: { _ in
|
||||
self.requestOperateGroup()
|
||||
}).disposed(by: disposeBag)
|
||||
|
|
|
|||