概览
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,并在必要的targets中依赖 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
用来在连接丢失时重新连接到代理服务器的间隔。每次连接失败后,这个间隔将会翻倍,直到成功连接。上限由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在 Active_subscription 到 Observable/Publisher 使用可配置的 TTL(生存时间)时,将持久化 QoS > 0 消息。
MQTT中的QoS级别
服务质量 (QoS) 级别是消息发送方和接收方之间的一种协议,它定义了特定消息的传输保证。MQTT中有 3 个 QoS 级别:
- 最多一次 (0)
- 至少一次 (1)
- 恰好一次 (2)。
当您在 MQTT 中提到 QoS 时,需要考虑消息传输的两个方面
- 从发布客户端到代理的消息传输。
- 从代理到订阅客户端的消息传输。
您可以从 HiveMQ 网站(https://www.hivemq.com/blog/mqtt-essentials-part-6-mqtt-quality-of-service-levels/)了解更多关于 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
实例时,在 MQTTClientConfig
中传递 JSONMessageAdapter
。
您只需调用 CourierSession/publishMessage(_: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数据包的UI。它还提供多种其他功能,如搜索、分享和清除数据。
它在内部使用 SwiftUI,最低iOS部署版本为15,您仍然可以在iOS 11上构建它,以在不使用视图的情况下访问日志。
使用方法
将依赖项添加到 CourierMQTTChuck
,然后如下声明记录器
import CourierMQTTChuck
let logger = MQTTChuckLogger()
然后在使用SwiftUI视图的代码中声明MQTTChuckView
,并传递logger
。
.sheet(isPresented: $showChuckView, content: {
NavigationView {
MQTTChuckView(logger: logger)
}
})
捐献指南
阅读我们的捐献指南,了解我们的开发过程、如何提出错误修复和改进建议,以及如何构建和测试您的Courier iOS库更改。
许可协议
除MQTTClientGJ模块外,所有Courier模块均遵循MIT许可协议。MQTTClientGJ遵循Eclipse许可协议。