[SwiftUI Mater] #19 Advanced Combine

Woozoo·2023년 4월 9일
0

[SwiftUI Review]

목록 보기
34/41
post-custom-banner
import SwiftUI
import Combine

class AdvancedCombineDataService {
    @Published var basicPublisher: [String] = []
    
    init() {
        publishFakeData()
    }
    
    private func publishFakeData() {
        DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
            self.basicPublisher = ["one", "two", "three"]
        }
    }
}

class AdvancedCombineBootcampViewModel: ObservableObject {
    
    @Published var data: [String] = []
    let dataService = AdvancedCombineDataService()
    
    var cancellables = Set<AnyCancellable>()
    
    init() {
        addSubscribers()
    }
    
    private func addSubscribers() {
        dataService.$basicPublisher
            .sink { completion in
                switch completion {
                case .finished:
                    break
                case .failure(let error):
                    print("Error: \(error.localizedDescription)")
                    break
                }
            } receiveValue: { [weak self] returnedValue in
                self?.data = returnedValue
            }
            .store(in: &cancellables)
    }
    
}

struct AdvancedCombineBootcamp: View {
    
    @StateObject private var vm = AdvancedCombineBootcampViewModel()
    
    var body: some View {
        ScrollView {
            VStack {
                ForEach(vm.data, id: \.self) {
                    Text($0)
                        .font(.largeTitle)
                        .fontWeight(.black)
                }
            }
        }
    }
}

DataService가 있고 이걸 ViewModel에서 받은 다음에 뷰에서
StateObject로 가지고 있다고 해보자

여기서 콤바인의 좋은 점이 따로 취소 안했으면 계속 살아있다는 거!
구독 티켓을 계속 가지고 있어서 값 변하면 변한 값을 뷰에 뿌려줄 수 있다


이번엔 하나씩 개별적으로 넣어봅시다



Publisher에 두가지 종류가 있는데 지금 어떤 값 방출하는 publihser는 많이 봤을 거임


요 두개는 같은 거임!

CurrentValueSubject는 보낼 값을 내가 지정한다고 생각하면 됨!
Published로 래핑한 프로퍼티는 알아서 보내졌는데


이거는 내가 send로 보내줘야함

$ 도 안붙여줘도 되고!

그리구 보통은 api콜로 받아올테니까

요렇게 Never가 아니라 Error로 작성해주면 되겠죠?

CurrentValueSubject랑 또 다른 게 있는데
PassthroughSubject 임!


얘는 안닦똥이랑 똑같음. 밸류를 저장하지는 않음!!
메모리에서 이점이 생기겠죠
보내기만 하니까!!

아래에도 다 passThrough로 바꿔보면 정상적으로 데이터가 넘어오는 걸 확인할 수 있습니다~

이거 String에서 Int로 한번 바꿔볼까요?


map도 해줘야겠죠

다른 opreator들 한번 살펴봅시다


first는 딱 한번 값뱉고 끝내는거!


first(where:)는 조건에 맞는 첫번째 친구를 뱉고 끝내는 거!


요런 것도 있음!

에러가 있으면 에러를 던지고 그 이외에 만족하는 첫번째 값을 내뱉는겨

지금은 3에서 걸려버리니까 값이 안 뱉어지겠죠
error는 나오는 지 한번 확인해봅시다



.last라는 operator도 있는데 이거는 명시적으로 마지막애 뱉은 애가 어떤 건지 얘기해줘야함


그래서 send(completion: .finished)로 어떤 게 마지막이었는지 얘기해줌


.last(where:) 은 끝까지 다 뱉은 뒤에 만족하는 마지막 애를 뱉어냄

tryLast도 있고!
이것도 마찬가지로 throw에서 걸리면 그 뒤로는 값안뱉고 빠져나와짐


.dropFirst()도 있음!
CurrentValueSubject처럼 초기에 가진 값도 뱉는 애한테 쓰면 좋겠죠
.dropFirst(3) 이러면 3번째까지는 버리는거!
.drop(while:)은 while에 오는 조건이 false일 때까지 값을 버림

.tryDrop도 있고!
이것도 다른 try랑 마찬가지로 error가 나오게 되면 completion에 넘겨지게 됨!

