5
5

More than 1 year has passed since last update.

【Swift × Combine】Combine入門 ①【コードあり】

Last updated at Posted at 2022-02-06

目的

Combineとは何かを学ぶこと
「Combineを使ってミニアプリを作れる」くらいのレベルになること

目次

1 : Combineとは

2 : 文字列の値を渡す

3 : 2つ以上の受け取り先を用意する

4 : store() : 複数のSubscriptionをまとめて保持する

5 : assign : 値の代入

1 : Combineとは

Appleが提供しているSwiftのフレームワーク。
オブジェクトからオブジェクトに何かしらのイベントを伝える仕組み を提供する。
(イベントの例 : API通信,アプリ内で発生した状態変化 等)

2 : 文字列の値を渡す

プログラム

ViewController.swift
import UIKit
import Combine

// イベントの送受信を仲介
// イベントは「文字列を渡して表示させる」
let subject = PassthroughSubject<String, Never>()   // <受信する型, 返信する型>

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // イベントの受け取り
        let cancellable = subject
            .sink(receiveCompletion: { completion in
                print("Receive Completion : ", completion)
            }, receiveValue:{ value in
                print("Receive Value : ", value)
            })

        // イベントの送信
        subject.send("あ")
        subject.send("い")
        subject.send("う")
        subject.send("え")
        subject.send("お")
        subject.send(completion: .finished)   // イベント終了を送信

    }
}

実行結果

Receive Value :  あ
Receive Value :  い
Receive Value :  う
Receive Value :  え
Receive Value :  お
Receive Completion :  finished

解説

文字列の値を渡すという簡単なイベントの実装を行った。

手段として Combineの PassthroughSubjectクラスを使用している。



■ イベントの仲介として subject を作成

ViewController.swift
let subject = PassthroughSubject<String, Never>()   // <受信する型, 返信する型>



■ イベントの送信として subject.send("~~~")

ViewController.swift
// イベントの送信
subject.send("あ")
subject.send("い")
subject.send("う")
subject.send("え")
subject.send("お")
subject.send(completion: .finished)   // イベント終了を送信



■ イベントの受信として subject.sink()
receiveCompleition で実行状態(成功/失敗, .finished/.failure) を受け取る
receiveValue で実行の戻り値を受け取る
subscription は AnyCancellable型 である

ViewController.swift
// イベントの受け取り
let subscription = subject
    .sink(receiveCompletion: { completion in
        print("Receive Completion : ", completion)
    }, receiveValue:{ value in
        print("Receive Value : ", value)
    })

注意 let subscription を忘れないようにしてください。 忘れた場合、イベントの受け取りを保持できません。 理由 sinkで指定した受信処理を有効にするためには、sinkの戻り値を破棄せずに保持し続ける必要があるからです。 subscription という変数に保持し続ける必要があります。

3 : 2つ以上の受け取り先を用意する

プログラム

ViewController.swift
import UIKit
import Combine

// イベントの送受信を仲介
// イベントは「文字列を渡して表示させる」というもの
public let subject = PassthroughSubject<String, Never>()   // <受信する型, 返信する型>

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // イベントの送信
        let receiver = Receiver()
        subject.send("あ")
        subject.send("い")
        receiver.subscription1.cancel()   // subscription1 を中止する
        subject.send("う")
        subject.send("え")
        subject.send("お")
        subject.send(completion: .finished)   // イベント終了を送信

    }
}

// 受け取り先を2つ作成
// final : 継承を禁止する
final class Receiver {
    // 受け取りを2つ用意
    let subscription1: AnyCancellable
    let subscription2: AnyCancellable

    init() {

        // 1つ目の受け取り
        subscription1 = subject
            .sink{ value in
                print("[1] subscription : ", value)
            }

        // 2つ目の受け取り
        subscription2 = subject
            .sink{ value in
                print("[2] subscription : ", value)
            }

    }
}

実行結果

