티스토리 뷰

반응형

안녕하세요 Pingu입니다.🐧

 

지난 글에서는 Combine의 Operator 중 Applying Mathematical Operations on Elements로 분류된 Operator들을 알아봤었습니다. Upstream에서 받은 값은 간단한 수학 연산으로 처리해서 만든 새로운 값을 Downstream으로 보내는 역할을 했습니다.

 

이번 글에서는 이어서 Applying Matching Criteria to Elements로 분류된 Operator에 대해서 알아보도록 하겠습니다.

Applying Matching Criteria to Elements

이번에 공부할 녀석들을 분류해둔 이름을 보면 값들이 어떤 기준에 맞는지 확인하는 역할을 할 거 같네요. 그럼 여기에 분류된 Publisher에는 어떤 것들이 있는지부터 알아보겠습니다.

  • Contains
  • ContainsWhere
  • TryContainsWhere
  • AllStatisfy
  • TryAllSatisfy

그리고 이 Publisher들을 활용해서 만든 Operator는 아래와 같습니다.

  • contains(_:)
  • contains(where:)
  • tryContains(where:)
  • allSatisfy(_:)
  • tryAllSatisfy(_:)

그럼 바로 어떤 역할들을 하는 것들인지 하나씩 알아보도록 하겠습니다.

Contains

가장 먼저 알아볼 Publisher는 Contains입니다.

정의를 보면 Upstream에서 특정한 값을 받으면 Downstream으로 Bool 값을 내려보내는 Publisher라고 하네요. 이걸 활용해서 만든  Operator를 봐야 어떤 Bool 값을 내려보내는지 알 수 있겠네요.

contains(_:)

Contains Publisher를 활용해서 만든 Operator는 contains(_:)입니다.

정의를 보면 매개변수로 받은 값과 동일한 값을 Upstream에서 받으면 Bool 값을 내려보낸다고 합니다.

물론 같은 값인지 확인을 해야 하니까 Output은 Equatable 프로토콜을 준수해야 할 거 같네요.

 

바로 사용해볼게요.

let intPublisher = [1, 2, 3, 4, 5].publisher

intPublisher
    .contains(3)
    .sink(receiveValue: { print($0) })
    
// contains(_:) 예제 코드
true

 

3이랑 같은 값이 있기 때문에 Bool 값으로 true를 내려보낸 것을 볼 수 있습니다.

 

여기서 헷갈릴 수 있는 부분은 아래와 같이 사용했을 때입니다.

let intPublisher = [3, 3, 3].publisher

intPublisher
    .contains(3)
    .sink(receiveValue: { print($0) })
    
// contains(_:) 예제 코드 결과
true

위와 같이 contains(_:)에 3을 주고 [3, 3, 3]의 값을 publish 하면 결과는 어떻게 나올까요?

결과는 아까와 같습니다. 즉 그냥 한 번이라도 같은 값을 받으면 true를 내려보내는 거죠.

즉 일치하는 값을 받는 순간 그냥 finish 되므로 위의 예의 경우 publisher는 첫 번째 3만 전달받고 바로 finish 됩니다.

 

그럼 없다면? 어떨까요

let intPublisher = [1, 1, 1].publisher

intPublisher
    .contains(3)
    .sink(receiveValue: { print($0) })
    
// contains(_:) 예제 코드 결과
false

없으면 위와 같이 false를 내려보내는 것을 볼 수 있습니다.

 

ContainsWhere

다음으로 ContainsWhere라는 Publisher를 알아보겠습니다.

정의를 보면 Upstream에서 받은 값이 클로저의 로직을 만족시키면 Bool값을 내려보낸다고 되어있네요.

뭔가 범위를 지정해서 처리하거나 하는 것이 가능해 보이네요.

contains(where:)

ContainsWhere Publisher를 활용해서 만든 Operator는 contains(where:)입니다.

Upstream에서 받은 값이 매개변수로 전달한 클로저를 만족시키면 Bool 값을 내려보내는 역할을 한다고 합니다.

 

바로 사용해보면 아래와 같이 사용할 수 있겠네요.

let intPublisher = [1, 2, 3, 4, 5].publisher

intPublisher
    .contains(where: { $0 % 2 == 0 })
    .sink(receiveValue: { print($0) })

// contains(where:) 예제 코드 결과
true

 

간단하죠? 받은 값 중에 클로저를 만족시키면 true를 하나도 만족시키지 못하면 False를 downstream으로 보내는 Operator입니다.

TryContainsWhere

다음으로 알아볼 TryContainsWhere은 방금 알아본 ContainsWhere에서 에러를 던질 수 있다는 차이만 있는 Publisher입니다.

tryContains(where:)

그리고 TryContainsWhere를 활용해서 만든 tryContains(where:) Operator도 아까 알아본 contains(where:)에서 에러만 던진다는 차이점만 존재합니다.

 

바로 사용해볼게요.

struct MoreThenTenError: Error { }

let intPublisher = [1, 3, 5, 7, 9, 11].publisher

intPublisher
    .tryContains { value in
        guard value < 10 else {
            throw MoreThenTenError()
        }
        return value < 10
    }
    .sink(receiveCompletion: { print("completion: \($0)") },
          receiveValue: { print("value: \($0)") })
          
