felix-iOS

[Combine] 유용한 Filtering Operator(2) 본문

Combine

[Combine] 유용한 Filtering Operator(2)

felix-mr 2021. 6. 24. 19:22

 

안녕하세요 🙋‍♂️ 

 

유용한 Filtering Operator 두번째 게시글에서는 prefix와 output operator에 대해 알아보려고 합니다 :)

저번 게시글처럼 공식문서와 예제를 통해 확인해보겠습니다!

 

prefix / prefix(untilOutputFrom:) / prefix(while:)

func prefix(_ maxLength: Int) -> Publishers.Output<Self>

지정된 최대 개수까지 요소를 Republish 합니다.

먼저 기본적인 prefix(_:) 입니다. 인자로 받는 maxLength 까지 요소를 Republish한다고 합니다. 현재 소개하고 있는 것은 Operator이기 때문에 당연히 Republish라는 단어가 맞겠죠? Upstream에서 받아서 다시 publish 해주는 것이기 때문이죠! ㅎㅎ

 

예제를 통해 알아보겠습니다!

 

let numbers = [1,2, 3, 4, 5]

cancellable = numbers.publisher
            .prefix(3)
            .sink {
            	print("prefix: \($0)")
            }

// 결과값
// prefix: 1
// prefix: 2
// prefix: 3

 

1부터 10까지 있는 Int 배열을 publish 하기 시작합니다. 그죠?

근데 prefix Operator를 만나게 됩니다. prefix는 3개까지 maxLength로 지정되어 있고! 그럼 3개까지만 Republish를 하겠죠?

결과값을 보면 1, 2, 3까지 정확하게 3개만 Republish 되는 모습을 볼 수 있습니다! 쉽다! 그죠?

 

 

다음은 prefix(untilOutputFrom:) 입니다! 바로 공식문서를 확인해봅시다!

func prefix<P>(untilOutputFrom publisher: P) -> Publishers.PrefixUntilOutput<Self, P> where P : Publisher

다른 publisher가 값을 방출할 때까지 값을 Republish 합니다.

 

띠용?! 이번에는 인자로 어나더 publisher를 받습니다! 설명을 보면 인자로 받는 다른 publisher의 값 방출에 영향을 받는 것으로 보여집니다.

 

예제를 통해 알아봅시다!

 

아앗! 공식문서에 코드가 없네요.. ㅎㅎ;; 직접 만들어봅시다유

subject1
  .prefix(untilOutputFrom: subject2)
  .sink {
    print("prefix: \($0)")
  }
  .store(in: &cancellables)

subject1.send("값 방출")
subject1.send("값 방출")
subject2.send("그만!")
subject1.send("값 방출 안됨")

// 결과값
// prefix: 값 방출
// prefix: 값 방출

 

공식문서의 설명대로 subject1이 계속 값을 방출하다가 untilOutputFrom으로 등록된 subject2가 값을 방출하는 순간부터 아무리 send를 해도 subject1은 더 이상 값을 방출하지 않습니다.

 

1편에서 작성한 drop(untilOutputFrom:)과 정반대의 역할을 하고 있네요 :)

 

 

이제 prefix 시리즈의 마지막인 prefix(while:) 입니다.

func prefix(while predicate: @escaping (Self.Output) -> Bool) -> Publishers.PrefixWhile<Self>

predicate closure가 계속 publish돼야 함을 나타내는 동안 요소를 Republish 합니다.

직역을 하니까 뭔가 이상하지만, 하고 싶은 말은 closure가 false를 반환하기 전까지 Republish한다는 뜻입니다!

헷갈리니까 바로 예제를 살펴봅시다.

 

let numbers = [1, 2, 3, 4, 5, 6, 7, 1]

let cancellable = numbers.publisher
    .prefix { $0 < 5 }
    .sink { print($0) }

//결과값
// 1
// 2
// 3
// 4

 

자, predicate closure는 "5보다 작은가"에 대한 Bool 값을 반환합니다. false를 반환할 때까지 요소를 Republish한다고 했으니까

 

1 ~ 4까지의 값을 Republish합니다. 마지막 요소 1은 5보다 작지만 이미 prefix에 의해 끝이 나버렸기 때문에 더 이상 값이 방출되지 않습니다! 

 

output(at:)/output(in:)

이제 output이란 필터 친구를 만나봅시다. 먼저 output(at:)입니다.

func output(at index: Int) -> Publishers.Output<Self>

특정 요소를 게시합니다. 이 요소는 Publish된 요소 순서로 인덱스로 표시됩니다.

 

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

let cancellable = numbers.publish
    .output(at: 5)
    .sink { print($0) }
    

//결과값
// 6

자 output의 인자값으로 5를 넣어줬습니다. 인덱스로 표시된다고 했으니 0부터 시작을 하겠죠?

그럼 0 ~ 4번까지는 publish 되지 않고 5번 인덱스의 값인 6만 출력되는 것을 확인할 수 있습니다!

 

다음은 비슷하게 생긴 ouput(in:) 입니다.

func output<R>(in range: R) -> Publishers.Output<Self> where R : RangeExpression, R.Bound == Int

범위에 따라 지정된 요소를 Publish된 요소 순서로 게시합니다.

이번에는 단일 값이 아닌 range값을 인자로 받습니다.

 

바로 예제로 확인해보겠습니다!

 

let numbers = [1, 1, 2, 2, 2, 3, 4, 5, 6]

let cancellable = numbers.publisher
    .output(in: (3...5))
    .sink { print($0) }
    
//결과값
// 2
// 2
// 3

3 ~ 5 번째 인덱스의 값만 Republish 하기 때문에 2, 2, 3이 출력되는 것을 확인할 수 있습니다.

 

여기서 꿀팁 아닌 꿀팁을 드리자면,

 

let numbers = [1, 1, 2, 2, 2, 3, 4, 5, 6]

numbers.publisher
    .output(in: (3...))
    .sink { print("\($0)", terminator: " ") }
    
//결과값
// 2
// 2
// 3
// 4
// 5
// 6

output의 인자를 range의 형태로 받기 때문에

 

partialRangeFrom 또는 partialRangeUpTo의 형태로도 받을 수 있다는 점!!

 

사용해보다가 이렇게도 사용할 수 있겠구나 싶어서 적어봤습니다!

 

유용한 FilterOperator에 대한 2편 글이 마무리가 되었네요. ㅎㅎ

혹시 틀린 부분이 있다면 지적 부탁드립니다 :) 감사합니다!

 

 

 

Reference

https://developer.apple.com/documentation/combine/fail/prefix(_:) 

 

Apple Developer Documentation

 

developer.apple.com

https://developer.apple.com/documentation/combine/fail/output(at:) 

 

Apple Developer Documentation

 

developer.apple.com

 

Comments