.prefix는 카운트 만큼만 뱉어냄

.prefix while은 만족하는 값까지만 뱉고 false가 되면 사라짐 ___

.output은 index에 해당하는 값을 뱉어내고!

이거는 In 범위 안에서 output 뱉어내고




Filter / Reducing Operations


try는 error 던지고 멈춤


compact는 nil이면 제껴버림

.tryCompactMap같은 것도 있고


이거 좋은데 remove가 앞뒤로 겹치는 거 있는지만 체크함


replaceNil은 Nil인 값 바꿔치기해줌


Empty는 [] 같이 빈 어레이 같은 거 바꿔치기해줌

.scan이라는 것도 있음



계속해서 나오는 값 더하는 거!

이거랑 똑같은 표현이다
더 줄일 수 있는데
.scan(0, +) 이것도 같은 표현! ㅋㅋ

reduce하면 한번에 다 합쳐줌 (모든 값이 방출되고 나서)
.reduce(0, +)도 같은 표현!

.collect는 다 모아서 array로 만들어줌!

.collect(3)하면 3개씩 합쳐서 값이 나옴


.allSatisfy는 조건을 다 만족하면 true 아니면 false



debounce는 다 끝나고 나면 보내는 거
cryptoApp에서 했었죠 키보드 칠 때

값 나오는 간격을 보는 겨

.delay는 말 그대로 delay 후에 값이 나옴

이거는 값 나오는 거 사이의 시간을 체크하는 거


throttle은 수도꼭지 잠그는 느낌
latest가 true이면 마지막 값이 나오고 그게 아니면 원래 나왔어야할 값이 순서대로 나옴

.retry는 여러번 시도 하는 거고

timeout은 시간 내에 값 안나오면 퍼블리싱 멈춤


여러개 섞어봅시다

먼저 Publisher하나 더 추가해줘야겠죠

boolPublihser 만들어줬습니다

그리고 x 값에 따라서 true 혹은 false 보내줬어용


그리고 viewModel에서 subscriber달아주는 메소드에서 이제 값이 두개씩 나오게끔 combineLatest로 합쳐주고, compactMap을 써서 내려온 bool이 true일 때만 int값이 스트링으로 변하게 해줬음!!


근데~!! 7788두개씩 나오는 게 보인다!!


여기서 send할 때마다 값이 나오니까 두번 나오는겨


이럴 때 대안은 removeDuplicates 해주면 되겠죠

참 지금의 compactMap을 축약표현으로 쓴다면

요렇게 되겠죠

combineLatest를 쓸 때 주의할 점은
값이 묶인 것들이 다 한번 쯤은 나와야된다는 거

publisher하나 더 묶어봅시다

지금 보면 인덱스에서 특정한 구간만 세번째 퍼블리셔가 센드 하게 해줌

이러면! intPublisher까지 값이 나오기전까지는 다른 값을 가져와지지가 않습니다

merge는 말그대로 합쳐줌


.zip은 값들을 튜플로 합쳐줌


.tryMap으로 error를 내보내고 .catch로 error를 잡은 담에 또 새로운 Publisher를 내보내는 것도 가능함
throw에 걸려서 이전 꺼는 끝났지만 catch에서 다시 내보내는 거죠


만약에 같은 publisher이지만 하나는 스트링으로 map하고 하나는 bool로 맵핑이 필요해서 따로따로 받아서 뷰에 보내고 있었다고 해보자
근데 비효율적이잖음
애초에 하나 퍼블리셔가지고 구성하면 됐을텐데

이럴때 사용하는 게 sharedPublisher임


요렇게 하나의 sharedPublisher로 받아주는겁니다


그리고 .multicast라는 것도 있는데 share랑 같이 종종 쓰임
이거는 일반적인 publisher랑 달리 autoconnect가 되지가 않아서 직접 커넥트를 해줘야함

요런식으로

그리구 subject 파라미터로 multicast할 Publisher를 바로 붙여줄 수 도 있다

요기 선언해놓고 저렇게 .multicast(subject:)로 붙여준겨


https://heckj.github.io/swiftui-notes/

SwiftUI Combine 참고문서

profile
우주형
post-custom-banner

0개의 댓글