Combine) Transforming Operators

Havi·2022년 3월 5일
0

Combine

목록 보기
5/8

collect()

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(_:)

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(_:)

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(maxPublishers:_:)

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!
*/

replaceNil(with:)

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
*/

replaceEmpty(with:)

empty를 대체한다.

let empty = Empty<Int, Never>()
empty
  .replaceEmpty(with: 1)
  .sink(receiveCompletion: { print($0) },
        receiveValue: { print($0) })
  .store(in: &subscriptions)
/*prints
 1
finished
*/

scan(::)

첫 파라미터로는 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 ".
profile
iOS Developer

0개의 댓글