7
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

CombineのPublisherにignoreNilを実装する

Last updated at Posted at 2019-12-12

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メソッドができました。

7
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?