[1] subscription :  あ
[2] subscription :  あ
[1] subscription :  い
(本来は改行なし)           ← subscription1をキャンセルする
[2] subscription :  い
[2] subscription :  う
[2] subscription :  え
[2] subscription :  お

解説

Receiver() に複数のsubscriptionを作成している。

subject.send("~~~")を行うと、複数のsubscription全てに向けて送信が実行される。

receiver.subscription1.cancel() のように、subscriptionを指定して送信をキャンセルすることが可能。
→ 本当にキャンセルできているかは、実行結果から確認できる

4 : store() : 複数のSubscriptionをまとめて保持する

プログラム

ViewController.swift
import UIKit
import Combine

// イベントの送受信を仲介
// イベントは「文字列を渡して表示させる」というもの
public let subject = PassthroughSubject<String, Never>()   // <受信する型, 返信する型>

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // イベントの送信
        let receiver = Receiver()
        subject.send("あ")
        subject.send("い")
        subject.send("う")
        subject.send("え")
        subject.send("お")
        subject.send(completion: .finished)   // イベント終了を送信

    }
}

// 受け取り先を2つ作成
// final : 継承を禁止する
final class Receiver {
    // 受け取りを2つ用意
    //let subscription1: AnyCancellable
    //let subscription2: AnyCancellable
    var subscriptions = Set<AnyCancellable>()   // → 2つのsubscriptionを 1つにまとめるための subscription

    init() {
        // 1つ目の受け取り
        //subscription1 = subject
        // 1つのsubscriptionにまとめる場合は subjectを変数で保持する必要なし
        subject
            .sink{ value in
                print("[1] subscription : ", value)
            }
            .store(in: &subscriptions)   // 1つのsubscriptionにまとめる

        // 2つ目の受け取り
        //subscription2 = subject
        subject
            .sink{ value in
                print("[2] subscription : ", value)
            }
            .store(in: &subscriptions)   // 1つのsubscriptionにまとめる
    }
}

実行結果

[1] subscription :  あ
[2] subscription :  あ
[1] subscription :  い
[2] subscription :  い
[1] subscription :  う
[2] subscription :  う
[1] subscription :  え
[2] subscription :  え
[1] subscription :  お
[2] subscription :  お

解説

var subscriptions = Set<AnyCancellable>() によって 複数のsubscriptionを1つにまとめることができる。

Set へ複数のsubscriptionを入れるためには、.store(in: &(Set先のsubscription))が必要

sink の後に store を呼べば良い

5 : assign : 値の代入

プログラム

ViewController.swift
import UIKit
import Combine

// イベントの送受信を仲介
// イベントは「文字列を渡して表示させる」というもの
public let subject = PassthroughSubject<String, Never>()   // <受信する型, 返信する型>

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // イベントの送信
        let receiver = Receiver()
        subject.send("あ")
        subject.send("い")
        subject.send("う")
        subject.send("え")
        subject.send("お")
    }

}

final class Receiver {
    var subscriptions = Set<AnyCancellable>()

    init() {
        // .assign を使い,受信結果をクラスの変数に代入する
        subject
            .assign(to: \.value, on: SomeObject())
            .store(in: &subscriptions)
    }
}

// 値が代入されるか確認するためにクラス
// valueに値が代入される
final class SomeObject {
    var value: String = "" {
        didSet {
            print("didSet value : ", value)
        }
    }
}

実行結果

didSet value :  あ
didSet value :  い
didSet value :  う
didSet value :  え
didSet value :  お

解説

.assign(to: \.(代入先の変数名), on: (代入先のクラス名)) を使い、クラスの変数へ代入をおこなっている
→ assign の使い方は、sinkと似ている

assign の利用条件 エラー型がNever のイベント(エラーが発生しないイベント)であること

注意 : assign(to: \.(代入先の変数名), on: (代入先のクラス名)) について to で指定するプロパティは書き込み可能なもの on で指定するオブジェクトは参照型(classやstructなど) である必要がある。

参考資料

5
5
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
5
5