Using Swift's async/await with SwiftUI can greatly simplify handling asynchronous tasks, such as fetching data from a network. Here's a basic example that includes a view, view model, use-case, repository, and service layer to illustrate how these components interact.
Seemy Github project for the tested source-code.
1. Service Layer
First, let's define a service layer responsible for fetching data. This could be a simple API service.APIService
conforms toAPIServiceProtocol
and simulates fetching data from an API.
importFoundationprotocolAPIServiceProtocol{funcfetchData()asyncthrows->String}classAPIService:APIServiceProtocol{funcfetchData()asyncthrows->String{// Simulate network delaytryawaitTask.sleep(nanoseconds:1_000_000_000)return"Data from API"}}
2. Repository Layer
The repository layer abstracts the data source (service layer) from the rest of the application.Repository
conforms toRepositoryProtocol
and uses theAPIService
to get data.
importFoundationprotocolRepositoryProtocol{funcgetData()asyncthrows->String}classRepository:RepositoryProtocol{privateletapiService:APIServiceProtocolinit(apiService:APIServiceProtocol=APIService()){self.apiService=apiService}funcgetData()asyncthrows->String{returntryawaitapiService.fetchData()}}
3. Use-Case Layer
The use-case layer contains the business logic. In this case, it fetches data using the repository.FetchDataUseCase
conforms toFetchDataUseCaseProtocol
and uses the repository to fetch data.
importFoundationprotocolFetchDataUseCaseProtocol{funcexecute()asyncthrows->String}classFetchDataUseCase:FetchDataUseCaseProtocol{privateletrepository:RepositoryProtocolinit(repository:RepositoryProtocol=Repository()){self.repository=repository}funcexecute()asyncthrows->String{returntryawaitrepository.getData()}}
4. ViewModel
The view model interacts with the use-case layer and provides data to the view.DataViewModel
is anObservableObject
that handles data fetching asynchronously using the use-case. It manages loading state, data, and potential error messages. Usingasync/await
in this way makes the code more readable and easier to follow compared to traditional completion handler approaches. The@MainActor
attribute ensures that UI updates happen on the main thread.
importFoundationimportSwiftUI@MainActorclassDataViewModel:ObservableObject{@Publishedvardata:String=""@PublishedvarisLoading:Bool=false@PublishedvarerrorMessage:String?privateletfetchDataUseCase:FetchDataUseCaseProtocolinit(fetchDataUseCase:FetchDataUseCaseProtocol=FetchDataUseCase()){self.fetchDataUseCase=fetchDataUseCase}funcloadData()async{isLoading=trueerrorMessage=nildo{letresult=tryawaitfetchDataUseCase.execute()data=result}catch{errorMessage=error.localizedDescription}isLoading=false}}
5. View
Finally, the view observes the view model and updates the UI accordingly.ContentView
observesDataViewModel
and displays a loading indicator, the fetched data, or an error message based on the state of the view model.
importSwiftUIstructContentView:View{@StateObjectprivatevarviewModel=DataViewModel()varbody:someView{VStack{ifviewModel.isLoading{ProgressView()}elseifleterrorMessage=viewModel.errorMessage{Text("Error:\(errorMessage)")}else{Text(viewModel.data)}}.onAppear{Task{awaitviewModel.loadData()}}.padding()}}
Top comments(0)
For further actions, you may consider blocking this person and/orreporting abuse