idghst.dev
article thumbnail
Published 2023. 3. 8. 09:00
[ Swift ] Combine - Operator Swift
728x90
반응형

Scheduler

  • on : 스레드 를 통해서 연산 스레드와 결과 수신 스레드를 구분하여 효율을 올릴 수 있습니다
  • 아래 코드 역시 map 을 통해 연산하는 스레드는 queue 라는 서브 스레드를 통해 진행하고
    receive 를 통해 결과는 메인 스레드에서 처리하는 것을 알 수 있습니다
import Foundation
import Combine

let arrPublisher = [1, 2, 3].publisher

let queue = DispatchQueue(label: "custom")

let subscription = arrPublisher
    .subscribe(on: queue)
    .map { value -> Int in
        print("transform: \(value), thread: \(Thread.current)")
        return value
    }
    .receive(on: DispatchQueue.main)
    .sink { value in
        print("Received Value : \(value), thread : \(Thread.current)")
    }

/*
 transform: 1, thread: <NSThread: 0x6000005195c0>{number = 4, name = (null)}
 transform: 2, thread: <NSThread: 0x6000005195c0>{number = 4, name = (null)}
 transform: 3, thread: <NSThread: 0x6000005195c0>{number = 4, name = (null)}
 Received Value : 1, thread : <_NSMainThread: 0x600000510180>{number = 1, name = main}
 Received Value : 2, thread : <_NSMainThread: 0x600000510180>{number = 1, name = main}
 Received Value : 3, thread : <_NSMainThread: 0x600000510180>{number = 1, name = main}
*/

 

map

  • map에서 작성한 수식을 통해 전달받은 값을 계산합니다
  • Subsciption 코드에는 계산 결과 값이 사용됩니다
import Foundation
import Combine

let numPublisher = PassthroughSubject<Int, Never>()
let mapSubscription = numPublisher
    .map{ value in
        value * 2
    }
    .sink { value in
        print("transformed value : \(value)")
    }

numPublisher.send(10)
numPublisher.send(20)
numPublisher.send(30)
mapSubscription.cancel()

/*
 transformed value : 20
 transformed value : 40
 transformed value : 60
*/

 

filter

  • filter에서 작성된 수식 결과가 true인 값만 사용합니다
  • 아래 코드와 같이 contains("a") 의 결과가 true인 abc, Jason, Jack 이라는 값만 사용되어 출력됩니다
import Foundation
import Combine

let strPublisher = PassthroughSubject<String, Never>()
let filterSubscription = strPublisher
    .filter{ value in
        value.contains("a")
    }
    .sink { value in
        print("filtered value : \(value)")
    }

strPublisher.send("abc")
strPublisher.send("Jason")
strPublisher.send("Joon")
strPublisher.send("Jenny")
strPublisher.send("Jack")
filterSubscription.cancel()

/*
 filtered value : abc
 filtered value : Jason
 filtered value : Jack
*/

 

combineLastest

  • 2개의 데이터를 모두 받았을 때 처리하는 함수라고 생각하면 됩니다
  • 각 구독을 통해 받은 값을 변수에 따로 저장하는 것이 아니라
  • 2개의 값을 모두 수신한 경우 (nil 이 아닌 경우) 처리 할 수 있도록 합니다
  • 아래 코드에서 처럼 "a" 만 받았을 때는 str 에는 값이 있지만 num 에는 값이 없어 print 가 실행되지 않지만
    1 이 들어온 이후에는 str, num 두 곳에 모두 값이 존재하는 상태이기 때문에 print 가 항상 실행됩니다
import Foundation
import Combine

let strPublisher = PassthroughSubject<String, Never>()
let numPublisher = PassthroughSubject<Int, Never>()

/*
strPublisher.combineLatest(numPublisher).sink { (str, num) in
    print("received : \(str), \(num)")
}
*/
Publishers.CombineLatest(strPublisher, numPublisher).sink { (str, num) in
    print("received : \(str), \(num)")
}

strPublisher.send("a")
numPublisher.send(1)
strPublisher.send("b")
numPublisher.send(2)
strPublisher.send("c")
numPublisher.send(3)

/*
 received : a, 1
 received : b, 1
 received : b, 2
 received : c, 2
 received : c, 3
*/
  • combineLastest 의 응용으로 아이디와 비밀번호에 대한 검증에 대한 예시 코드입니다
  • map 을 통해 받은 아이디와 비밀번호에 대한 검증 결과를 연산하여 그 결과를 기반으로 처리합니다
