SwiftのCombineのPublisherにはOutputがOptionalの場合にreplaceNil(with:)メソッドが存在します。これはOutputがOptional.noneの場合にreplaceNilの引数に置き換え、OutputをOptional.Wrappedに変換するメソッドです。
この場合replaceNilの引数、つまりOptional.Wrappedのデフォルト値が必要になりますし、私はnilを無視するメソッドが欲しいのです。なのでignoreNilを作成してみました。
public protocol OptionalType {
associatedtype Wrapped
var optional: Wrapped? { get }
}
extension Optional: OptionalType {
public var optional: Wrapped? { self }
}
extension Publisher where Output: OptionalType {
public func ignoreNil() -> AnyPublisher<Output.Wrapped, Failure> {
flatMap { output -> AnyPublisher<Output.Wrapped, Failure> in
guard let output = output.optional
else { return Empty<Output.Wrapped, Failure>(completeImmediately: false).eraseToAnyPublisher() }
return Just(output).setFailureType(to: Failure.self).eraseToAnyPublisher()
}.eraseToAnyPublisher()
}
}
Publisher.OutputがOptionalの場合にignoreNilを実装したいのですがOptionalをそのままwhereで判定することができません。ですのでOptionalTypeプロトコルを作成してOptionalに適合させ、Publisher.OutputがOptionalTypeである場合に実装しています。
実装の内容は、flatMapでnilの場合はEmptyを、実体がある場合はJustを返しています。
EmptyはcompleteImmediatelyをfalseにしてすぐにFinishしないようにしています。が、trueにしても動作が変わったようにならないので、この引数の効果はよく分かっていません。誰か教えてください。
以下のように利用すると
let c = ["first", nil, "second", nil, "third"]
.publisher
.ignoreNil()
.sink { string in print(string) }
first
second
third
と表示され、nilが無視されているのが分かります。
まだCombineに精通していないのでこれが正しい実装か分かりませんが、とりあえずignoreNilメソッドができました。