เริ่มต้น Reactive Programming ด้วย Combine Part5: Combining Operator

Cocodev
Lotus’s IT
Published in
3 min readDec 18, 2023

Combine มี operators หลายตัวที่ใช้ในการผสานข้อมูลจาก publishers ต่างๆ และแต่ละตัวมีวัตถุประสงค์เฉพาะของตัวเอง ในบทนี้เราจะมาสำรวจว่าแต่ละตัวทำงานยังไงบ้างและเราสามารถหยิบเอามาใช้ให้เหมาะสมกับงาน

combineLatest

คือการรวมค่าสุดท้ายของ publishers มากกว่า 2 ตัวขึ้นไป และไม่จำเป็นต้องเป็น type เดียวกันก็ได้ จะทำงานก็ต่อเมื่อ publishers ตัวใดตัวหนึ่ง มี Event ใหม่เข้ามาและใช้ค่าล่าสุดของแต่ publishers มาคำนวณ

func combineLatest<P, T>(
_ other: P,
_ transform: @escaping (Self.Output, P.Output) -> T
) -> Publishers.Map<Publishers.CombineLatest<Self, P>, T> where P : Publisher, Self.Failure == P.Failure

example

let pub1 = PassthroughSubject<Int, Never>()
let pub2 = PassthroughSubject<Int, Never>()


cancellable = pub1
.combineLatest(pub2) { (v1, v2) in
return v1 * v2
}
.sink { print("Result: \($0).") }


pub1.send(1)
pub1.send(2)
pub2.send(2)
pub1.send(9)
pub1.send(3)
pub2.send(12)
pub1.send(13)
//
// Prints:
//Result: 4. (pub1 latest = 2, pub2 latest = 2)
//Result: 18. (pub1 latest = 9, pub2 latest = 2)
//Result: 6. (pub1 latest = 3, pub2 latest = 2)
//Result: 36. (pub1 latest = 3, pub2 latest = 12)
//Result: 156. (pub1 latest = 13, pub2 latest = 12)

combineLatest อธิบายโดยใช้ marble diagram อธิบายได้ดังนี้

pub1:   |----1----2--------9----3---------11-------------------|-->
pub2: |-------------2--------------12------------------------|-->

opratoion: pub1.combineLatest.pub2 { v1 * v2 }

newPub: |--------------4---18----6----36--156------------------|-->

time(s):|----|----|----|----|----|----|----|----|----|----|----|-->
0 1 2 3 4 5 6 7 8 9 10 11

append

คือการเอา publishers ที่มีการสิ้นสุดการทำงานแล้วหรือสามารถเกิด event ได้อีก มาต่อกัน เช่น a.append(b)

func append(_ elements: Self.Output...) -> Publishers.Concatenate<Self, Publishers.Sequence<[Self.Output], Self.Failure>>

example

let numbers1 = (1...5).publisher
let numbers2 = (6...10).publisher
let numbers3 = numbers1.append(numbers2)

numbers3.sink {
print($0) // 1,2,3,4,5,6,7,8,9,10z
}
numbers1:   |--12345--|------------------->
numbers2: |--5678910-|------------------>

opratoion: numbers1.append(numbers2)

numbers3: |--------------12345678910------------------|-->

time(s):|----|----|----|----|----|----|----|----|----|----|----|-->
0 1 2 3 4 5 6 7 8 9 10 11

merge

คือการรวม publishers มากกว่า 1 ตัวเข้ามาเป็น publishers ตัวเดียวกัน

func merge<B, C>(
with b: B,
_ c: C
) -> Publishers.Merge3<Self, B, C> where B : Publisher, C : Publisher, Self.Failure == B.Failure, Self.Output == B.Output, B.Failure == C.Failure, B.Output == C.Output

exmaple

let pubA = PassthroughSubject<Int, Never>()
let pubB = PassthroughSubject<Int, Never>()
let pubC = PassthroughSubject<Int, Never>()

let newPub = pubA.merge(with: pubB, pubC)

let cancellable = newPub.sink { print("\($0)", terminator: "," )}

pubA.send(1)
pubB.send(40)
pubC.send(90)
pubA.send(2)
pubB.send(50)
pubC.send(4)


// Output: 1,40,90,2,50,4,
pubA:   |----1-------------2-----------------------------------|-->
pubB: |--------40-------------50-----------------------------|-->
pubC: |-------------90-------------4-------------------------|-->

opratoion: pubA.merge(with: pubB, pubC)

newPub: |----1---40----90---2---50----4------------------------|-->

time(s):|----|----|----|----|----|----|----|----|----|----|----|-->
0 1 2 3 4 5 6 7 8 9 10 11

zip

คือการรวม publishers โดยจับคู่ value ในลำดับที่เท่ากัน อาจจะมองไม่เห็นภาพดูตัวอย่างกันดีกว่า

func zip<P>(_ other: P) -> Publishers.Zip<Self, P> where P : Publisher, Self.Failure == P.Failure

example

let numbersPub = PassthroughSubject<Int, Never>()
let lettersPub = PassthroughSubject<String, Never>()
let zipPub = numbersPub.zip(lettersPub)

let cancellable = zipPub.sink { print("\($0)") }
numbersPub.send(1) // numbersPub: 1 lettersPub: zip output: <none>
numbersPub.send(2) // numbersPub: 1,2 lettersPub: zip output: <none>
lettersPub.send("A")// numbers: 1,2 letters:"A" zip output: <none>
numbersPub.send(3) // numbers: 1,2,3 letters: zip output: (1,"A")
lettersPub.send("B")// numbers: 1,2,3 letters: "B" zip output: (2,"B")


// Prints:
// (1, "A")
// (2, "B")
numbersPub: |----1----2---------3----------------------------------|-->
lettersPub: |--------------A---------B-----------------------------|-->
opratoion: numbersPub.zip(lettersPub)

zipPub: |------------(1,A)-----(2,B)---------------------------|-->

time(s): |----|----|----|----|----|----|----|----|----|----|----|-->
0 1 2 3 4 5 6 7 8 9 10 11

บทความนี้เป็นการยกตัวอย่าง Combining Operator ที่สำคัญๆ 4 ตัว แต่จริงๆแล้วมันมีมากกว่านี้ สามารถเข้าไปอ่านได้ที่ developer.apple.com หรือลิ้งด่านล้างนี้ครับ

Next part6: Reactive Programming ด้วย Combine Part6: Filtering Operator

https://medium.com/@ittipongkeawmahing/ea3e6591dabc

--

--