Skip to content

Instantly share code, notes, and snippets.

@tigi44
Last active July 11, 2023 07:15
Show Gist options
  • Save tigi44/a5fed5e5d98c1792908de60e8dabc3d3 to your computer and use it in GitHub Desktop.
Save tigi44/a5fed5e5d98c1792908de60e8dabc3d3 to your computer and use it in GitHub Desktop.
Async / Await with Combine in Swift, SwiftUI (MVVM pattern)
import SwiftUI
struct AsyncAwaitWithCombineView: View {
@StateObject var viewModel: AsyncAwaitWithCombineViewModel = AsyncAwaitWithCombineViewModel()
var body: some View {
NavigationView {
VStack {
Text("pull to refresh")
.foregroundColor(.secondary)
List(viewModel.stringList, id: \.self) {
Text($0)
}
}
.navigationTitle("Async/Await With Combine")
.navigationBarTitleDisplayMode(.inline)
.refreshable {
// viewModel.fetch()
await viewModel.asyncFetch()
// await viewModel.asyncFetchByAwaitSink()
}
}
}
}
struct AsyncAwaitWithCombineView_Previews: PreviewProvider {
static var previews: some View {
AsyncAwaitWithCombineView()
}
}
import Foundation
import Combine
// MARK: - AsyncAwaitWithCombineViewModel
class AsyncAwaitWithCombineViewModel: ObservableObject {
@Published var stringList: [String] = []
private var bag: Set<AnyCancellable> = Set<AnyCancellable>()
func fetch() {
DataSource().fetch()
.replaceError(with: [])
.assign(to: \.stringList, on: self)
.store(in: &bag)
}
func asyncFetch() async {
return await withCheckedContinuation { continuation in
DataSource().fetch()
.replaceError(with: [])
.sink(receiveValue: { stringList in
self.stringList = stringList
continuation.resume()
})
.store(in: &self.bag)
}
}
func asyncFetchByAwaitSink() async {
do {
self.stringList = try await DataSource().fetch().awaitSink(cancellable: &bag)
} catch {
self.stringList = []
}
}
}
// MARK: - Extension : Publisher
extension Publisher {
func awaitSink(cancellable: inout Set<AnyCancellable>) async throws -> Output {
return try await withCheckedThrowingContinuation { continuation in
self.sink { completion in
switch completion {
case .failure(let error):
continuation.resume(with: .failure(error))
case .finished:
break
}
} receiveValue: { result in
continuation.resume(with: .success(result))
}
.store(in: &cancellable)
}
}
}
// MARK: - DataSource : Combine
class DataSource {
func fetch() -> AnyPublisher<[String], Error> {
let source = ["First", "Second", "Third", "fourth"]
let result = source.shuffled()
return Just(result)
.setFailureType(to: Error.self)
.delay(for: 5, scheduler: DispatchQueue.global())
.eraseToAnyPublisher()
}
}
@tigi44
Copy link
Author

tigi44 commented Jan 5, 2022

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment