collect
operator는 각각의 값들을 하나의 array로 만드는 쉬운 방법이다.
buffering operator를 사용할 때에는 unbounded되어 있을 때 주의해야한다.
upstream이 끝나기 전에는 emit를 하지 않으므로 많은 양의 메모리를 사용할 수 있기 때문이다.
[1,2,3,4,5].publisher
.sink(
receiveCompletion: { print($0) },
receiveValue: { print($0) }
)
.store(in: &cancelBag)
/*prints
1
2
3
4
5
finished
*/
[1,2,3,4,5].publisher
.collect()
.sink(
receiveCompletion: { print($0) },
receiveValue: { print($0) }
)
.store(in: &cancelBag)
/*prints
[1, 2, 3, 4, 5]
finished
*/
collect에 count parameter를 줄 경우 count만큼 묶어서 방출한다.
[1,2,3,4,5].publisher
.collect(2)
.sink(
receiveCompletion: { print($0) },
receiveValue: { print($0) }
)
.store(in: &cancelBag)
/*prints
[1, 2]
[3, 4]
[5]
finished
*/
map
operator는 Swift의 기본 map과 같은 방식으로 동작한다. upstream에서 내려온 값을 map 을 통해 re-publish한다.
다음 예시에서 123, 4, 56이라는 값을 map 안에서 formatter를 통해 한글로 바꿔주는 역할을 한다.
let formatter = NumberFormatter()
formatter.numberStyle = .spellOut
[123, 4, 56].publisher
.map { formatter.string(for: NSNumber(integerLiteral: $0)) ?? "" }
.sink(receiveValue: { print($0) })
.store(in: &cancelBag)
/*prints
백이십삼
사
오십육
*/
map
operator는 keyPath를 통한 접근 또한 가능하다.
struct Coordinate {
var x: Int = 0
var y: Int = 0
}
let coordination: Coordinate = .init(x: 2, y: 4)
let coordination2: Coordinate = .init(x: 5, y: 1)
let publisher = PassthroughSubject<Coordinate, Never>()
publisher
.map(\.x, \.y)
.sink { completion in
print(completion)
} receiveValue: { x, y in
print(x)
print(y)
}
.store(in: &cancelBag)
publisher.send(coordination)
publisher.send(coordination2)
publisher.send(completion: .finished)
/*prints
2
4
5
1
finished
*/
tryMap
operator를 사용하면 throwing closure를 받을 수 있고, error가 throw되면 바로 error를 downstream으로 emit한다.
Just("Directory name that does not exist")
.tryMap { try FileManager.default.contentsOfDirectory(atPath: $0) }
.sink(receiveCompletion: { print($0) },
receiveValue: { print($0) })
.store(in: &cancelBag)
/*prints
failure(.."The folder “Directory name that does not exist”
doesn't exist."..) // some error
*/
flatMap
operator는 upstream publisher에서 온 value를 down stream으로 flatten하는 역할을 한다.
[72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33]
.publisher
.collect()
.flatMap(decode)
.sink(receiveValue: { print($0) })
.store(in: &cancelBag)
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()
}
/*prints
Hello, World!
*/
nil이 떨어질 경우 parameter 값으로 replace한다.
주의할 점은 replaceNil(with:)는 Optional한 값을 unwrapping하는 operator가 아니라는 점이다.
["A", nil, "C"].publisher
.eraseToAnyPublisher()
.replaceNil(with: "B")
.sink(receiveValue: { print($0) })
.store(in: &cancelBag)
/*prints
A
B
C
*/
empty를 대체한다.
let empty = Empty<Int, Never>()
empty
.replaceEmpty(with: 1)
.sink(receiveCompletion: { print($0) },
receiveValue: { print($0) })
.store(in: &subscriptions)
/*prints
1
finished
*/
첫 파라미터로는 initialResult를 받고, 두번째 파라미터는 nextPartialResult closure를 받는다.
따라서 다음 코드는 0에서 시작해 nextValue를 하나씩 더해가는 operator의 역할을 한다.
let range = (0...5)
cancellable = range.publisher
.scan(0) { return $0 + $1 }
.sink { print ("\($0)", terminator: " ") }
// Prints: "0 1 3 6 10 15 ".