【问题标题】:Writing Combine wrapper around 3rd party SDK围绕 3rd 方 SDK 编写组合包装器
【发布时间】:2020-06-09 08:43:02
【问题描述】:

我必须在我的应用程序中使用第 3 方 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,但我怀疑这是来自timeout,我在scanning 方法中。我尝试了许多不同的方法,但没有任何效果。然后,我想,也许flatMap 不是这样工作的,所以我尝试将几个URLSession.shared.dataTaskPublishers 嵌套在一起,它们工作得很好!


更多的挖掘,我处于我理解为什么这一切发生的状态。看起来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 发生之前订阅。尽管它有效,但感觉就像是一种 hacky 的做事方式,我对此并不满意。寻找更好的方法。

【问题讨论】:

    标签: swift combine


    【解决方案1】:

    要帮助您编写这段代码有点困难,因为我们没有太多关于“BleManager”如何工作的信息。

    当你像这样声明一个流时:

    newDeviceFoundPublisher = PassthroughSubject<Device, Error>()
    

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

    但是您的“扫描”操作似乎范围有限。它似乎以“startScanning”开头并以“stopScanning”结尾。我不确定这里的答案是什么,但无休止的设备流的声明似乎与“扫描操作”是限时活动的想法不一致。您扫描设备多长时间,以及在该扫描期间返回了多少设备。这似乎是一个概念,应该在底层模块上放置一个更好的接口。

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

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

    在重新设计您的低级管理器时,您保持了它的整体性,但看起来您确实有几个单独的操作,每个操作都可以通过 Combine 框架以不同的方式得到最好的体现。也许您可以通过考虑如何单独处理它们来改进您的界面。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-06-11
      • 1970-01-01
      • 2018-04-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-24
      • 1970-01-01
      相关资源
      最近更新 更多