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를 사용해서 구현하면 다음과 같다.
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("이 구문은 절대 출력이 안될 것이다.")
}