[회고] 신입 iOS 개발자가 되기까지 feat. 카카오 자세히보기

🍎 Apple/Combine & Rx

[Combine] Operator (1): Transforming Operator

inu 2022. 1. 10. 15:17
반응형

Transforming Operator

Combine에서 Operator는 Publisher를 가공해 다른 형태나 구성으로 변경해주는 역할을 한다. Upstream으로부터 전달받은 데이터를 가공하여 downstream으로 전달하는 것이다.

 

그 중 데이터의 형태(타입)를 바꿔주는 Operator(Transforming Operator) 정리해볼 것이다.


collect

Example

["A","B","C","D","E"].publisher.collect(3).sink {
    print($0)
}

Output

["A", "B", "C"]
["D", "E"]

  • Input 데이터를 모아서 Array로 한번에 반환하도록 처리한다.
  • 파라미터로 숫자를 넣어서 몇개를 하나로 묶을지도 결정할 수 있다. (default는 모든 데이터를 한번에 묶는 것이다. 이 경우 complete 될 때까지 무한정 array를 채울 수 있기 때문에 유의해야 한다.)
  • 파라미터에 주어진 숫자를 채우지못하고 남는 데이터는 그것들끼리만 반환된다.

map

Example 1

let formatter = NumberFormatter()
formatter.numberStyle = .spellOut

[123,45,67].publisher.map {
    formatter.string(from: NSNumber(integerLiteral: $0)) ?? ""
}.sink {
    print($0)
}

Output 1

one hundred twenty-three
forty-five
sixty-seven

  • 기존의 map 함수와 비슷한 역할을 한다. 특정 타입의 Array를 전혀 다른 형태의 Array로 변환할 수 있다.

Example 2

struct Point {
    let x: Int
    let y: Int
}

let publisher = PassthroughSubject<Point, Never>()

publisher.map(\.x, \.y).sink { x, y in
    print("x is \(x) and y is \(y)")
}

publisher.send(Point(x: 2, y: 10))

Output 2

x is 2 and y is 10

  • map에서 KeyPath를 사용해 특정 value를 바로 가져올 수도 있다
  • 3개까지만 가능하다.

flatMap

Example

struct School {
    let name: String
    let noOfStudents: CurrentValueSubject<Int, Never>

    init(name: String, noOfStudents: Int) {
        self.name = name
        self.noOfStudents = CurrentValueSubject(noOfStudents)
    }
}

let citySchool = School(name: "Fountain Head School", noOfStudents: 100)

let school = CurrentValueSubject<School,Never>(citySchool)

school
    .flatMap {
        $0.noOfStudents
    }
    .sink {
    print($0)
}

let townSchool = School(name: "Town Head School", noOfStudents: 45)

school.value = townSchool

citySchool.noOfStudents.value += 1
townSchool.noOfStudents.value += 10

Output

100
45
101
55

  • 여러개의 publisher upstream을 하나의 단일 downstream으로 변환한다.
  • 다차원 구조의 publisher가 존재할 경우 이를 하나의 단일 publisher 형태로 변환하기 때문에 내부에 존재하는 publisher에 대한 발행도 받아올 수 있다. (예시참고)
  • downstream의 단일 publisher를 업데이트할 때 기존의 publisher를 버퍼링한다.
  • cf. maxPublishers 라는 파라미터를 제공하는데, 이는 버퍼링하는 최대 publisher 개수를 설정한다.

replaceNil

Example

["A","B",nil,"C"].publisher.replaceNil(with: "*")
    .sink {
        print($0)
}

Output

Optional("A")
Optional("B")
Optional("*")
Optional("C")

  • nil을 특정값으로 변환하여 반환하도록 변경한다.

replaceEmpty

Example

let empty = Empty<Int, Never>()

empty
    .replaceEmpty(with: 1)
    .sink(receiveCompletion: { print($0) }, receiveValue: { print($0) })

Output

1
finished

  • upstream에서 value가 emit되지않고 completion이 수행되면 value를 하나 넣어준다.

scan

Example

let publisher = (1...10).publisher

publisher.scan([]) { numbers, value -> [Int] in
    numbers + [value]
}.sink {
    print($0)
}

Output

[1]
[1, 2]
[1, 2, 3]
[1, 2, 3, 4]
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 5, 6]
[1, 2, 3, 4, 5, 6, 7]
[1, 2, 3, 4, 5, 6, 7, 8]
[1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

  • value를 누적해서 원하는 형태로 변환 후 내보낸다.
  • scan operator의 파라미터로 초기값을 넣고, 클로저에서는 이에 대한 처리를 수행한다.
반응형