アイリッジ開発者ブログ

アイリッジに所属するエンジニアが技術情報を発信していきます。

Combine Cocoa 導入してみた

こんにちは。開発部第1グループの涌井です。
今回は Combine Cocoa というライブラリを導入してみたというお話になります。
※ Combine Cocoa にスポットをあてたいので、以降に出てくる RxSwift や Combine の基礎知識(Subscribe や Publisher など)に関しては、割愛となっています。

開発環境

  • MacOS : Venture
  • 対応 iOS : 14系-16系
  • Combine Cocoa : 0.4.1

導入背景

  • あるアプリでは、イベント検知は RxSwift で、画面は UIKit で実装
  • このアプリにおいて下記の改修を行う
    • 新規機能はイベント検知を Combine で、画面は SwiftUI で実装
    • 既存機能は、画面は UIKit のままで、イベント検知は Combine に置き換え
    • 既存機能の改修は、機能は動作的に問題ないので、なるべくロジック変更なしで、工数を抑えたい

ポイント

  • イベント検知の実装方法を RxSwift → Combine に変更
  • 工数を抑えたい(既存機能の改修影響も抑えたい)

そんなことできるの?という感じですが、できるんですよ。Combine Cocoa ならね。
ともったいつけてきたわけですが、そもそも Combine Cocoaとはなんぞや。

Combine Cocoa とは

超ざっくりいうと、UIKitのイベント処理に対して Combine の Publisher を実装してくれているライブラリです。
https://github.com/CombineCommunity/CombineCocoa
Publisher を提供している箇所は、下記画像の赤枠内のような構成になっています。

流石にざっくりすぎるので

  • RxSwift での UIButton のタップ処理のイベント購読コード例
  • Combine のイベント購読コード例

を踏まえて、Combine Cocoa での実装をみていきます。

RxSwift での UIButton のタップ処理のイベント購読コード例

button.rx.tap
    .subscribe(onNext: {[unowned self] _ in
        // 何かの処理
    })
    .disposed(by: disposeBag)

Combine のイベント購読コード例

publisher.sink {
    // 何かの処理
}
.store(in: &cancellables)

上の2つの例を眺めていると、Combine の購読処理は sink で行われるので、button の tap 処理と Combine の Publisher が紐づいてくれると、sink で処理できそうに見えますね。
ここで Combine Cocoa の UIButton+Combine.swift を見ると

Combine Cocoa の tapPublisher

#if !(os(iOS) && (arch(i386) || arch(arm)))
import Combine
import UIKit

@available(iOS 13.0, *)
public extension UIButton {
    /// A publisher emitting tap events from this button.
    var tapPublisher: AnyPublisher<Void, Never> {
        controlEventPublisher(for: .touchUpInside)
    }
}
#endif

UIButton の tap の Publisher が提供されています。つまり、button のタップ処理と Combine の Publisher が紐づいているわけです。素敵ですね。
以上を踏まえて、Combine Cocoa で置き換えしたコード例は以下のようになります。

Combine Cocoa での UIButton のタップ処理のイベント購読コード例

button.tapPublisher.sink {
    // 何かの処理
}
.store(in: &cancellables)

これだけです。素敵ですね。
置き換え前後の差分比較すると、コードの構造的に大きな変更もなく、置き換えによる影響は抑えられているように思います。
また例では UIButton を挙げましたが、UIGestureRecognizer などもあり、色々な Publisher が提供されているので、置き換えは Combine Cocoa で賄えました。素敵ですね。

まとめ

  • Combine Cocoa を使うと、UIKIt のイベント処理に対して Combine で処理できる
  • UIKit を使いつつ RxSwift から Combine へ置き換える際に、Combine Cocoa を使うと、コード変更の構造的な差分は少なく、工数がそれほどかからない
  • Combine Cocoa 素敵ですね

参考記事

https://dev.classmethod.jp/articles/combinecocoa/
https://emoshu.co.jp/blog/archives/18