1 2 3 4 public func example (of description : String , action : () -> Void ) { print ("\n === 範例: \(description) ===" ) action() }
Lesson.01 - Notification vs Combine
先以一個iOS基本的Notification為基準
1 2 3 4 5 6 7 8 9 10 example(of: "Notification" ) { let myNotification = Notification .Name ("MyNotification" ) let center = NotificationCenter .default let observer = center.addObserver(forName: myNotification, object: nil , queue: nil ) { notification in print ("\(notification.object! ) " ) } let object = "「張君雅小妹妹,恁兜的泡麵已經煮好了,恁阿嬤限妳一分鐘內趕緊回去呷;哪嘸,到時麵若爛去,伊是概不負責!」" center.post(name: myNotification, object: object) center.removeObserver(observer) }
1 2 === 範例: Notification === 「張君雅小妹妹,恁兜的泡麵已經煮好了,恁阿嬤限妳一分鐘內趕緊回去呷;哪嘸,到時麵若爛去,伊是概不負責!」
可以類比成NotificationCenter.default.post()的功能
有發布訊息的功能 (單向)
1 2 3 4 5 6 7 8 example(of: "Publisher" ) { let myNotification = Notification .Name ("MyNotification" ) let center = NotificationCenter .default let publisher = center.publisher(for: myNotification, object: nil ) center.post(name: myNotification, object: nil ) }
訂閱者 - Subscriber
可以類比成NotificationCenter.default.addObserver()的功能
有收接訊息的功能 (單向)
1 2 3 4 5 6 7 8 9 10 example(of: "Subscriber" ) { let myNotification = Notification .Name ("MyNotification" ) let center = NotificationCenter .default let publisher = center.publisher(for: myNotification, object: nil ) let subscription = publisher.sink { _ in print ("收到從發布者傳來的通知了" ) } center.post(name: myNotification, object: nil ) subscription.cancel() }
我心中的Combine
初學Publisher Just
使用Combine時,最基本的Publisher
一個發布者,可由多個訂閱者接收
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 example(of: "Just" ) { let just = Just ("Hello world!" ) let subscription_1 = just.sink( receiveCompletion: { print ("Received completion: " , $0 ) }, receiveValue: { print ("Received value: " , $0 ) }) let subscription_2 = just.sink( receiveCompletion: { print ("Received completion (another): " , $0 ) }, receiveValue: { print ("Received value (another): " , $0 ) }) subscription_1.cancel() subscription_2.cancel() }
1 2 3 4 5 === 範例: Just === Received value: Hello world! Received completion: finished Received value (another): Hello world! Received completion (another): finished
Combine運算子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 example(of: "assign(to:on:)" ) { final class SomeObject { var value: String = "" { didSet { print (value) } } } let array = ["Hello" , "world!" ] let object = SomeObject () let publisher = array.publisher _ = publisher.assign(to: \.value, on: object) }
1 2 3 === 範例: assign(to:on:) === Hello world!
這是個相當經典的例子
利用@Published ,將變數value轉成Publisher,這時候的value就要用$value來處理
而要存到變數value,就要使用&object來處理
使用sink() 取值,而不是從數值去取值了
1 2 3 4 5 6 7 8 9 10 11 example(of: "assign(to:)" ) { final class SomeObject { @Published var value = 0 } let object = SomeObject () object.$value .sink { print ("收到的值為: \($0 ) " ) } (0 ..< 10 ).publisher.assign(to: & object.$value ) }
1 2 3 4 5 6 7 8 9 10 11 12 === 範例: assign(to:) === 收到的值為: 0 收到的值為: 0 收到的值為: 1 收到的值為: 2 收到的值為: 3 收到的值為: 4 收到的值為: 5 收到的值為: 6 收到的值為: 7 收到的值為: 8 收到的值為: 9
自訂Subscriber
只要使用協定Subscriber就可以自己產生一個自訂Subscriber
如果使用subscription.request(.max(3)) + return .none,就只會印三個,而且不會結束
但如果把return .none改成return .unlimited,就只會印全部,而且會結束
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 example(of: "Custom Subscriber" ) { final class MagazineSubscriber : Subscriber { typealias Input = String typealias Failure = Never private let maxCount = 3 private var count = 0 private var subscription: Subscription ? func receive (subscription : Subscription ) { print (subscription) self .subscription = subscription subscription.request(.unlimited) } func receive (_ input : Input ) -> Subscribers .Demand { print ("Received value" , input, count) count += 1 if (count >= maxCount) { subscription? .cancel() } return .unlimited } func receive (completion : Subscribers .Completion <Never >) { print ("Received completion" , completion) } } let magazines = ["ABC互動英語" , "LIVE互動英語" , "CNN互動英語" , "互動日本語" , "跟我一起學日語" , "KOREA韓語學習誌" ] let publisher = magazines.publisher let subscriber = MagazineSubscriber () publisher.subscribe(subscriber) }
1 2 3 4 5 === 範例: Custom Subscriber === ["ABC互動英語" , "LIVE互動英語" , "CNN互動英語" , "互動日本語" , "跟我一起學日語" , "KOREA韓語學習誌" ] Received value ABC互動英語 0 Received value LIVE互動英語 1 Received value CNN互動英語 2
Publisher
聽名字就知道,接收未來資料與事件非同步 的Publisher
在這裡我們讓它停止3秒,再顯示值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 var subscriptions = Set <AnyCancellable >()example(of: "Future" ) { func futureIncrement (integer : Int , afterDelay delay : UInt32 ) -> Future <Int , Never > { Future <Int , Never > { promise in print ("Original => \(integer) " ) sleep(delay) promise(.success(integer + 1 )) } } let future = futureIncrement(integer: 1 , afterDelay: 3 ) future .sink(receiveCompletion: { print ("Completion => \($0 ) " ) }, receiveValue: { print ("Value => \($0 ) " ) }) .store(in: & subscriptions) future .sink(receiveCompletion: { print ("Completion => \($0 ) " ) }, receiveValue: { print ("Value => \($0 ) " ) }) .store(in: & subscriptions) }
1 2 3 4 5 6 === 範例: Future === Original => 1 Value => 2 Completion => finished Value => 2 Completion => finished
與之前我們討論的 Publisher 不同的是,Subject 的最大特點就是可以手動傳送資料
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 example(of: "PassthroughSubject" ) { enum MyError : Error { case test } final class StringSubscriber : Subscriber { typealias Input = String typealias Failure = MyError func receive (subscription : Subscription ) { subscription.request(.max(2 )) } func receive (_ input : String ) -> Subscribers .Demand { print ("Received value (input): " , input) return input == "World" ? .max(1 ) : .none } func receive (completion : Subscribers .Completion <MyError >) { print ("Received completion (input): " , completion) } } let subscriber = StringSubscriber () let subject = PassthroughSubject <String , MyError >() subject.subscribe(subscriber) let subscription = subject .sink( receiveCompletion: { completion in print ("Received completion (sink): " , completion) }, receiveValue: { value in print ("Received value (sink): " , value) } ) subject.send("Hello" ) subject.send("World" ) subscription.cancel() subject.send("Still there?" ) subject.send(completion: .finished) subject.send("How about another one?" ) }
1 2 3 4 5 6 7 === 範例: PassthroughSubject === Received value (sink): Hello Received value (input): Hello Received value (sink): World Received value (input): World Received value (input): Still there? Received completion: finished
與 PassthroughSubject 不同,CurrentValueSubject 會保留一個最後的資料,並在被訂閱時將這個資料傳送給下游的 Publisher 或 Subscriber。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 example(of: "CurrentValueSubject" ) { let subject = CurrentValueSubject <Int , Never >(0 ) subject .print() .sink(receiveValue: { print ("First subscription: \($0 ) " ) }) .store(in: & subscriptions) subject.send(1 ) subject.send(2 ) print (subject.value) subject.value = 3 print (subject.value) subject .print() .sink(receiveValue: { print ("Second subscription:" , $0 ) }) .store(in: & subscriptions) subject.send(completion: .finished) }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 === 範例: CurrentValueSubject === receive subscription: (CurrentValueSubject) request unlimited receive value: (0) First subscription: 0 receive value: (1) First subscription: 1 receive value: (2) First subscription: 2 2 receive value: (3) First subscription: 3 3 receive subscription: (CurrentValueSubject) request unlimited receive value: (3) Second subscription: 3 receive finished receive finished
這裡是自動根據輸入的值,去改變Subscribers.Demand
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 example(of: "Dynamically adjusting Demand" ) { final class IntSubscriber : Subscriber { typealias Input = Int typealias Failure = Never func receive (subscription : Subscription ) { subscription.request(.max(3 )) } func receive (_ input : Int ) -> Subscribers .Demand { print ("Received value" , input) switch input { case 1 : return .max(2 ) case 3 : return .max(1 ) default : return .none } } func receive (completion : Subscribers .Completion <Never >) { print ("Received completion: " , completion) } } let subscriber = IntSubscriber () let subject = PassthroughSubject <Int , Never >() subject.subscribe(subscriber) (1 ... 10 ).forEach { subject.send($0 ) } }
1 2 3 4 5 6 7 === 範例: Dynamically adjusting Demand === Received value 1 Received value 2 Received value 3 Received value 4 Received value 5 Received value 6
轉成AnyPublisher
讓Publisher的類型簡單化
1 2 3 4 5 6 7 8 9 10 11 12 example(of: "Type erasure" ) { let subject = PassthroughSubject <Int , Never >() let publisher = subject.eraseToAnyPublisher() publisher .sink(receiveValue: { print ($0 ) }) .store(in: & subscriptions) subject.send(0 ) }
1 2 === 範例: Type erasure === 0
async / await
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 example(of: "async / await" ) { let subject = CurrentValueSubject <Int , Never >(0 ) Task { for await element in subject.values { print ("Element: \(element) " ) } print ("Completed." ) } sleep(2 ) subject.send(1 ) sleep(2 ) subject.send(2 ) sleep(2 ) subject.send(3 ) subject.send(completion: .finished) }
1 2 3 4 5 6 === 範例: async / await === Element: 0 Element: 1 Element: 2 Element: 3 Completed.
Lesson.02 - Operators也是Publisher
1 2 3 4 5 6 7 8 9 10 11 12 example(of: "collect" ) { let array = ["A" , "B" , "C" , "D" , "E" ] array.publisher .collect(2 ) .sink( receiveCompletion: { print ($0 ) }, receiveValue: { print ($0 ) } ) .store(in: & subscriptions) }
1 2 3 4 5 === 範例: collect === ["A" , "B" ] ["C" , "D" ] ["E" ] finished
1 2 3 4 5 6 7 8 9 10 11 12 13 14 example(of: "map" ) { let array = [123 , 4 , 56 ] let formatter = NumberFormatter () formatter.numberStyle = .spellOut array.publisher .map({ value in formatter.string(for: NSNumber (integerLiteral: value)) ?? "" }) .sink(receiveValue: { print ($0 ) }) .store(in: & subscriptions) }
1 2 3 4 === 範例: map === one hundred twenty-three four fifty-six
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 example(of: "mapping key paths" ) { let publisher = PassthroughSubject <Coordinate , Never >() publisher .map(\.x, \.y) .sink(receiveValue: { x, y in guard let quadrant = quadrantOf(x: x, y: y) else { print ("座標在 (\(x) , \(y) ) 在軸上" ); return } print ("座標在 (\(x) , \(y) ) 第\(quadrant) 象限" ) }) .store(in: & subscriptions) publisher.send(Coordinate (x: 10 , y: - 8 )) publisher.send(Coordinate (x: 0 , y: 5 )) }
1 2 3 === 範例: mapping key paths === 座標在 (10, -8) 第4象限 座標在 (0, 5) 在軸上
1 2 3 4 5 6 7 8 9 10 example(of: "tryMap" ) { Just ("資料夾名稱不存在!!!" ) .tryMap { try FileManager .default.contentsOfDirectory(atPath: $0 ) } .sink( receiveCompletion: { print ("Completion: \($0 ) " ) }, receiveValue: { print ("ReceiveValue: \($0 ) " ) } ) .store(in: & subscriptions) }
1 2 3 4 === 範例: tryMap === Completion: failure(Error Domain=NSCocoaErrorDomain Code=260 "The folder “資料夾名稱不存在!!!” doesn’t exist." UserInfo={NSUserStringVariant=( Folder ), NSFilePath=資料夾名稱不存在!!!, NSUnderlyingError=0x600003aff900 {Error Domain=NSPOSIXErrorDomain Code=2 "No such file or directory" }})
字如其義,就是扁平化的意思 - flat,把Publishers => 單一個Publisher (Publisher轉換)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 example(of: "flatMap" ) { func decode (_ codes : [Int ]) -> AnyPublisher <String , Never > { Just (codes.compactMap { code in guard (32 ... 255 ).contains(code) else { return nil } return String (UnicodeScalar (code) ?? " " ) }.joined()) .eraseToAnyPublisher() } let array = [72 , 101 , 108 , 108 , 111 , 44 , 32 , 87 , 111 , 114 , 108 , 100 , 33 , 33 , 33 ] array.publisher .collect() .flatMap(decode) .sink(receiveValue: { print ($0 ) }) .store(in: & subscriptions) }
1 2 === 範例: flatMap === Hello, World!!!
字如其義,就是處理nil值,有點像 (value ?? “-“)
1 2 3 4 5 6 7 8 example(of: "replaceNil" ) { ["A" , nil , "C" ].publisher .eraseToAnyPublisher() .replaceNil(with: "-" ) .sink(receiveValue: { print ($0 ) }) .store(in: & subscriptions) }
1 2 3 4 === 範例: replaceNil === A - C
字如其義,就是處理空值,用在演示或測試用
example(of: "replaceEmpty(with:)") {
let empty = Empty<String, Never>()
empty
.replaceEmpty(with: "Test") /// 可以試試不使用它的結果
.sink(
receiveCompletion: { print($0) },
receiveValue: { print($0) }
)
.store(in: &subscriptions)
}
1 2 3 === 範例: replaceEmpty(with:) === Test finished
累加之用,很像reduce()
example(of: "scan") {
let initValue = 50
let maxCount = 10
var dailyGainLoss: Int { .random(in: -maxCount...maxCount) } /// 將產生-10 ~ 10的隨時數
let june2023 = (0..<maxCount) /// 產生10組
.map { _ in dailyGainLoss } /// [Int]
.publisher
june2023
.scan(initValue) { latest, current in
max(0, latest + current) /// 累加
}
.sink(receiveValue: { _ in })
.store(in: &subscriptions)
}
Lesson.03 - 過濾用的Operator
example(of: “filter”) {
let multiple = 3
let numbers = (1...10).publisher
numbers
.filter { $0.isMultiple(of: multiple) }
.sink(receiveValue: { number in print("\(number) 是 \(multiple) 的倍數 !!!")})
.store(in: &subscriptions)
}
1 2 3 4 5 6 ```bash === 範例: filter === 3 是 3 的倍數 !!! 6 是 3 的倍數 !!! 9 是 3 的倍數 !!!
1 2 3 4 5 6 7 8 9 10 example(of: "removeDuplicates" ) { let words = "hey hey there! want to listen to mister mister ?" .components(separatedBy: " " ) .publisher words .removeDuplicates() .sink(receiveValue: { print ($0 ) }) .store(in: & subscriptions) }
1 2 3 4 5 6 7 8 hey there! want to listen to mister ?
1 2 3 4 5 6 7 8 9 example(of: "compactMap" ) { let strings = ["a" , "1.24" , "3" , "def" , "45" , "0.23" ].publisher strings .compactMap { Float ($0 ) } .sink(receiveValue: { print ($0 ) }) .store(in: & subscriptions) }
1 2 3 4 5 === 範例: compactMap === 1.24 3.0 45.0 0.23
1 2 3 4 5 6 7 8 9 10 11 12 example(of: "ignoreOutput" ) { let numbers = (1 ... 10_000 ).publisher numbers .ignoreOutput() .sink( receiveCompletion: { print ("完成: \($0 ) " ) }, receiveValue: { print ($0 ) } ) .store(in: & subscriptions) }
1 2 === 範例: ignoreOutput === 完成: finished
1 2 3 4 5 6 7 8 9 10 11 12 13 14 example(of: "first(where:)" ) { let multiple = 2 let numbers = (1 ... 9 ).publisher numbers .print("numbers" ) .first(where: { $0 % multiple == 0 }) .sink( receiveCompletion: { print ("完成: \($0 ) " ) }, receiveValue: { print ("第一個偶數: \($0 ) " ) } ) .store(in: & subscriptions) }
1 2 3 4 5 6 7 8 === 範例: first(where :) === numbers: receive subscription: (1...9) numbers: request unlimited numbers: receive value: (1) numbers: receive value: (2) numbers: receive cancel 第一個偶數: 2 完成: finished
取得最後一個符合過濾的值
取得第一個很簡單,只要有符合就行了;但是最後一個的話,就要finished才可以
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 example(of: "last(where:)" ) { let numbers = PassthroughSubject <Int , Never >() numbers .last(where: { $0 % 2 == 0 }) .sink( receiveCompletion: { print ("完成: \($0 ) " ) }, receiveValue: { print ("最後一個偶數: \($0 ) " ) } ) .store(in: & subscriptions) numbers.send(1 ) numbers.send(2 ) numbers.send(3 ) numbers.send(4 ) numbers.send(5 ) numbers.send(completion: .finished) }
1 2 3 === 範例: last(where :) === 最後一個偶數: 4 完成: finished
1 2 3 4 5 6 7 8 9 example(of: "dropFirst" ) { let numbers = (1 ... 10 ).publisher numbers .dropFirst(8 ) .sink(receiveValue: { print ($0 ) }) .store(in: & subscriptions) }
1 2 3 === 範例: dropFirst === 9 10
1 2 3 4 5 6 7 8 9 example(of: "drop(while:)" ) { let numbers = (1 ... 10 ).publisher numbers .drop(while: { print ("x" ); return $0 % 5 != 0 }) .sink(receiveValue: { print ($0 ) }) .store(in: & subscriptions) }
1 2 3 === 範例: dropFirst === 9 10
1 2 3 4 5 6 7 8 9 10 11 12 13 14 example(of: "drop(untilOutputFrom:)" ) { let isReady = PassthroughSubject <Void , Never >() let taps = PassthroughSubject <Int , Never >() taps.drop(untilOutputFrom: isReady) .sink(receiveValue: { print ($0 ) }) .store(in: & subscriptions) (1 ... 5 ).forEach { n in taps.send(n) if n == 3 { isReady.send() } } }
1 2 3 === 範例: drop(untilOutputFrom:) === 4 5
跟drop(while:)相反的功能,只取前面幾組,後面的不重要
1 2 3 4 5 6 7 8 9 10 11 12 example(of: "prefix(while:)" ) { let numbers = (1 ... 10 ).publisher numbers .prefix(while: { $0 < 3 }) .sink( receiveCompletion: { print ("完成: \($0 ) " ) }, receiveValue: { print ($0 ) } ) .store(in: & subscriptions) }
1 2 3 4 === 範例: prefix(while :) === 1 2 完成: finished
跟drop(while:)相反的功能,只取前面幾組,後面的不重要
1 2 3 4 5 6 7 8 9 10 11 12 example(of: "prefix(while:)" ) { let numbers = (1 ... 10 ).publisher numbers .prefix(while: { $0 < 3 }) .sink( receiveCompletion: { print ("完成: \($0 ) " ) }, receiveValue: { print ($0 ) } ) .store(in: & subscriptions) }
1 2 3 4 === 範例: prefix(while :) === 1 2 完成: finished
跟drop(untilOutputFrom:)相反的功能,只取前面幾組,後面的不重要
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 example(of: "prefix(untilOutputFrom:)" ) { let isReady = PassthroughSubject <Void , Never >() let taps = PassthroughSubject <Int , Never >() taps .prefix(untilOutputFrom: isReady) .sink( receiveCompletion: { print ("完成: \($0 ) " ) }, receiveValue: { print ($0 ) } ) .store(in: & subscriptions) (1 ... 5 ).forEach { n in taps.send(n) if n == 2 { isReady.send() } } }
1 2 3 4 === 範例: prefix(untilOutputFrom:) === 1 2 完成: finished
Lesson.04 - 結合用的Operator
在原本的publisher之前加入數值,可以應用在一些一定要出現的介紹詞之上…
1 2 3 4 5 6 7 8 9 10 example(of: "prepend(Output...)" ) { let publisher = ["新會員" , "William" ].publisher publisher .prepend("歡迎來到『人生40才開始』" , "別忘了繳入會費喲" ) .prepend("嗨!!!" , "您好" ) .sink(receiveValue: { print ($0 ) }) .store(in: & subscriptions) }
1 2 3 4 5 6 7 === 範例: prepend(Output...) === 嗨!!! 您好 歡迎來到『人生40才開始』 別忘了繳入會費喲 新會員 William
1 2 3 4 5 6 7 8 9 10 11 12 13 14 example(of: "prepend(Sequence)" ) { let publisher = [5 , 6 , 7 ].publisher let array = [3 , 4 ] let set = Set (1 ... 2 ) let stride = stride (from: 6 , to: 11 , by: 2 ) publisher .prepend(array) .prepend(set ) .prepend(stride) .sink(receiveValue: { print ($0 ) }) .store(in: & subscriptions) }
1 2 3 4 5 6 7 8 9 10 11 === 範例: prepend(Sequence) === 6 8 10 1 2 3 4 5 6 7
1 2 3 4 5 6 7 8 9 10 example(of: "prepend(Publisher)" ) { let publisher1 = [3 , 4 ].publisher let publisher2 = [1 , 2 ].publisher publisher1 .prepend(publisher2) .sink(receiveValue: { print ($0 ) }) .store(in: & subscriptions) }
1 2 3 4 5 === 範例: prepend(Publisher) === 1 2 3 4
1 2 3 4 5 6 7 8 9 10 11 12 13 14 example(of: "prepend(Publisher) #2" ) { let publisher1 = [3 , 4 ].publisher let publisher2 = PassthroughSubject <Int , Never >() publisher1 .prepend(publisher2) .sink(receiveValue: { print ($0 ) }) .store(in: & subscriptions) publisher2.send(1 ) publisher2.send(2 ) publisher2.send(completion: .finished) }
1 2 3 4 5 === 範例: prepend(Publisher) 1 2 3 4
append正好跟prepend相反,它是加在後面的功能…
1 2 3 4 5 6 7 8 9 10 example(of: "append(Output...)" ) { let publisher = ["您好" , "我是William" ].publisher publisher .append("-演出人員名單-" , "-配樂清單-" ) .append("~終~" ) .sink(receiveValue: { print ($0 ) }) .store(in: & subscriptions) }
1 2 3 4 5 6 === 範例: append(Output...) === 您好 我是William -演出人員名單- -配樂清單- ~終~
1 2 3 4 5 6 7 8 9 10 11 12 13 14 example(of: "append(Output...) #2" ) { let publisher = PassthroughSubject <Int , Never >() publisher .append(3 , 4 ) .append(5 ) .sink(receiveValue: { print ($0 ) }) .store(in: & subscriptions) publisher.send(1 ) publisher.send(2 ) publisher.send(completion: .finished) }
1 2 3 4 5 6 === 範例: append(Output...) 1 2 3 4 5
跟append(Sequence)相反,就不多做介紹了
1 2 3 4 5 6 7 8 9 10 11 example(of: "append(Sequence)" ) { let publisher = [1 , 2 , 3 ].publisher publisher .append([4 , 5 ]) .append(Set ([6 , 7 ])) .append(stride (from: 8 , to: 11 , by: 2 )) .sink(receiveValue: { print ($0 ) }) .store(in: & subscriptions) }
1 2 3 4 5 6 7 8 9 10 === 範例: append(Sequence) === 1 2 3 4 5 7 6 8 10
跟append(Publisher)相反,也不多做介紹了
1 2 3 4 5 6 7 8 9 10 example(of: "append(Publisher)" ) { let publisher1 = [1 , 2 ].publisher let publisher2 = [3 , 4 ].publisher publisher1 .append(publisher2) .sink(receiveValue: { print ($0 ) }) .store(in: & subscriptions) }
1 2 3 4 5 === 範例: append(Publisher) === 1 2 3 4
切換到最後一個Publisher
在應用上的話,就像及時搜尋單字框,會一直送request,但是我們只需要最後一次的單字就好,中間的不要動作,減少多餘的request發送
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 example(of: "switchToLatest" ) { let publisher1 = PassthroughSubject <Int , Never >() let publisher2 = PassthroughSubject <Int , Never >() let publisher3 = PassthroughSubject <Int , Never >() let publishers = PassthroughSubject <PassthroughSubject <Int , Never >, Never >() publishers .switchToLatest() .sink( receiveCompletion: { _ in print ("Completed!" ) }, receiveValue: { print ($0 ) } ) .store(in: & subscriptions) publishers.send(publisher1) publisher1.send(1 ) publisher1.send(2 ) publishers.send(publisher2) publisher1.send(3 ) publisher2.send(4 ) publisher2.send(5 ) publishers.send(publisher3) publisher2.send(6 ) publisher3.send(7 ) publisher3.send(8 ) publisher3.send(9 ) publisher3.send(completion: .finished) publishers.send(completion: .finished) }
1 2 3 4 5 6 7 8 9 === 範例: switchToLatest === publisher1 - 1 publisher1 - 2 publisher2 - 4 publisher2 - 5 publisher3 - 7 publisher3 - 8 publisher3 - 9 Completed!
實際應用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 example(of: "switchToLatest - Network Request" ) { let url = URL (string: "https://picsum.photos/128" )! let taps = PassthroughSubject <Void , Never >() taps.map { _ in getImage() } .switchToLatest() .sink(receiveValue: { _ in }) .store(in: & subscriptions) taps.send() DispatchQueue .main.asyncAfter(deadline: .now() + 3.0 ) { print (Date ()); taps.send() } DispatchQueue .main.asyncAfter(deadline: .now() + 3.1 ) { print (Date ()); taps.send() } DispatchQueue .main.asyncAfter(deadline: .now() + 3.2 ) { print (Date ()); taps.send() } DispatchQueue .main.asyncAfter(deadline: .now() + 3.3 ) { print (Date ()); taps.send() } DispatchQueue .main.asyncAfter(deadline: .now() + 3.4 ) { print (Date ()); taps.send() } func getImage () -> AnyPublisher <UIImage ?, Never > { URLSession .shared .dataTaskPublisher(for: url) .map { data, _ in UIImage (data: data) } .print("[image]" ) .replaceError(with: nil ) .eraseToAnyPublisher() } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 === 範例: switchToLatest - Network Request === [image]: receive subscription: (DataTaskPublisher) [image]: request unlimited [image]: receive value: (Optional(<UIImage:0x600001eec480 anonymous {128, 128} renderingMode=automatic(original)>)) [image]: receive finished 2023-05-30 07:02:02 +0000 [image]: receive subscription: (DataTaskPublisher) [image]: request unlimited 2023-05-30 07:02:02 +0000 [image]: receive cancel [image]: receive subscription: (DataTaskPublisher) [image]: request unlimited 2023-05-30 07:02:02 +0000 [image]: receive cancel [image]: receive subscription: (DataTaskPublisher) [image]: request unlimited 2023-05-30 07:02:02 +0000 [image]: receive cancel [image]: receive subscription: (DataTaskPublisher) [image]: request unlimited 2023-05-30 07:02:02 +0000 [image]: receive cancel [image]: receive subscription: (DataTaskPublisher) [image]: request unlimited [image]: receive value: (Optional(<UIImage:0x600001eec5a0 anonymous {128, 128} renderingMode=automatic(original)>)) [image]: receive finished
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 example(of: "merge(with:)" ) { let publisher1 = PassthroughSubject <Int , Never >() let publisher2 = PassthroughSubject <Int , Never >() publisher1 .merge(with: publisher2) .sink( receiveCompletion: { _ in print ("Completed" ) }, receiveValue: { print ($0 ) } ) .store(in: & subscriptions) publisher1.send(1 ) publisher1.send(2 ) publisher2.send(3 ) publisher1.send(4 ) publisher2.send(5 ) publisher1.send(completion: .finished) publisher2.send(completion: .finished) }
1 2 3 4 5 6 7 === 範例: merge(with:) === 1 2 3 4 5 Completed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 example(of: "combineLatest" ) { let publisher1 = PassthroughSubject <Int , Never >() let publisher2 = PassthroughSubject <String , Never >() publisher1 .combineLatest(publisher2) .sink( receiveCompletion: { _ in print ("Completed" ) }, receiveValue: { print ("(\($0 ) , \($1 ) )" ) } ) .store(in: & subscriptions) publisher1.send(1 ) publisher2.send("a" ) publisher1.send(2 ) publisher2.send("b" ) publisher1.send(3 ) publisher2.send("c" ) publisher1.send(completion: .finished) publisher2.send(completion: .finished) }
1 2 3 4 5 6 7 === 範例: combineLatest === (1, a) (2, a) (2, b) (3, b) (3, c) Completed
這個跟combineLatest不太一樣,它是一對一合併
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 example(of: "zip" ) { let publisher1 = PassthroughSubject <Int , Never >() let publisher2 = PassthroughSubject <String , Never >() publisher1 .zip(publisher2) .sink( receiveCompletion: { _ in print ("Completed" ) }, receiveValue: { print ("(\($0 ) ,\($1 ) )" ) } ) .store(in: & subscriptions) publisher1.send(1 ) publisher1.send(2 ) publisher2.send("a" ) publisher2.send("b" ) publisher1.send(3 ) publisher2.send("c" ) publisher2.send("d" ) publisher1.send(4 ) publisher2.send("e" ) publisher1.send(completion: .finished) publisher2.send(completion: .finished) }
1 2 3 4 5 6 === 範例: zip === (1 ,a) (2 ,b) (3 ,c) (4 ,d) Completed
Lesson.05 - 時間操作運算子
可以延遲來自發佈者的值,以便您看到它們比它們實際發生的時間晚…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 let delaySeccond = 2 let timerSeccond = 1 var subscriptions = Set <AnyCancellable >()let timerPublisher = Timer .publish(every: Double (timerSeccond) , on: .main, in: .common) .autoconnect() timerPublisher .handleEvents(receiveOutput: { date in print ("每\(timerSeccond) 秒發送訊息\t \t \(date) " ) }) .delay(for: .seconds(delaySeccond), scheduler: DispatchQueue .main) .sink { value in print ("收到前\(delaySeccond) 秒的訊息\t \(value) " ) } .store(in: & subscriptions)
1 2 3 4 5 6 7 8 每1秒發送訊息 2023-06-14 05:35:30 +0000 每1秒發送訊息 2023-06-14 05:35:31 +0000 每1秒發送訊息 2023-06-14 05:35:32 +0000 收到前2秒的訊息 2023-06-14 05:35:30 +0000 每1秒發送訊息 2023-06-14 05:35:33 +0000 收到前2秒的訊息 2023-06-14 05:35:31 +0000 每1秒發送訊息 2023-06-14 05:35:34 +0000 收到前2秒的訊息 2023-06-14 05:35:32 +0000
圖文版加強版
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 let valuesPerSecond = 1.0 let delayInSeconds = 1.5 let sourcePublisher = PassthroughSubject <Date , Never >()let delayedPublisher = sourcePublisher.delay(for: .seconds(delayInSeconds), scheduler: DispatchQueue .main)let subscription = Timer .publish(every: 1.0 / valuesPerSecond, on: .main, in: .common) .autoconnect() .subscribe(sourcePublisher) let sourceTimeline = TimelineView (title: "發射數值 - \(valuesPerSecond) 個/秒" )let delayedTimeline = TimelineView (title: "發射數值 - 延遲\(delayInSeconds) 秒" )let view = VStack (spacing: 50 ) { sourceTimeline delayedTimeline } sourcePublisher.displayEvents(in: sourceTimeline) delayedPublisher.displayEvents(in: delayedTimeline) PlaygroundPage .current.liveView = UIHostingController (rootView: view.frame(width: 800 , height: 600 ))
1 2 3 4 let subscriber = Timer .publish(every: 0.5 , on: .main, in: .default) .autoconnect() .collect(.byTime(RunLoop .main, .seconds(3 ))) .sink { print ("\($0 ) " , terminator: "\n \n " ) }
1 2 3 4 5 [2023-06-14 05:44:25 +0000, 2023-06-14 05:44:25 +0000, 2023-06-14 05:44:26 +0000, 2023-06-14 05:44:26 +0000, 2023-06-14 05:44:27 +0000] [2023-06-14 05:44:27 +0000, 2023-06-14 05:44:28 +0000, 2023-06-14 05:44:28 +0000, 2023-06-14 05:44:29 +0000, 2023-06-14 05:44:29 +0000, 2023-06-14 05:44:30 +0000] [2023-06-14 05:44:30 +0000, 2023-06-14 05:44:31 +0000, 2023-06-14 05:44:31 +0000, 2023-06-14 05:44:32 +0000, 2023-06-14 05:44:32 +0000, 2023-06-14 05:44:33 +0000]
圖文版加強版
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 let valuesPerSecond = 1.0 let collectTimeStride = 4 let collectMaxCount = 2 let sourcePublisher = PassthroughSubject <Date , Never >()let collectedPublisher = sourcePublisher .collect(.byTime(DispatchQueue .main, .seconds(collectTimeStride))) .flatMap { dates in dates.publisher } let collectedPublisher2 = sourcePublisher .collect(.byTimeOrCount(DispatchQueue .main, .seconds(collectTimeStride), collectMaxCount)) .flatMap { dates in dates.publisher } let subscription = Timer .publish(every: 1.0 / valuesPerSecond, on: .main, in: .common) .autoconnect() .subscribe(sourcePublisher) let sourceTimeline = TimelineView (title: "Emitted values:" )let collectedTimeline = TimelineView (title: "Collected values (every \(collectTimeStride) s):" )let collectedTimeline2 = TimelineView (title: "Collected values (at most \(collectMaxCount) every \(collectTimeStride) s):" )let view = VStack (spacing: 40 ) { sourceTimeline collectedTimeline collectedTimeline2 } PlaygroundPage .current.liveView = UIHostingController (rootView: view.frame(width: 600 , height: 600 ))sourcePublisher.displayEvents(in: sourceTimeline) collectedPublisher.displayEvents(in: collectedTimeline) collectedPublisher2.displayEvents(in: collectedTimeline2)
取時間內的最後一個數值
比如在短時間內Button連點的問題,只處理最後一次點選事件
也就是除了Delay,還會過濾
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 let subject = PassthroughSubject <Int , Never >()let bounces:[(Int , TimeInterval )] = [ (1 , 0.1 ), (2 , 0.2 ), (5 , 1.1 ), (6 , 1.2 ) ] var cancellable = subject .debounce(for: .seconds(0.5 ), scheduler: RunLoop .main) .sink { index in print ("Received index \(index) " ) } for bounce in bounces { DispatchQueue .main.asyncAfter(deadline: .now() + bounce.1 ) { subject.send(bounce.0 ) } }
圖文版加強版
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 let subject = PassthroughSubject <String , Never >()let debounced = subject .debounce(for: .seconds(1.0 ), scheduler: DispatchQueue .main) .share() let subjectTimeline = TimelineView (title: "Emitted values" )let debouncedTimeline = TimelineView (title: "Debounced values" )let view = VStack (spacing: 100 ) { subjectTimeline debouncedTimeline } subject.displayEvents(in: subjectTimeline) debounced.displayEvents(in: debouncedTimeline) let subscription1 = subject .sink { string in print ("+\(deltaTime) s: Subject emitted: \(string) " ) } let subscription2 = debounced .sink { string in print ("+\(deltaTime) s: Debounced emitted: \(string) " ) } subject.feed(with: typingHelloWorld) PlaygroundPage .current.liveView = UIHostingController (rootView: view.frame(width: 600 , height: 600 ))
限制數量用
如果在2秒內瘋狂點選按鈕,時間窗口的時長為0.5秒,那麼throttle可以傳送4次資料
而debounce不會傳送資料,只有當我停止點選0.5秒後,才會傳送一次資料。
1 2 3 4 5 6 7 8 let cancellable = Timer .publish(every: 3.0 , on: .main, in: .default) .autoconnect() .print("\(Date().description) " ) .throttle(for: 10.0 , scheduler: RunLoop .main, latest: true ) .sink( receiveCompletion: { print ("Completion: \($0 ) ." ) }, receiveValue: { print ("Received Timestamp \($0 ) ." ) } )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 2023-06-14 06:42:34 +0000: request unlimited 2023-06-14 06:42:34 +0000: receive value: (2023-06-14 06:42:37 +0000) Received Timestamp 2023-06-14 06:42:37 +0000. 2023-06-14 06:42:34 +0000: receive value: (2023-06-14 06:42:40 +0000) 2023-06-14 06:42:34 +0000: receive value: (2023-06-14 06:42:43 +0000) 2023-06-14 06:42:34 +0000: receive value: (2023-06-14 06:42:46 +0000) Received Timestamp 2023-06-14 06:42:46 +0000. 2023-06-14 06:42:34 +0000: receive value: (2023-06-14 06:42:49 +0000) 2023-06-14 06:42:34 +0000: receive value: (2023-06-14 06:42:52 +0000) 2023-06-14 06:42:34 +0000: receive value: (2023-06-14 06:42:55 +0000) Received Timestamp 2023-06-14 06:42:55 +0000. 2023-06-14 06:42:34 +0000: receive value: (2023-06-14 06:42:58 +0000) 2023-06-14 06:42:34 +0000: receive value: (2023-06-14 06:43:01 +0000) 2023-06-14 06:42:34 +0000: receive value: (2023-06-14 06:43:04 +0000) 2023-06-14 06:42:34 +0000: receive value: (2023-06-14 06:43:07 +0000) Received Timestamp 2023-06-14 06:43:07 +0000.
圖文版加強版
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 let throttleDelay = 1.0 let subject = PassthroughSubject <String , Never >()let throttled = subject .throttle(for: .seconds(throttleDelay), scheduler: DispatchQueue .main, latest: true ) .share() let subjectTimeline = TimelineView (title: "Emitted values" )let throttledTimeline = TimelineView (title: "Throttled values" )let view = VStack (spacing: 100 ) { subjectTimeline throttledTimeline } subject.displayEvents(in: subjectTimeline) throttled.displayEvents(in: throttledTimeline) let subscription1 = subject .sink { string in print ("+\(deltaTime) s: Subject emitted: \(string) " ) } let subscription2 = throttled .sink { string in print ("+\(deltaTime) s: Throttled emitted: \(string) " ) } subject.feed(with: typingHelloWorld) PlaygroundPage .current.liveView = UIHostingController (rootView: view.frame(width: 400 , height: 600 ))
1 2 3 4 5 6 7 8 9 10 11 12 13 14 let WaitTime : Int = 2 let TimeoutTime : Int = 5 let subject = PassthroughSubject <String , Never >()let cancellable = subject .timeout(.seconds(TimeoutTime ), scheduler: DispatchQueue .main, options: nil , customError:nil ) .sink( receiveCompletion: { print ("\(Date()) - \(TimeoutTime) 秒後完成: \($0 ) " ) }, receiveValue: { print ("\(Date()) - 數值: \($0 ) " ) } ) print ("\(Date()) - 開始" )DispatchQueue .main.asyncAfter(deadline: .now() + .seconds(WaitTime ), execute: { subject.send("\(Date()) - 等\(WaitTime) 秒後發出" ) } )
1 2 3 2023-06-14 06:55:46 +0000 - 開始 2023-06-14 06:55:49 +0000 - 數值: 2023-06-14 06:55:49 +0000 - 等2秒後發出 2023-06-14 06:55:54 +0000 - 完成: finished
圖文版加強版
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 enum TimeoutError : Error { case timedOut } let subject = PassthroughSubject <Void , TimeoutError >()let timedOutSubject = subject.timeout(.seconds(5 ), scheduler: DispatchQueue .main, customError: { .timedOut })let timeline = TimelineView (title: "Button taps" )let view = VStack (spacing: 100 ) { Button (action: { subject.send() }) { Text ("Press me within 5 seconds" ) } timeline } timedOutSubject.displayEvents(in: timeline) PlaygroundPage .current.liveView = UIHostingController (rootView: view.frame(width: 600 , height: 600 ))
測量並行出從上游發佈者接收到的事件之間的時間間隔…
可以看出時間間隔大約就是一秒
1 2 3 4 let cancellable = Timer .publish(every: 1 , on: .main, in: .default) .autoconnect() .measureInterval(using: RunLoop .main) .sink { print ("\($0 ) " , terminator: "\n " ) }
1 2 3 4 5 6 7 8 9 Stride(magnitude: 1.0004009008407593) Stride(magnitude: 1.0000990629196167) Stride(magnitude: 1.0000349283218384) Stride(magnitude: 1.0007370710372925) Stride(magnitude: 1.0000169277191162) Stride(magnitude: 0.9993090629577637) Stride(magnitude: 0.9999560117721558) Stride(magnitude: 1.0000669956207275) Stride(magnitude: 0.9999699592590332)
圖文版加強版
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 let subject = PassthroughSubject <String , Never >()let measureSubject = subject.measureInterval(using: DispatchQueue .main)let measureSubject2 = subject.measureInterval(using: RunLoop .main)let subjectTimeline = TimelineView (title: "Emitted values" )let measureTimeline = TimelineView (title: "Measured values" )let view = VStack (spacing: 100 ) { subjectTimeline measureTimeline } subject.displayEvents(in: subjectTimeline) measureSubject.displayEvents(in: measureTimeline) let subscription1 = subject.sink { print ("+\(deltaTime) s: Subject emitted: \($0 ) " ) } let subscription2 = measureSubject.sink { print ("+\(deltaTime) s: Measure emitted: \(Double($0 .magnitude) / 1_000_000_000.0 ) " ) } let subscription3 = measureSubject2.sink { print ("+\(deltaTime) s: Measure2 emitted: \($0 ) " ) } subject.feed(with: typingHelloWorld) PlaygroundPage .current.liveView = UIHostingController (rootView: view.frame(width: 375 , height: 600 ))
Lesson.06 - 序列用運算子
1 2 3 4 5 6 7 8 9 10 example(of: "min()" ) { let publisher = [1 , - 50 , 246 , 0 ].publisher publisher .print("publisher" ) .min() .sink(receiveValue: { print ("最小值:\($0 ) " ) }) .store(in: & subscriptions) }
1 2 3 4 5 6 7 8 9 === 範例: min() === publisher: receive subscription: ([1, -50, 246, 0]) publisher: request unlimited publisher: receive value: (1) publisher: receive value: (-50) publisher: receive value: (246) publisher: receive value: (0) publisher: receive finished 最小值:-50
1 2 3 4 5 6 7 8 9 10 11 12 13 14 example(of: "min non-Comparable" ) { let publisher = ["12345" , "ab" , "hello world" ] .map { Data ($0 .utf8) } .publisher publisher .print("publisher" ) .min { $0 .count < $1 .count } .sink { data in guard let string = String (data: data, encoding: .utf8) else { return } print ("最小資料的是 \(string) , \(string.count) bytes" ) }.store(in: & subscriptions) }
1 2 3 4 5 6 7 8 === 範例: min non-Comparable === publisher: receive subscription: ([5 bytes, 2 bytes, 11 bytes]) publisher: request unlimited publisher: receive value: (5 bytes) publisher: receive value: (2 bytes) publisher: receive value: (11 bytes) publisher: receive finished 最小資料的是 ab, 2 bytes
1 2 3 4 5 6 7 8 9 10 example(of: "max()" ) { let publisher = ["A" , "F" , "Z" , "E" ].publisher publisher .print("publisher" ) .max() .sink(receiveValue: { print ("最大值:\($0 ) " ) }) .store(in: & subscriptions) }
1 2 3 4 5 6 7 8 9 === 範例: max() === publisher: receive subscription: (["A" , "F" , "Z" , "E" ]) publisher: request unlimited publisher: receive value: (A) publisher: receive value: (F) publisher: receive value: (Z) publisher: receive value: (E) publisher: receive finished 最大值:Z
1 2 3 4 5 6 7 8 9 10 11 12 13 14 example(of: "max()" ) { let publisher = PassthroughSubject <Int , Never >() publisher .print("publisher" ) .max() .sink(receiveValue: { print ("最大值:\($0 ) " ) }) .store(in: & subscriptions) publisher.send(1 ) publisher.send(2 ) publisher.send(completion: .finished) }
1 2 3 4 5 6 7 === 範例: max() === publisher: receive subscription: (PassthroughSubject) publisher: request unlimited publisher: receive value: (1) publisher: receive value: (2) publisher: receive finished 最大值:2
1 2 3 4 5 6 7 8 9 10 example(of: "first()" ) { let publisher = ["A" , "F" , "Z" , "E" ].publisher publisher .print("publisher" ) .first() .sink(receiveValue: { print ("第一個值:\($0 ) " ) }) .store(in: & subscriptions) }
1 2 3 4 5 6 === 範例: first() === publisher: receive subscription: (["A" , "F" , "Z" , "E" ]) publisher: request unlimited publisher: receive value: (A) publisher: receive cancel 第一個值:A
1 2 3 4 5 6 7 8 9 10 example(of: "first(where:)" ) { let publisher = ["J" , "O" , "H" , "N" ].publisher publisher .print("publisher" ) .first(where: { "Hello World" .contains($0 ) }) .sink(receiveValue: { print ("符合條件的第一個值:\($0 ) " ) }) .store(in: & subscriptions) }
1 2 3 4 5 6 7 8 === 範例: first(where :) === publisher: receive subscription: (["J" , "O" , "H" , "N" ]) publisher: request unlimited publisher: receive value: (J) publisher: receive value: (O) publisher: receive value: (H) publisher: receive cancel 符合條件的第一個值:H
1 2 3 4 5 6 7 8 9 10 example(of: "last()" ) { let publisher = ["A" , "E" , "I" , "O" , "U" ].publisher publisher .print("publisher" ) .last() .sink(receiveValue: { print ("最後一個值:\($0 ) " ) }) .store(in: & subscriptions) }
1 2 3 4 5 6 7 8 9 10 === 範例: last() === publisher: receive subscription: (["A" , "E" , "I" , "O" , "U" ]) publisher: request unlimited publisher: receive value: (A) publisher: receive value: (E) publisher: receive value: (I) publisher: receive value: (O) publisher: receive value: (U) publisher: receive finished 最後一個值:U
1 2 3 4 5 6 7 8 9 10 11 example(of: "output(at:)" ) { let publisher = ["A" , "E" , "I" , "O" , "U" ].publisher let index = 2 publisher .print("publisher" ) .output(at: index) .sink(receiveValue: { print ("index = \(index) 的值:\($0 ) " ) }) .store(in: & subscriptions) }
1 2 3 4 5 6 7 8 9 10 === 範例: output(at:) === publisher: receive subscription: (["A" , "E" , "I" , "O" , "U" ]) publisher: request unlimited publisher: receive value: (A) publisher: request max: (1) (synchronous) publisher: receive value: (E) publisher: request max: (1) (synchronous) publisher: receive value: (I) index = 2的值:I publisher: receive cancel
1 2 3 4 5 6 7 8 9 10 11 12 13 example(of: "output(in:)" ) { let publisher = ["A" , "E" , "I" , "O" , "U" ].publisher let range = 1 ... 3 publisher .output(in: range) .sink( receiveCompletion: { print ($0 ) }, receiveValue: { print ("區間內的值:\($0 ) " ) } ) .store(in: & subscriptions) }
1 2 3 4 5 === 範例: output(in :) === 區間內的值:E 區間內的值:I 區間內的值:O finished
1 2 3 4 5 6 7 8 9 10 example(of: "count()" ) { let publisher = ["A" , "B" , "C" ].publisher publisher .print("publisher" ) .count() .sink(receiveValue: { print ("有\($0 ) 個項目" ) }) .store(in: & subscriptions) }
1 2 3 4 5 6 7 8 === 範例: count() === publisher: receive subscription: (["A" , "B" , "C" ]) publisher: request unlimited publisher: receive value: (A) publisher: receive value: (B) publisher: receive value: (C) publisher: receive finished 有3個項目
1 2 3 4 5 6 7 8 9 10 11 12 13 example(of: "contains()" ) { let publisher = ["A" , "B" , "C" , "D" , "E" ].publisher let letter = "C" publisher .print("publisher" ) .contains(letter) .sink(receiveValue: { isContains in print (isContains ? "Publisher包含 - \(letter) " : "Publisher不包含 - \(letter) !" ) }) .store(in: & subscriptions) }
1 2 3 4 5 6 7 8 === 範例: contains() === publisher: receive subscription: (["A" , "B" , "C" , "D" , "E" ]) publisher: request unlimited publisher: receive value: (A) publisher: receive value: (B) publisher: receive value: (C) publisher: receive cancel Publisher包含 - C
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 example(of: "contains(where:)" ) { struct Person { let id: Int let name: String } let people = [(123 , "Shai Mishali" ), (777 , "Marin Todorov" ), (214 , "Florent Pillet" )] .map(Person .init ) .publisher people .contains(where: { $0 .id == 800 || $0 .name == "Marin Todorov" }) .sink(receiveValue: { isContains in print (isContains ? "有找到符合條件的值" : "沒有找到符合條件的值" ) }) .store(in: & subscriptions) }
1 2 === 範例: contains(where :) === 有找到符合條件的值
1 2 3 4 5 6 7 8 9 10 11 12 13 example(of: "allSatisfy()" ) { let publisher = stride (from: 0 , to: 5 , by: 1 ).publisher let condition: (Int ) -> Bool = { $0 % 2 == 0 } publisher .print("publisher" ) .allSatisfy { condition($0 ) } .sink(receiveValue: { isAllEven in print (isAllEven ? "全部都是偶數" : "不全都是偶數" ) }) .store(in: & subscriptions) }
1 2 3 4 5 6 7 === 範例: allSatisfy() === publisher: receive subscription: (Sequence) publisher: request unlimited publisher: receive value: (0) publisher: receive value: (1) publisher: receive cancel 不全都是偶數
1 2 3 4 5 6 7 8 9 10 example(of: "reduce()" ) { let publisher = ["Hel" , "lo" , " " , "Wor" , "ld" , "!" ].publisher publisher .print("publisher" ) .reduce("" , + ) .sink(receiveValue: { print ("合併為:\($0 ) " ) }) .store(in: & subscriptions) }
1 2 3 4 5 6 7 8 9 10 11 === 範例: reduce() === publisher: receive subscription: (["Hel" , "lo" , " " , "Wor" , "ld" , "!" ]) publisher: request unlimited publisher: receive value: (Hel) publisher: receive value: (lo) publisher: receive value: ( ) publisher: receive value: (Wor) publisher: receive value: (ld) publisher: receive value: (!) publisher: receive finished 合併為:Hello World!