概述
Courier是一个用于通过MQTT创建长期连接的库,MQTT是物联网消息传递的行业标准。长期连接是客户端和服务器之间建立的持久连接,用于双向通信。在保持存活数据包的帮助下,尽可能地维护长期连接以提供即时更新。这也有助于节省移动设备上的电池和数据。
详细文档
请在此处找到详细文档 - https://gojek.github.io/courier-iOS/
端到端Courier示例 - https://gojek.github.io/courier/docs/Introduction
入门指南
设置Courier以实现iOS设备和代理之间的双向长期连接来订阅、发送和接收消息。
示例应用
您可以通过运行示例应用连接到任何可配置的代理。从方案中选择 CourierE2EApp
。
安装
Courier 支持使用 Cocoapods 和 SPM 进行依赖管理。它分为 5 个模块。
CourierCore
:包含Courier的公共API,如协议和数据类型。其他模块对此模块有基本依赖。如果您想在没有添加Courier实现的情况下在您的项目中实现接口,则可以使用此模块。CourierMQTT
:使用MQTT
实现了CourierClient
和CourierSession
。此模块对MQTTClientGJ
有依赖关系。MQTTClientGJ
:开源库MQTT-Client-Framework的分支版本。它增加了连接和空闲超时等功能。它还修复了在MQTTSocketEncoder
和Connack
状态5没有在调用MQTTTransportDidClose
之前完成解码的竞争条件崩溃错误。CourierProtobuf
:使用Protofobuf
实现ProtobufMessageAdapter
。此模块对SwiftProtobuf
库有依赖关系,这是可选的
,如果正在使用protobuf进行数据序列化,则可以使用。CourierMQTTChuck
:可用于检查底层MQTT连接的入站或出站数据包。它拦截所有数据包,持久化它们并提供一个界面,用于访问已发送或接收的所有MQTT数据包。它还提供搜索、共享和清除数据等功能。在下面使用SwiftUI
。
Cocoapods
// Podfile
target 'Example-App' do
use_frameworks!
pod 'CourierCore'
pod 'CourierMQTT'
pod 'CourierProtobuf' #optional
pod 'CourierMQTTChuck' #optional
end
Swift 包管理器 (SPM)
简单地将包依赖添加到 Package.swift 中,并在相关目标中依赖 CourierCore
和 CourierMQTT
dependencies: [
.package(url: "https://github.com/gojek/courier-iOS", branch: "main")
]
实现 IConnectionServiceProvider 以提供身份验证的 ConnectOptions
要连接到 MQTT 代理,您需要实现 IConnectionServiceProvider。首先,您需要实现 IConnectionServiceProvider/clientId
并返回一个用于标识您的客户端的唯一字符串。这对于每个连接到代理的设备必须是唯一的。
var clientId: String {
UIDevice.current.identifierForVendor?.uuidString ?? UUID().uuidString
}
接下来,您需要实现 IConnectionServiceProvider/getConnectOptions(completion:)
方法。您需要提供将用于连接到代理的 ConnectOptions
实例。该方法提供了一个逃逸闭包,以便在需要从远程 API 异步检索凭证的情况下使用。
func getConnectOptions(completion: @escaping (Result<ConnectOptions, AuthError>) -> Void) {
executeNetworkRequest { (response: ConnectOptions) in
completion(.success(connectOptions))
} failure: { _, _, error in
completion(.failure(error))
}
}
以下是您需要在 ConnectOptions 中提供的数据。
/// IP Host address of the broker
public let host: String
/// Port of the broker
public let port: UInt16
/// Keep Alive interval used to ping the broker over time to maintain the long run connection
public let keepAlive: UInt16
/// Unique Client ID used by broker to identify connected clients
public let clientId: String
/// Username of the client
public let username: String
/// Password of the client used for authentication by the broker
public let password: String
/// Tells broker whether to clear the previous session by the clients
public let isCleanSession: Bool
使用 CourierClientFactory 配置并创建 MQTT CourierClient 实例
接下来,我们需要创建使用 MQTT 作为其实现的 CourierClient 的实例。初始化 CourierClientFactory
实例并调用 CourierClientFactory/makeMQTTClient(config:)
。我们需要传递 MQTTClientConfig 实例,其中包含多个可定制的参数。
let clientFactory = CourierClientFactory()
let courierClient = clientFactory.makeMQTTClient(
config: MQTTClientConfig(
authService: HiveMQAuthService(),
messageAdapters: [
JSONMessageAdapter(),
ProtobufMessageAdapter()
],
autoReconnectInterval: 1,
maxAutoReconnectInterval: 30
)
)
MQTTClientConfig/messageAdapters
:我们需要传递数组中的MessageAdapter
。这将用于从代理接收消息时的序列化以及在代理发送消息时的序列化。CourierMQTT
为符合Codable
协议的 JSON(JSONMessageAdapter)
和 Plist(PlistMessageAdapter)
格式提供了内置的消息适配器。您只能使用其中一个,因为它们都实现了 Codable 以避免冲突。要使用 protobuf,请导入CourierProtobuf
并传入ProtobufMessageAdapter
。MQTTClientConfig/authService
:我们需要传递自己实现的 IConnectionServiceProvider 协议来提供 ConnectOptions,以供客户端使用。MQTTClientConfig/autoReconnectInterval
当连接丢失时用来重连到代理的间隔。每次未能成功连接时,该值将乘以 2,直到成功连接。上限基于MQTTClientConfig/maxAutoReconnectInterval
。
在CourierClient中管理连接生命周期
要连接到代理,您只需调用 connect
方法
courierClient.connect()
要断开连接,只需调用 disconnect
方法
courierClient.disconnect()
要获取 ConnectionState,您可以访问 CourierSession/connectionState 属性
courierClient.connectionState
您还可以使用 CourierSession/connectionStatePublisher
属性订阅 ConnectionState
发布者。Courier 提供的可观察 API 与 Apple Combine
非常相似,尽管它是通过内部使用 RxSwift
实现的,因此我们可以支持 iOS 12
。
courierClient.connectionStatePublisher
.sink { [weak self] self?.handleConnectionStateEvent($0) }
.store(in: &cancellables)
由于 MQTT 支持在无互联网连接和用户重新连接到代理时确保可投递性的 QoS 1 和 QoS 2 消息,我们也会在本地缓存中持久化这些消息。要断开并删除所有这些缓存,您可以调用。
courierClient.destroy()
在使用 Courier 时,您需要牢记以下几点
- Courier 在应用进入后台时始终会断开,因为 iOS 不支持在后台长时间运行套接字连接。
- 当应用进入前台且有订阅主题时,Courier 总是会自动重新连接。
- Courier 使用 Reachability 框架处理因坏/丢失互联网连接导致的重连。
- Courier 会根据可配置的 TTL 持久 QoS > 0 消息,以防没有活跃订阅 Observable/Publisher。
MQTT 中的 QoS 级别
服务质量 (QoS) 级别是消息发送方和接收方之间的一种协议,用于定义特定消息的交付保证。MQTT 中有 3 个 QoS 级别
- 最多一次(0)
- 至少一次(1)
- 恰好一次(2)。
当谈论 MQTT 中的 QoS 时,您需要考虑消息传递的两个方面
- 从发布客户端到代理的消息传递
- 从代理到订阅客户端的消息传递
您可以从 HiveMQ 网站了解更多关于 MQTT 中 QoS 的详细信息。
从代理订阅主题
要从代理订阅主题,可以调用CourierSession/subscribe(_:)
方法,并传入包含主题字符串和QoS枚举元组的元组。
courierClient.subscribe(("chat/user1", QoS.zero))
您还可以订阅多个主题,调用CourierSession/subscribe(_:)
方法,并传入包含主题字符串和QoS枚举元组的元组数组。
courierClient.subscribe([
("chat/user1", QoS.zero),
("order/1234", QoS.one),
("order/123456", QoS.two),
])
从已订阅主题接收消息
订阅主题后,您需要通过CourierSession/messagePublisher(topic:)
方法订阅消息发布者,并传入相关的主题。此方法使用Generic
将二进制数据进行序列化到类型。请确保您已提供可解码数据的关联消息适配器。
courierClient.messagePublisher(topic: topic)
.sink { [weak self] (note: Note) in
self?.messages.insert(Message(id: UUID().uuidString, name: "Protobuf: \(note.title)", timestamp: Date()), at: 0)
}.store(in: &cancellables)
此方法返回AnyPublisher,您可以使用类似AnyPublisher/filter(predicate:)
或AnyPublisher/map(transform:)
的算子来链式调用。
Courier提供的可观察API与Apple Combine非常相似,尽管它是使用RxSwift内部实现的,所以我们支持iOS 12,仅支持filter
和map
运算符。
从主题取消订阅
要从主题取消订阅,可以调用CourierSession/unsubscribe(_:)
方法,并传入主题字符串。
courierClient.unsubscribe("chat/user1")
您还可以取消订阅多个主题,调用CourierSession/unsubscribe(_:)
方法,并传入包含主题字符串和QoS枚举元组的元组数组。
courierClient.unsubscribe([
"chat/user1",
"order/"
])
向代理发送消息
要将消息发送到代理,首先请确保您已提供了一个能够将您的对象编码为二进制数据格式的 MessageAdapter
。例如,如果您有一个要发送为 JSON 的数据结构。请确保它符合 Encodable
协议,并在创建 CourierClient
实例时,将 JSONMessageAdapter
传递给 MQTTClientConfig
。
您只需调用 CourierSession/publishMessageHING:topic:qos:)
,传入主题字符串和 QoS 枚举。这是一个 throwing
函数,如果编码失败可能会抛出异常。
let message = Message(
id: UUID().uuidString,
name: message,
timestamp: Date()
)
try? courierService?.publishMessage(
message,
topic: "chat/1234",
qos: QoS.zero
)
监听 Courier 内部事件
要监听Courier系统事件,例如CourierEvent/connectionSuccess
、CourierEvent/connectionAttempt
等在CourierEvent
枚举中声明的更多情况,您可以实现ICourierEventHandler
协议并实现ICourierEventHandler/onEvent(_:)
方法。这个方法将被所有Courier系统事件调用。
最后,确保对实例有强引用,并将实例传递给CourierEventManager/addEventHandler(_:)
调用。
courierClient.addEventHandler(analytics)
监控 Courier MQTT 数据包日志
CourierMQTTChuck
用于检查底层 MQTT 连接的所有传入或传出数据包。
它拦截所有数据包,将它们保存并提供一个用户界面来访问所有发送或接收的 MQTT 数据包。它还提供多个其他功能,如搜索、分享和清除数据。
它使用 SwiftUI 作为底层,iOS 的最小部署版本为 15,您仍然可以构建它以在 iOS 11 上访问记录器,而无需视图。
使用方法
将依赖项添加到 CourierMQTTChuck
并像这样声明记录器
import CourierMQTTChuck
let logger = MQTTChuckLogger()
然后在 SwiftUI 视图中传递 logger
声明 MQTTChuckView
。
.sheet(isPresented: $showChuckView, content: {
NavigationView {
MQTTChuckView(logger: logger)
}
})
贡献指南
请阅读我们的贡献指南了解我们的开发流程、如何提出修复和改进建议,以及如何构建和测试Courier iOS库的更改。