How to use AsyncPublisher to convert @Published to Async/Await in Swift | Swift 동시성 #12

Sung Daegyu·2024년 5월 1일

Swift: How to use AsyncPublisher to convert @Published to Async/Await in Swift | Swift Concurrency #12

상황 설명.

AsyncPublisherDataManager라는 데이터 매니저 class가 있고 여기에는 @Publised 변수인 myData가 있다.

class AsyncPublisherDataManager {
  @Published var myData: [String] = []
 }

이 데이터들은 시간에 따라 변한다. 코드에서는 2초마다 새로운 데이터가 추가된다.

myData.append("Apple")
try? await Task.sleep(nanoseconds: 2_000_000_000)

myData.append("Banana")
try? await Task.sleep(nanoseconds: 2_000_000_000)

myData.append("Orange")
try? await Task.sleep(nanoseconds: 2_000_000_000)

myData.append("Watermelon")

AsyncPublisherBootcampViewModel라는 view model class가 있고, 여기에도 마찬가지로 @Published 변수인 dataArray가 있다.

데이터 매니저에서의 변화를 감지하고 dataArray에 반영해야하기 때문에 데이터 매니저를 변수(myData)를 참조한다.

class AsyncPublisherBootcampViewModel: ObservableObject {
  @MainActor @Published var dataArray: [String] = []
  let manager = AsyncPublisherDataManager() // 데이터 매니저를 참조.
}

여기서 DataManager가 변화할 때마다 변화된 부분을 즉각적으로 viewModel의 dataArray에 반영할 수 있을까? 다시 말해서 “구독” 효과를 구현할 수 있을까?

기존에는 swift의 Combine을 통해서 구독 기능을 구현할 수 있었다.

import combine

var cancellabes = Set<AnyCancellable>()

manager.$myData
     .receive(on: DispatchQueue.main, options: nil)
     .sink { dataArray in
     self.dataArray = dataArray
     }
     .store(in: &cancellabes)

하지만, 보다시피 너무 코드가 복잡할 뿐더러 Combine 라이브러리의 의존이 필요하다.

또한Combine을 모르면 사용이 불가능하다.. ㅠㅠ

AsyncPublisher 사용.

AsyncPublisher를 사용하면 코드가 더 간단해지면서도 같은 기능을 구현할 수 있다!

위 코드를 그대로 AsyncPublisher를 사용해서 구현하면 다음과 같다.

Task {
	for await value in manager.$myData.values {
		await MainActor.run {
			self.dataArray = value
		}
	}
}

→ 코드가 매우 간결해졌다!

manager.$myData.values를 보면 AsyncPublisher 타입임을 알 수 있다.

주의점.

위 코드의 for-in 구문은 구독 기능을 구현한 것이다. 따라서, 직접 break 구문을 넣지 않는 이상 코드가 영원히 돌아간다.

→ 계속 manager.$mydata의 변화를 listening한다.

만약 for-in 구문 아래의 코드를 작성한다면, 그 코드는 실행되지 않는다.

→ for-in 다음 구문으로 넘어가지 않는다.

Task {
	for await value in manager.$myData.values {
		// 계속 listening 하기 떄문에 다음으로 넘어가지 않음. (영원히 계속됨)
		// 다음 코드로 넘어가려면 break을 해줘야 함.
		await MainActor.run {
		self.dataArray = value
		}
	}
	
	print("이 구문은 절대 출력이 안될 것이다.")
}
profile
대규의 개발로그

0개의 댓글