使用可选的结构变量组合

Combine using optional struct variable

我正在尝试使用 Combine 将 SCEvent 的数组加载到 EventModel 的数组中。变量 imagePath 是可选的,我想在其对应的 EventModel.imageData 变量中将其转换为空 Data()

struct SCEvent {
    let name: String
    let imagePath: String?
}
struct EventModel {
    let name: String
    let imageData: Data
}

下面的代码似乎可行,但我不禁想知道这是否是最佳的实现方式:

func loadEvents(_ events: [SCEvent]) -> AnyPublisher<[EventModel], Error> {
    events.publisher
        .flatMap(loadEvent(_:))
        .collect()
        .eraseToAnyPublisher()
}

func loadEvent(_ event: SCEvent) -> AnyPublisher<EventModel, Error> {
    if let imagePath = event.imagePath {
        return DataDownloader.downloadData(fromPath: imagePath)
                   .map { EventModel(name: event.name, imageData: [=12=]) }
                   .eraseToAnyPublisher()
    }

    return Just(EventModel(name: event.name, imageData: Data())
               .eraseToAnyPublisher()
}

理想情况下,我想在 loadEvent 函数中使用单个发布者。也许是这样的(不起作用,但作为我期望的例子):

func loadEvent(_ event: SCEvent) -> AnyPublisher<[EventModel], Error> {
    event.imagePath
        .flatMap(DataDownloader.downloadData(_:))
        .replaceNil(with: Data())
        .map {
            EventModel(name: event.name, imageData: [=13=])
        }
        .eraseToAnyPublisher()
}

这不起作用,因为 .replaceNil 应该在 event.imagePath 之后使用来替换 nil 字符串。另一种可能的方法是:

func loadEvent(_ event: SCEvent) -> AnyPublisher<[EventModel], Error> {
    event.imagePath
        .replaceNil(with: "")
        .flatMap(
            DataDownloader.downloadData(_:)
                .replaceError(with: Data())
        )
        .map {
            EventModel(name: event.name, imageData: [=14=])
        }
        .eraseToAnyPublisher()
}

不过好像是被逼的。 Combine甚至有可能吗?我的初始方法是唯一有效的解决方案吗?

您可以使用 Optionalpublisher,如果可选值不为零,它会为您提供一个发布一个元素的发布者,否则为一个空发布者。

然后您可以 replaceEmpty(with: Data()),然后 mapEventModel

func loadEvent(_ event: SCEvent) -> AnyPublisher<EventModel, Error> {
    event.imagePath.publisher
        .flatMap(DataDownloader.downloadData(fromPath:))
        .replaceEmpty(with: Data())
        .map {
            EventModel(name: event.name, imageData: [=10=])
        }
        .eraseToAnyPublisher()
}

不过,我认为用 Data() 替换不是个好主意。更好的设计是替换为 nil,在这种情况下,您必须首先映射到一个可选的:

struct EventModel {
    let name: String
    let imageData: Data?
}

...

        .flatMap(DataDownloader.downloadData(fromPath:))
        .map { [=11=] as Data? } // here
        .replaceEmpty(with: nil)