본문 바로가기

iOS/Combine

[Combine] map(_:), tryMap(_:)

1. map(_:)

1)  분류

Combine Framework    Publishers
Protocol    Publisher
Operator    Mapping Elements

2)  타입

func map<T>(_ transform: @escaping (Self.Output) -> T) -> Publishers.Map<Self, T>

 

Upstream Publisher(상위 스트림에서 발행된 것)를 <T>(변환하고자 하는 타입)로 변환하고, 해당 작업은 비동기로 처리되므로 처리가 완료되면 콜백 함수를 실행(@escaping) 한다.
Publishers.Map<Self, T>을 return 한다.

3) 설명

[상황]

놀이동산 티켓팅 프로그램을 개발한다고 가정해보자.

친구 다섯명이 자신들의 이름과 나이를 입력하고 자유이용권을 선택 후 티켓 발행을 눌렀다.

다섯명에 대한 티켓이 발행되어야 한다.

 

[코드]

코드 작성 전 도식화로 먼저 이해해보면,

입력된 유저 정보(Upstream Publisher)를 <T:티켓>으로 변환하고 완료시 콜백 함수로 실행 시키면 유저정보가 담긴 티켓이 발행될 것이다.

 

설계한 내용을 코드로 작성해보면 아래와 같다.

 func map(){
        cancellable = ResponseResults.Friends.publisher
            .map { user in
                let ticket = Ticket(name: user.name, age: user.age, type: "자유이용권")
                return ticket
            }
            .sink { ticket in
                print("\(ticket.name)님, 환영합니다. \(ticket.type)이 발행되었습니다.")
            }
    }

 

map() 실행영상

[응용]

배열 데이터를 발행하여 순차적으로 데이터를 변환하고 싶을 때 사용하는것도 좋은 활용 방식이다.

위의 예제의 경우 Friends 라는 User 구조체들의 배열 Publisher 로 설정하였고, User 가 순차적으로 발행되면서 변환이 이뤄졌다.

 

[정리]

map, tryMap, mapError 는 Swift standard library의 map(:_) 과 유사한 작동을 한다고 생각하면 된다.

쉽게 정리해보면 어떠한 요소를 다른 요소로 변환(transform)시키는 함수(function) 이다.

함수들의 이름으로 (map), try(Map), (map)Error 등 어떤 기능을 할지 생각해보면 좋을 것 같다.

4) 참고

Apple Documentaion

GitHub 예시 코드

 


2. tryMap(_:)

1)  분류

Combine Framework    Publishers
Protocol    Publisher
Operator    Mapping Elements

2)  타입

func tryMap<T>(_ transform: @escaping (Self.Output) throws -> T) -> Publishers.tryMap<Self, T>

Upstream Publisher(상위 스트림에서 발행된 것)를 <T>(변환하고자 하는 타입)로 변환하고, 해당 작업은 비동기로 처리되므로 처리가 완료되면 콜백 함수를 실행(@escaping) 한다.

조건에 맞지 않는 경우 동작을 멈추고 에러를 반환 할 수 있다.

Publishers.tryMap<Self, T>을 return 한다.

3) 설명

[상황]

26세 부터 탈 수 있는 놀이기구여서 티켓 검사를 하는 프로그램을 만든다고 가정해보자.

위의 티켓을 발급 받은 친구들 중 [cascade]는 빠른년생이어서 현재 25살이다.

표 검사중 [cascade]의 티켓은 조건과 맞지 않으므로 Reject Error 를 반환하려고 한다.

 

[코드]

func tryMap(){
    cancellable = tickets.publisher
        .tryMap { try self.validateTicket(ticket: $0) }
        .sink(
            receiveCompletion: { print ("completion: \($0)") },
            receiveValue: { print ("[\($0.name)]님은 입장가능하십니다.") }
         )
}
    
    
private func validateTicket(ticket:Ticket) throws -> Ticket {
    guard ticket.age > 25 else {
        throw RejectError()
    }

    return ticket
}
tryMap() 실행 영상

[정리]

조건에 따라 변환을 불가능하게 하고, 유저에게 알림을 주어야 한다면 매우 유용한 함수가 될 것이다.

고려해야할 사항이 있다면 중간에 조건이 맞지 않는 경우 변환이 중지되고 에러를 반환하는 것이다.

예를 들어 위의 상황으로 정리해보면,

 

총 5명의 유저중 3번째 유저의 나이가 25살 이었다고 가정해보자.

1번 유저 통과 → 2번 유저 통과 → 3번 유저 실패 → 에러

위와 같은 흐름으로 진행되게 된다.

4) 참고

Apple Documentaion

GitHub 예시 코드