import Foundation
import Combine

let usernamePublisher = PassthroughSubject<String, Never>()
let passwordPublisher = PassthroughSubject<String, Never>()

let validatedCrendetialsSubscription = usernamePublisher.combineLatest(passwordPublisher)
    .map { (username, password) in
        return !username.isEmpty && !password.isEmpty && password.count > 12
    }
    .sink { valid in
        print(valid ? "success" : "failure")
    }

usernamePublisher.send("leehees")
passwordPublisher.send("weakPassword") // failure
passwordPublisher.send("veryStrongPassword") // success
/*
 failure
 success
*/

 

merge

  • 두 퍼블리셔의 타입이 같은 경우 (반드시 같아야 합니다)
  • 병합하여 한 번에 받을 수 있다
import Foundation
import Combine

let publisher1 = [1, 2, 3, 4, 5].publisher
let publisher2 = [100, 200, 300].publisher

// combineLastest 와 달리
// 두 퍼블리셔의 타입은 같아야 함
/*
let mergePublisherSubscription = publisher1.merge(with: publisher2)
    .sink { value in
        print("Merge : received value = \(value)")
    }
*/
Publishers.Merge(publisher1, publisher2).sink { value in
    print("Merge : received value = \(value)")
}

/*
 Merge : received value = 1
 Merge : received value = 2
 Merge : received value = 3
 Merge : received value = 4
 Merge : received value = 5
 Merge : received value = 100
 Merge : received value = 200
 Merge : received value = 300
*/

 

removeDuplicates

  • Publisher로 전달받는 값에 대해 중복제거합니다
import Foundation
import Combine

var subscriptions = Set<AnyCancellable>()

let words = "hey hey there! Mr Mr ?"
    .components(separatedBy: " ")
    .publisher

words
    .removeDuplicates() // 퍼블리셔 중복 제거
    .sink { completion in
        print("Completed with: \(completion)")
    } receiveValue: { value in
        print(value)
    }
    .store(in: &subscriptions)

/*
 hey
 there!
 Mr
 ?
 Completed with: finished
*/

 

compactMap

  • 1차원 배열에서 nil 값을 제외 해줍니다
  • 값을 그대로 출력하여 원래 nil 이 아닌것만 추출 가능하고
    Float(value) 등으로 연산 결과가 nil 이 아닌 것만 추출하는 것도 가능합니다
import Foundation
import Combine

var subscriptions = Set<AnyCancellable>()

let strings = ["a", "1.24", "3", "def", "45", "0.23"].publisher

strings
    .compactMap { value in
        Float(value)
    } // nil 값 제외 출력
    .sink { completion in
        print("Completed with: \(completion)")
    } receiveValue: { value in
        print(value)
    }
    .store(in: &subscriptions)

/*
 1.24
 3.0
 45.0
 0.23
 Completed with: finished
*/

 

ignoreOutput

  • output을 무시하고 completion만 받도록 합니다
import Foundation
import Combine

var subscriptions = Set<AnyCancellable>()

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

numbers
    .ignoreOutput() // 데이터 출력은 무시 (성공/실패 유무만 출력)
    .sink { completion in
        print("Completed with: \(completion)")
    } receiveValue: { value in
        print(value)
    }
    .store(in: &subscriptions)

/*
 Completed with: finished
*/

 

prefix

  • Publisher의 값 전달 개수를 제한합니다
import Foundation
import Combine

var subscriptions = Set<AnyCancellable>()

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

tens
    .prefix(2) // 퍼블리셔 전달 데이터 개수 설정 (2로 설정한 경우 데이터가 더 있어도 2개만 보내고 끝냄)
    .sink { completion in
        print("Completed with: \(completion)")
    } receiveValue: { value in
        print(value)
    }
    .store(in: &subscriptions)

/*
 1
 2
 Completed with: finished
*/
728x90
반응형

'Swift' 카테고리의 다른 글

[ Swift ] Network - 개념  (0) 2023.03.15
[ Swift ] Combine - 실습  (0) 2023.03.13
[ Swift ] Combine - Subscriber  (0) 2023.03.06
[ Swift ] Combine - Publisher  (0) 2023.03.03
[ Swift ] Combine - 개념  (0) 2023.03.01
profile

idghst.dev

@idghst.dev

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!