새소식

iOS/SwiftUI

[SwiftUI] subscribe / receive 에 관해서

  • -

[학습 목표]

저번 시간에 main thread 와 global thread를 동시에 사용할시에 main thread가 global thread 안에 제대로 가지 않거나 global thread가 main thread 안에 제대로 들어가지 않는것을 볼 수가 있었다.

이러한 thread의 정보를 한 곳으로 입력받고 출력하는 방법에 대해 알아보자.

[구현 방법]

class DataManagerClass {
    @Published var dataArray = [String]()
    
    static let shared = DataManagerClass()
    private init() {
        getData()
    }
    
    func getData() {
        DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
            self.dataArray.append("A")
        }
    }
}

DataMangerClass를 선언해서 main thread 안에서 5초뒤에 A 라는 데이터를 제공하는 클래스를 만들어주자.

 

class SubscriberViewModel: ObservableObject {
    @Published var dataArray = [String]()
    var cancellables = Set<AnyCancellable>()
    
    init() {
        addSubscriber()
    }
    
    private func addSubscriber() {
        DataManagerClass.shared.$dataArray
            .subscribe(on: DispatchQueue.global(qos: .background))
            .receive(on: DispatchQueue.main)
            .sink { _ in
                
            } receiveValue: { [weak self] returnedData in
                guard let self = self else { return }
                self.dataArray.append(contentsOf: returnedData)
            }
            .store(in: &cancellables)
    }
}

Data를 구독하기 위한 Subscriber 클래스이다.

해당 클래스 내부에 있는 addSubscriber() 함수를 이용해 DataMangerClass에서 제공하는 data를

.subscribe(on: DispatchQueue.global(qos: .background))

를 이용해서 구독을 먼저 한다.

해당 구문을 읽어보면 background에서 실행되는 thread를 구독한다는 것을 알 수가 있다.

.receive(on: DispatchQueue.main)

또한 receive를 통해서 해당 구독한 내용을 main으로 보낼 수가 있다. 즉, background 에서 실행되고 있는 내용이 모두 main으로 보낼 수가 있다.

 

.sink { _ in
                
            } receiveValue: { [weak self] returnedData in
                guard let self = self else { return }
                self.dataArray.append(contentsOf: returnedData)
            }

sink를 이용하면 closure에서 새로운 값이나 종료 이벤트에 대한 처리를 할 수가 있다.

해당 구문에서는 받은 데이터를 바로 밑으로 보내주는 역할을 하고 있다.

만일 sink가 없이 publisher를 사용해서 만든다면

1. 구독자가 게시자를 구독

2. 게시자는 구독을 생성해서 구독자에게 제공

3. 구독자가 값을 요청함

4. 게시자가 값을 보냄

5. 게시자가 완료를 보냄

출처: https://nsios.tistory.com/181 [NamS의 iOS일기:티스토리]

의 작업을 해야하기에 sink를 사용하면 매우 편해진다.

 

.store(in: &cancellables)

해당 구문을 이용하면 저장이 될 때 메모리 누수를 막을 수가 있다. 해당 내용을 쓰지 않으면 생각보다 일어나는 메모리 누수가 많으니 주의하기 바란다.

 

[전체 코드]

import SwiftUI
import Combine

class DataManagerClass {
    @Published var dataArray = [String]()
    
    static let shared = DataManagerClass()
    private init() {
        getData()
    }
    
    func getData() {
        DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
            self.dataArray.append("A")
        }
    }
}

class SubscriberViewModel: ObservableObject {
    @Published var dataArray = [String]()
    var cancellables = Set<AnyCancellable>()
    
    init() {
        addSubscriber()
    }
    
    private func addSubscriber() {
        DataManagerClass.shared.$dataArray
            .subscribe(on: DispatchQueue.global(qos: .background))
            .receive(on: DispatchQueue.main)
            .sink { _ in
                
            } receiveValue: { [weak self] returnedData in
                guard let self = self else { return }
                self.dataArray.append(contentsOf: returnedData)
            }
            .store(in: &cancellables)
    }
}

struct AsyncAwaitBootcamp: View {
    
    @StateObject private var viewModel = SubscriberViewModel()

    var body: some View {
        
        List{
            ForEach(viewModel.dataArray, id: \.self) {data in
                Text(data)
            }
        }
        .onAppear{
            
        }
    }
}

struct AsyncAwaitBootcamp_Previews: PreviewProvider {
    static var previews: some View {
        AsyncAwaitBootcamp()
    }
}

 

Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.