围绕第三方 SDK 编写组合包装器



我必须在我的应用程序中使用第三方 SDK,所以我认为这也是在现实生活中使用 Combine 并将此 SDK 包装在一层薄薄的 Combine 帮助程序中的绝佳机会。我能够相当轻松地涵盖所有内容,但现在我面临着我的一些send方法从未交付的问题。我将分享我的一些代码,以更好地描绘我所拥有的内容。

final class Manager: NSObject {
private let sdkManager: BleManager
// Private Publishers
private let newDeviceFoundPublisher = PassthroughSubject<Device, Error>()
private let connectedToDevicePublisher = PassthroughSubject<Device, Error>()
private let deviceDataPublisher = PassthroughSubject<DeviceData, Never>()
init(manager: BleManager = BleManager.shareInstance()) {
self.vvBleManager = manager
super.init()
self.sdkManager.delegate = self
}
func startScanning(deviceType: DeviceType) -> AnyPublisher<Device, Error> {
sdkManager.startScan(deviceType)
return newDeviceFoundPublisher
.map { Device.init }
.timeout(.seconds(10), scheduler: DispatchQueue.main, customError: { .scanningTimeout })
.eraseToAnyPublisher()
}
func stopScanning() {
sdkManager.stopScan()
}
func connect(to device: Device) -> AnyPublisher<Device, Error> {
sdkManager.connect(device)
return connectedToDevicePublisher.eraseToAnyPublisher()
}
func disconnect(device: DeviceType) {
sdkManager.disconnect(device)
}
func deviceData() -> AnyPublisher<DeviceData, Never> {
return deviceDataPublisher.eraseToAnyPublisher()
}
}
extension VivalnkManager: BLEDelegate {
func onDeviceFound(_ device: Device!) {
newDeviceFoundPublisher.send(device)
}
func onConnected(_ device: Device!) {
connectedToDevicePublisher.send(device)
connectedToDevicePublisher.send(completion: .finished)
}
func onReceiveData(_ data: Any!) {
if let newData = try? DeviceData(from: data!) {
deviceDataPublisher.send(newData)
}
}
}

这里没有什么疯狂的事情发生,当我检查 SDK 中的日志时,所有方法都正确执行,委托在新数据等上被触发。这就是我知道我的代码一定有问题的方式。现在,我正在尝试实现但无法使其工作的内容呈现了以下代码示例:

manager
.startScanning(deviceType: .swiftometr)
.filter { $0.deviceId == id }
.handleEvents(receiveOutput: { _ in environment.manager.stopScanning() })
.flatMap { environment.manager.connect(to: $0) }
.sink(receiveCompletion: { _ in print("Completion") },
receiveValue: { _ in print("Value") })
.store(in: &cancellables)

结果,我只能在控制台中打印一段时间后才能看到Completion,但我怀疑这是来自我在方法scanningtimeout。我尝试了许多不同的方法,但没有任何效果。然后,我想,也许flatMap不能像这样工作,所以我尝试将几个URLSession.shared.dataTaskPublisher嵌套在一起,它们工作得很好!


更多的挖掘,我处于我理解为什么这一切发生的状态。看起来sdkManager.connect(device)发生得如此之快,以至于我的应用程序无法订阅,所以这就是我永远不会收到任何价值的原因。对我有帮助的是简单地将其包装在这样的延迟块中:

func connect(to device: Device) -> AnyPublisher<Device, Error> {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
sdkManager.connect(device)
}
return connectedToDevicePublisher.eraseToAnyPublisher()
}

现在,connect能够返回AnyPublisher并允许订阅者在send发生之前订阅。尽管它有效,但它感觉像是一种笨拙的做事方式,我对此并不满意。寻找一些更好的方法。

帮助您处理此代码有点困难,因为我们没有很多关于"BleManager"如何工作的信息。

当您声明如时:

newDeviceFoundPublisher = PassthroughSubject<Device, Error>()

您声明这是一个永无止境的设备流。 因此,大概当您开始扫描设备时,流永远不会结束。

但是您的"扫描"操作似乎范围有限。 它似乎以"开始扫描"开始,以"停止扫描"结束。 我不确定这里的答案是什么,但是无穷无尽的设备流的声明似乎与"扫描操作"是限时活动的想法不一致。 扫描设备的时间,以及扫描期间返回的设备数量。 这似乎是一个概念,应该体现在底层模块上放置更好的接口中。

您似乎还有另一个操作,即"连接到已发现的设备"。就类似Combine的功能而言,"连接操作"似乎应该是一个Future- 您要么成功连接到设备一次,要么失败。

您拥有的最后一个操作是从连接的设备读取数据流。 从这个意义上说,"连接到设备"未来类似操作的结果应该是来自连接设备的数据流。 这是 Combine 可能有用的第三个地方,但它似乎都与类中的其他两个操作混为一谈。

在重新设计您的低级管理器时,您保留了其整体性质,但实际上似乎您有几个单独的操作,每个操作最好通过 Combine 框架以不同的方式反映出来。也许您可以通过考虑如何单独处理它们来改进您的界面。

最新更新