CourierCore 0.0.26

CourierCore 0.0.26

Alfian Losarialfian 维护。



  • Alfian Losari

Build and Smoketest Status Documentation Maintenance GitHub Release Date GitHub last commit Discord : Gojek Courier

概述

Courier 是一个用于创建使用物联网消息传输行业标准 MQTT ( MQTT.org ) 的长期运行的连接的库。长期运行的连接是在客户端和服务器之间为双向通信建立的持久连接。长期运行的连接通过心跳数据包保持尽可能长时间,以实现即时更新。这也有助于减少移动设备上的电池和数据的消耗。

详细文档

详细文档请在这里找到 - 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 实现 CourierClientCourierSession。此模块依赖 MQTTClientGJ
  • MQTTClientGJ:开源库 MQTT-Client-Framework 的一个分支版本。它添加了连接和空闲超时等功能。它还修复了 MQTTSocketEncoder 中的竞态条件崩溃,以及 Connack 状态 5 在调用 MQTTTransportDidClose 之前未完成解码的错误。
  • CourierProtobuf:使用 Protobuf 实现 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 中,并在必要的目标中依赖 CourierCoreCourierMQTT

dependencies: [
    .package(url: "https://github.com/gojek/courier-iOS", branch: "main")
]

实现 IConnectionServiceProvider 以提供身份验证连接选项

要连接到 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 协议以提供连接选项供客户端使用。
  • MQTTClientConfig/autoReconnectInterval 当连接丢失时用于重新连接到代理的间隔。在成功建立连接之前,此值会每次乘以2。上限基于 MQTTClientConfig/maxAutoReconnectInterval

在CourierClient中管理连接生命周期

要连接到代理,您只需调用 connect 方法。

courierClient.connect()

要断开连接,您只需调用 disconnect 方法。

courierClient.disconnect()

要获取 ConnectionState,您可以通过访问 CourierSession/connectionState 属性。

courierClient.connectionState

您还可以使用 CourierSession/connectionStatePublisher 属性订阅 ConnectionState 发布者。Courier提供的 Observable 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 将二进制数据序列化到类型。请确保您已经提供了解码数据的关联 MessageAdapter。

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,仅支持 filtermap 操作符。

取消订阅主题

要取消订阅主题,我们可以调用 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/connectionSuccessCourierEvent/connectionAttempt 等,这些事件在 CourierEvent 枚举中声明,您可以实现 ICourierEventHandler 协议并实现 ICourierEventHandler/onEvent(_:) 方法。该方法将为任何Courier系统事件被调用。

最后,请确保对实例具有强引用,并通过传递实例调用 CourierEventManager/addEventHandler(_:)

courierClient.addEventHandler(analytics)

监控Courier MQTT数据包日志

CourierMQTTChuck 用于检查底层MQTT连接的所有出入数据包。

它拦截并持久化所有数据包,提供了一个用户界面来访问所有发送或接收的MQTT数据包。它还提供了搜索、共享和清除数据等其他功能。

它在底层使用 SwiftUI,最小 iOS 部署版本为 15,但您仍然可以在 iOS 11 上构建此程序以访问记录器而不显示视图。

Simulator Screen Shot - iPhone 14 Pro - 2023-04-10 at 17 26 54

Simulator Screen Shot - iPhone 14 Pro - 2023-04-10 at 17 27 31

用法

将依赖添加到 CourierMQTTChuck 并在 SwiftUI 视图中声明记录器如下:

import CourierMQTTChuck

let logger = MQTTChuckLogger()

然后将 MQTTChuckView (通过 SwiftUI 视图)传递 logger

.sheet(isPresented: $showChuckView, content: {
            NavigationView {
                MQTTChuckView(logger: logger)
            }
        })

贡献指南

阅读我们的贡献指南,了解我们的开发过程,如何提出错误修复和改进建议,以及如何构建和测试您的Courier iOS库更改。

许可协议

除了MQTTClientGJ以外的所有Courier模块均采用MIT许可证颁发。MQTTClientGJ采用Eclipse许可证