はじめに
Apple公式ドキュメント(https://developer.apple.com/documentation/combine/receiving-and-handling-events-with-combine)の"Change the Output Type with Operators"にあたる内容です。Operatorsを使ってPublishersが出力する値を変換する処理を試しました。
開発環境
- macOS Big Sur 11.4
- Xcode 12.5.1
- Swift 5.4.2
本題
以下のサンプルでは、Operatorである"compactMap(_:)"で数字の文字列をInt型にキャストしている。出力される値はInt型の1。
compactMapなので、仮にInt型にキャストできない文字列が送信されてきた場合(nilになる場合)は出力されない。
ContentView.swift
import SwiftUI import Combine struct ContentView: View { private let sampleNotification = Notification.Name("sampleNotification") private var cancellable: AnyCancellable? init() { cancellable = NotificationCenter.default.publisher(for: sampleNotification, object: nil) .compactMap { Int($0.userInfo!["numberString"] as! String) } .sink(receiveCompletion: { completion in switch completion { case .finished: print("finished") case .failure(let error): print("error \(error.localizedDescription)") } }, receiveValue: { number in print(number) }) } var body: some View { Button(action: { NotificationCenter.default.post( name: sampleNotification, object: nil, userInfo: ["numberString": "1"] ) }, label: { Text("Send notification") }) } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }
compactMap以外にも多くのOperatorがある。
※参考:https://developer.apple.com/documentation/combine/just-publisher-operators
例えば、compactMapからmapにサンプルコードを変更して、nilの出力を許容してみる。
変更前
.compactMap { Int($0.userInfo!["numberString"] as! String) }
変更後
.map { Int($0.userInfo!["numberString"] as! String) }
この場合にOperatorの"replaceNil(with:)"を使うと、mapの結果がnilになった場合に代わりの値を出力値として置き換えることができる。
※参考:https://developer.apple.com/documentation/combine/just/replacenil(with:)
サンプルは以下。この場合はnilが出力される代わりにInt型の2が出力される。
ContentView.swift
import SwiftUI import Combine struct ContentView: View { private let sampleNotification = Notification.Name("sampleNotification") private var cancellable: AnyCancellable? init() { cancellable = NotificationCenter.default.publisher(for: sampleNotification, object: nil) .map { Int($0.userInfo!["numberString"] as! String) } .replaceNil(with: 2) .sink(receiveCompletion: { completion in switch completion { case .finished: print("finished") case .failure(let error): print("error \(error.localizedDescription)") } }, receiveValue: { number in print(number) }) } var body: some View { Button(action: { NotificationCenter.default.post( name: sampleNotification, object: nil, userInfo: ["numberString": "Non-numeric string"] ) }, label: { Text("Send notification") }) } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }
おわりに
簡単なサンプルですが、Operatorsが上流から流れてきた値を変換してSubscribersに値を流しているイメージを掴むことができました。Operatorsは種類が多く、全部把握するのは骨が折れそうですが実践の中で学んでいければと思います。
参考
-
https://developer.apple.com/documentation/combine/receiving-and-handling-events-with-combine
-
https://developer.apple.com/documentation/combine/just-publisher-operators
-
https://developer.apple.com/documentation/combine/just/compactmap(_:)-61xqc
-
https://developer.apple.com/documentation/combine/just/map(_:)-7fb7v
-
https://developer.apple.com/documentation/combine/just/replacenil(with:)