prepend
add (something) to the beginning of something else.
func prepend(_ elements: Self.Output...) -> Publishers.Concatenate<Publishers.Sequence<[Self.Output], Self.Failure>, Self>
publisher의 output 앞에 특정한 값을 붙이는 operator
let dataElements = (0...10)
cancellable = dataElements.publisher
.prepend(0, 1, 255)
.sink { print("\($0)", terminator: " ") }
// Prints: "0 1 255 0 1 2 3 4 5 6 7 8 9 10"
0, 1, 255
가 먼저 publish 되고 dataElements
의 항목들이 publish된다.let publisher = [3, 4].publisher
publisher
.prepend(1, 2)
.prepend(-1, 0)
.sink(receiveValue: { print($0) })
/*
-1
0
1
2
3
4
*/
마지막에 연결된 prepend
가 가장 앞에 온다.
→ prepend(1, 2)
를 실행해서 1, 2가 붙어서 새로 publish 되고, 그 다음 prepend(-1, 0)
가 실행되어서 그 앞에 또 -1, 0이 붙어서 다시 publish 되니까?!
struct Concatenate<Prefix, Suffix> where Prefix : Publisher, Suffix : Publisher, Prefix.Failure == Suffix.Failure, Prefix.Output == Suffix.Output
한 publisher의 모든 값을 다른 publisher 전에 방출하는 publisher
suffix
가 값을 다시 방출하기 전에 모든 값을 다시 publish파라미터로 sequence를 받을 수도 있다.
Sequence가 뭐지?
배열이나 Range 등을 publisher로 변환하면 Publishers.Sequence
타입이 된다.
Publishers.Sequence는 여러 항목을 연속으로 publish하는 publisher이다.
이 Sequence는 생성할 때 Elements
를 파라미터로 받는데, 이 친구는 Sequence
라는 프로토콜을 준수하는 타입이어야 한다.
Swift의 Array, Set, Dictionary 등은 Collection
프로토콜을 준수하고 있는데, Collection
은 Sequence
를 준수하고 있다.
그래서 Array 같은 Collection 타입들을 publisher로 변환하면 Publishers.Sequence
라는 타입이 되었던 것!
// 1
let publisher = [5, 6, 7].publisher
// 2
publisher
.prepend([3, 4])
.prepend(Set(1...2))
.prepend(stride(from: 6, to: 11, by: 2))
.sink(receiveValue: { print($0) })
.store(in: &subscriptions)
/*
6
8
10
1
2
3
4
5
6
7
*/
Set
은 순서를 보장하지 않기 때문에 위처럼 1, 2
가 아닌 2, 1
처럼 출력될 수 있다.stride
처럼 다양한 Sequence 타입을 전달할 수 있다.👀 combining operator라면서 ,, 단순한 값이 아니라 다른 publisher의 값도 붙일 수는 없나?
func prepend<P>(_ publisher: P) -> Publishers.Concatenate<P, Self> where P : Publisher, Self.Failure == P.Failure, Self.Output == P.Output
publisher의 output 앞에 매개변수로 받은 publisher가 방출한 항목을 붙이는 operator
let prefixValues = [0, 1, 255]
let dataElements = (0...10)
cancellable = dataElements.publisher
.prepend(prefixValues.publisher)
.sink { print("\($0)", terminator: " ") }
// Prints: "0 1 255 0 1 2 3 4 5 6 7 8 9 10"
dataLements
가 값을 publish 하기 전에 prefixValues
가 값을 publish 한다.
// 1
let publisher1 = [3, 4].publisher
let publisher2 = PassthroughSubject<Int, Never>()
// 2
publisher1
.prepend(publisher2)
.sink(receiveValue: { print($0) })
.store(in: &subscriptions)
// 3
publisher2.send(1)
publisher2.send(2)
/*
1
2
*/
먼저 진행된 publisher가 반드시 완료된 후에 다음 publisher가 값을 방출할 수 있다.
publisher2.send(completion: .finished)
publisher2
를 종료시키면 publisher1
도 값을 방출할 수 있다.
1
2
3
4
prepend와 비슷하다. 대신 publisher의 뒤에 값을 추가해줌!
func append(_ elements: Self.Output...) -> Publishers.Concatenate<Self, Publishers.Sequence<[Self.Output], Self.Failure>>
publisher의 output 뒤에 특정한 값들을 추가하는 operator
let dataElements = (0...10)
cancellable = dataElements.publisher
.append(0, 1, 255)
.sink { print("\($0)", terminator: " ") }
// Prints: "0 1 2 3 4 5 6 7 8 9 10 0 1 255"
dataElements
의 모든 값들을 다시 publish 한 다음에 파라미터로 받은 값들을 publish 한다.
append와 prepend의 가장 큰 차이점은 원래 publisher가 .finished
로 종료된 후에 작동한다는 것이다.
append()의 리턴 타입이 Publishers.Concatenate<Self, Publishers.Sequence<[Self.Output], Self.Failure>>
었고, Concatenate의 suffix는 preffix가 종료된 후에 다시 publish 된다고 했기 때문
// 1
let publisher = PassthroughSubject<Int, Never>()
publisher
.append(3, 4)
.append(5)
.sink(receiveValue: { print($0) })
// 2
publisher.send(1)
publisher.send(2)
/*
1
2
*/
publisher
가 종료되지 않았기 때문에 append()
가 아무런 영향을 주지 않는다.
publisher.send(completion: .finished)
publisher를 종료시키면 원하는 결과를 얻을 수 있다.
1
2
3
4
5
Publishers.Concatenate<Prefix, Suffix>
는 항상 앞에 애가 finish 되어야 뒤에 애의 값을 republish 할 수 있다는 걸 기억하자!!
Sequence를 파라미터로 받는 append operator
// 1
let publisher = [1, 2, 3].publisher
publisher
.append([4, 5]) // 2
.append(Set([6, 7])) // 3
.append(stride(from: 8, to: 11, by: 2)) // 4
.sink(receiveValue: { print($0) })
.store(in: & subscriptions)
/*
1
2
3
4
5
7 // 순서가 바뀔 수 있음
6 // 순서가 바뀔 수 있음
8
10
*/
prepend와 마찬가지로 다양한 종류의 Sequence를 append 할 수 있다.
Publisher를 파라미터로 받는 append operator
// 1
let publisher1 = [1, 2].publisher
let publisher2 = [3, 4].publisher
// 2
publisher1
.append(publisher2)
.sink(receiveValue: { print($0) })
/*
1
2
3
4
*/
가장 최신의 publisher가 값을 방출하면 기존 publisher의 구독을 취소하고 최신 publisher로 구독을 전환(switching)하는 operator
// 1
let publisher1 = PassthroughSubject<Int, Never>()
let publisher2 = PassthroughSubject<Int, Never>()
let publisher3 = PassthroughSubject<Int, Never>()
// 2
let publishers = PassthroughSubject<PassthroughSubject<Int, Never>, Never>()
// 3
publishers
.switchToLatest()
.sink(receiveCompletion: { _ in print("Completed!") },
receiveValue: { print($0) })
.store(in: &subscriptions)
// 4
publishers.send(publisher1)
publisher1.send(1)
publisher1.send(2)
// 5
publishers.send(publisher2)
publisher1.send(3)
publisher2.send(4)
publisher2.send(5)
// 6
publishers.send(publisher3)
publisher2.send(6)
publisher3.send(7)
publisher3.send(8)
publisher3.send(9)
// 7
publisher3.send(completion: .finished)
publishers.send(completion: .finished)
3개의 PassthroughSubjects
를 만듦
PassthroughSubjects
를 받는 PassthroughSubjects
를 만듦
publishers
에 switchToLatest()
적용
publisher1
을 publishers
로 보내고 1, 2 값을 보냄
→ 현재 publishers
는 publisher1
구독!
publishers
에 publisher2
를 보냄
→ publishers
가 publisher2
를 구독해서 publisher1
의 값은 무시됨
publishers
에 publisher3
을 보냄
→ publishers
가 publisher3
을 구독해서 publisher2
의 값은 무시됨
publisher3
과 publishers
을 종료해서 활성화된 모든 구독을 완료함
1
2
4
5
7
8
9
completed!
struct SwitchToLatest<P, Upstream> where P : Publisher, P == Upstream.Output, Upstream : Publisher, P.Failure == Upstream.Failure
🤔 위 예시에서
publishers
의 Output 타입은PassthroughSubject
인데.. 왜 Int 값이 출력되지?
⇒ switchToLatest()는 publisher들을 납작하게 (flatten) 만든 publisher를 리턴하기 때문이다!
let url = URL(string: "https://source.unsplash.com/random")!
// 1
func getImage() -> AnyPublisher<UIImage?, Never> {
return URLSession.shared
.dataTaskPublisher(for: url)
.map { data, _ in UIImage(data: data) }
.print("image")
.replaceError(with: nil)
.eraseToAnyPublisher()
}
// 2
let taps = PassthroughSubject<Void, Never>()
taps
.map { _ in getImage() } // 3
.switchToLatest() // 4
.sink(receiveValue: { _ in })
.store(in: &subscriptions)
// 5
taps.send()
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
taps.send()
}
DispatchQueue.main.asyncAfter(deadline: .now() + 3.1) {
taps.send()
}
랜덤 이미지를 가져오는 API를 호출하는 메소드 getImage()
작성
사용자가 버튼을 터치하는 이벤트를 방출하는 PassthroughSubject
선언
버튼을 터치하면 getImage()
가 호출되고, switchToLatest()도 적용
taps
에 이벤트를 3번 보냄
image: receive subscription: (DataTaskPublisher)
image: request unlimited
image: receive value: (Optional(<UIImage:0x600000364120 anonymous {1080, 720}>))
image: receive finished
image: receive subscription: (DataTaskPublisher)
image: request unlimited
image: receive cancel
image: receive subscription: (DataTaskPublisher)
image: request unlimited
image: receive value: (Optional(<UIImage:0x600000378d80 anonymous {1080, 1620}>))
image: receive finished
실제로는 2개의 이미지만 가져와진다. 두 번째 send 0.1초 뒤에 다음 send가 불려서 두 번째 구독은 취소되었기 때문!
func merge(with other: Self) -> Publishers.MergeMany<Self>
같은 타입의 다른 publisher와 합쳐서 값들을 상호 교차해서 전달해주는 operator
// 1
let publisher1 = PassthroughSubject<Int, Never>()
let publisher2 = PassthroughSubject<Int, Never>()
// 2
publisher1
.merge(with: publisher2)
.sink(receiveCompletion: { _ in print("Completed") },
receiveValue: { print($0) })
.store(in: &subscriptions)
// 3
publisher1.send(1)
publisher1.send(2)
publisher2.send(3)
publisher1.send(4)
publisher2.send(5)
// 4
publisher1.send(completion: .finished)
publisher2.send(completion: .finished)
/*
1
2
3
4
5
Completed
*/
func combineLatest<P>(_ other: P) -> Publishers.CombineLatest<Self, P> where P : Publisher, Self.Failure == P.Failure
다른 publisher를 구독하고, publisher들로부터 마지막으로 받은 output의 튜플을 publish하는 operator
let pub1 = PassthroughSubject<Int, Never>()
let pub2 = PassthroughSubject<Int, Never>()
cancellable = pub1
.combineLatest(pub2)
.sink { print("Result: \($0).") }
pub1.send(1)
pub1.send(2)
pub2.send(2)
pub1.send(3)
pub1.send(45)
pub2.send(22)
// Prints:
// Result: (2, 2). // pub1 latest = 2, pub2 latest = 2
// Result: (3, 2). // pub1 latest = 3, pub2 latest = 2
// Result: (45, 2). // pub1 latest = 45, pub2 latest = 2
// Result: (45, 22). // pub1 latest = 45, pub2 latest = 22
모든 upstream publisher들이 종료되면 이 publisher도 종료된다.
upstream publisher가 값을 publish하지 않으면 이 publisher도 절대 종료되지 않는다.
→ combineLatest로 결합된 모든 publisher들은 최소 한 번 이상 값을 방출해야 한다.
func zip<P>(_ other: P) -> Publishers.Zip<Self, P> where P : Publisher, Self.Failure == P.Failure
다른 publisher와 결합하고, 그 값들의 쌍을 튜플 형태로 전달하는 operator
let numbersPub = PassthroughSubject<Int, Never>()
let lettersPub = PassthroughSubject<String, Never>()
cancellable = numbersPub
.zip(lettersPub)
.sink { print("\($0)") }
numbersPub.send(1) // numbersPub: 1 lettersPub: zip output: <none>
numbersPub.send(2) // numbersPub: 1,2 lettersPub: zip output: <none>
letters.send("A") // numbers: 1,2 letters:"A" zip output: <none>
numbers.send(3) // numbers: 1,2,3 letters: zip output: (1,"A")
letters.send("B") // numbers: 1,2,3 letters: "B" zip output: (2,"B")
// Prints:
// (1, "A")
// (2, "B")
upstream publisher 중 하나라도 정상적으로 종료되거나 실패하면 zip 된 publisher도 똑같이 종료된다.
merge
combineLatest
zip