// tryContains(where:) 예제 코드 결과
value: true
completion: finished

 

10 이상의 값을 받으면 에러를 발생하도록 만들었습니다.

엥? 근데 분명 11이라는 값도 내려보낼 텐데 에러가 발생하지 않았습니다.

이유는 contains(_:), contains(where:), tryContains(where:) 모두 만족하는 값을 받으면 바로 true를 내려보내고 Publisher는 finish 됩니다.

즉 방금 예제에서는 1을 받자마자 true를 내려보내고 finish 된 것이죠.

 

에러를 발생시키고 싶다면 아래와 같이 조건을 만족시키는 값을 내려보내기 전에 만족시키지 않는 값을 내려보내 줘야 합니다.

struct MoreThenTenError: Error { }

let intPublisher = [11, 9, 7, 5, 3, 1].publisher

intPublisher
    .tryContains { value in
        guard value < 10 else {
            throw MoreThenTenError()
        }
        return value < 10
    }
    .sink(receiveCompletion: { print("completion: \($0)") },
          receiveValue: { print("value: \($0)") })

// tryContains(where:) 예제 코드 결과
completion: failure(__lldb_expr_21.(unknown context at $10f2b109c).(unknown context at $10f2b10f0).(unknown context at $10f2b10f8).MoreThenTenError())

이렇게 10보다 큰 11을 먼저 내려보냈더니 에러가 발생해서 failure가 발생하는 것을 볼 수 있습니다.

AllSatisfy

다음으로 알아볼 Publisher는 AllSatisfy입니다.

이름만 봐도 Upstream에서 받은 모든 값이 원하는 값과 일치하는지를 볼 거 같이 생겼네요.

allSatisfy(_:)

allSatisfy(_:)는 Upstream에서 받은 모든 값이 클로저를 만족시키면 true를, 하나라도 만족시키지 못한다면 false를 downstream에 내려보냅니다.

 

간단하게 모두 만족시키는 예를 만들어보면 아래와 같습니다.

let intPublisher = [2, 2, 2, 2].publisher

intPublisher
    .allSatisfy { $0 == 2 }
    .sink(receiveCompletion: { print("completion: \($0)") },
          receiveValue: { print("value: \($0)")})
          
// allSatisfy(_:) 예제 코드 결과
value: true
completion: finished

 

지금까지 알아본 contains라는 이름을 가진 Operator들은 모두 만족하는 값을 하나라도 받으면 finish 했지만, 이번에는 그 반대로 만족시키지 못하는 값을 하나라도 받으면 finish 됩니다. 물론 모든 값이 조건을 만족하는지 확인하려면 모든 값을 확인해야 하니 당연한 말입니다.😄

 

그럼 모든 값이 만족시키지는 못하는 예를 한 번 볼게요.

let intPublisher = [2, 2, 3, 2].publisher

intPublisher
    .allSatisfy {
        print("Upstream에게 받은 값: \($0)")
        return $0 == 2
    }
    .sink(receiveCompletion: { print("completion: \($0)") },
          receiveValue: { print("value: \($0)")})

// allSatisfy(_:) 예제 코드 결과
Upstream에게 받은 값: 2
Upstream에게 받은 값: 2
Upstream에게 받은 값: 3
value: false
completion: finished

예상대로 만족시키지 못하는 값을 받으면 바로 에러를 발생시키고 finish 되는 것을 볼 수 있어요.

TryAllSatisfy

그럼 이번 글의 마지막 Publisher인 TryAllSatisfy를 알아보겠습니다.

뭐 역시나 AllSatisfy랑 같으면서 에러만 던질 수 있는 녀석입니다.

tryAllSatisfy(_:)

tryAllSatisfy(_:)도 allSatisfy(_:)에서 에러만 던질 수 있는 차이밖에 없네요.

 

바로 사용해볼게요.

struct MoreThenTenError: Error { }

let intPublisher = [7, 9, 11, 13].publisher

intPublisher
    .tryAllSatisfy { value in
        print("Upstream에게 받은 값: \(value)")
        guard value < 10 else {
            throw MoreThenTenError()
        }
        return value < 10
    }
    .sink(receiveCompletion: { print("completion: \($0)") },
          receiveValue: { print("value: \($0)") })
          
// tryAllSatisfy(_:) 예제 코드 결과
Upstream에게 받은 값: 7
Upstream에게 받은 값: 9
Upstream에게 받은 값: 11
completion: failure(__lldb_expr_31.(unknown context at $10d2e86dc).(unknown context at $10d2e875c).(unknown context at $10d2e8764).MoreThenTenError())

위와 같이 사용할 수 있습니다.

조건에 맞지 않는 값을 받자마자 에러를 발생시키고 Publisher는 failure로 처리됩니다.

 

이렇게 Combine의 Publisher와 Operator 중에서 Applying Matching Criteria to Elements로 분류된 것들에 대해 알아봤습니다.

 

다음 글에서는 Applying Sequence Operations to Elements로 분류된 녀석들을 알아볼 예정인데... 엄청 많네요.😔

 

전체 코드는 여기에서 볼 수 있습니다.

 

감사합니다!

반응형
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함