目的
Combineとは何かを学ぶこと
「Combineを使ってミニアプリを作れる」くらいのレベルになること
目次
[1 : Combineとは](#1--combineとは)
[2 : 文字列の値を渡す](#2--文字列の値を渡す)
[3 : 2つ以上の受け取り先を用意する](#3--2つ以上の受け取り先を用意する)
[4 : store() : 複数のSubscriptionをまとめて保持する](#4--store--複数のsubscriptionをまとめて保持する)
[5 : assign : 値の代入](#5--assign--値の代入)
1 : Combineとは
[3 : 2つ以上の受け取り先を用意する](#3--2つ以上の受け取り先を用意する)
[4 : store() : 複数のSubscriptionをまとめて保持する](#4--store--複数のsubscriptionをまとめて保持する)
[5 : assign : 値の代入](#5--assign--値の代入)
1 : Combineとは
[5 : assign : 値の代入](#5--assign--値の代入)
1 : Combineとは
Appleが提供しているSwiftのフレームワーク。
オブジェクトからオブジェクトに何かしらのイベントを伝える仕組み を提供する。
(イベントの例 : API通信,アプリ内で発生した状態変化 等)
2 : 文字列の値を渡す
プログラム
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` を作成
let subject = PassthroughSubject<String, Never>() // <受信する型, 返信する型>
■ イベントの送信として `subject.send("~~~")`
// イベントの送信
subject.send("あ")
subject.send("い")
subject.send("う")
subject.send("え")
subject.send("お")
subject.send(completion: .finished) // イベント終了を送信
■ イベントの受信として `subject.sink()` `receiveCompleition` で実行状態(成功/失敗, .finished/.failure) を受け取る `receiveValue` で実行の戻り値を受け取る `subscription` は AnyCancellable型 である
// イベントの受け取り
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つ以上の受け取り先を用意する
プログラム
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をまとめて保持する
プログラム
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 : 値の代入
プログラム
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など)
である必要がある。
参考資料