Async/await and SwiftUI

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 comp…


This content originally appeared on DEV Community and was authored by Tamas Dancsi

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.

1. Service Layer

First, let's define a service layer responsible for fetching data. This could be a simple API service. APIService conforms to APIServiceProtocol and simulates fetching data from an API.

import Foundation

protocol APIServiceProtocol {
    func fetchData() async throws -> String
}

class APIService: APIServiceProtocol {
    func fetchData() async throws -> String {
        // Simulate network delay
        try await Task.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 to RepositoryProtocol and uses the APIService to get data.

import Foundation

protocol RepositoryProtocol {
    func getData() async throws -> String
}

class Repository: RepositoryProtocol {
    private let apiService: APIServiceProtocol

    init(apiService: APIServiceProtocol = APIService()) {
        self.apiService = apiService
    }

    func getData() async throws -> String {
        return try await apiService.fetchData()
    }
}

3. Use-Case Layer

The use-case layer contains the business logic. In this case, it fetches data using the repository. FetchDataUseCase conforms to FetchDataUseCaseProtocol and uses the repository to fetch data.

import Foundation

protocol FetchDataUseCaseProtocol {
    func execute() async throws -> String
}

class FetchDataUseCase: FetchDataUseCaseProtocol {
    private let repository: RepositoryProtocol

    init(repository: RepositoryProtocol = Repository()) {
        self.repository = repository
    }

    func execute() async throws -> String {
        return try await repository.getData()
    }
}

4. ViewModel

The view model interacts with the use-case layer and provides data to the view. DataViewModel is an ObservableObject that handles data fetching asynchronously using the use-case. It manages loading state, data, and potential error messages. Using async/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.

import Foundation
import SwiftUI

@MainActor
class DataViewModel: ObservableObject {
    @Published var data: String = ""
    @Published var isLoading: Bool = false
    @Published var errorMessage: String?

    private let fetchDataUseCase: FetchDataUseCaseProtocol

    init(fetchDataUseCase: FetchDataUseCaseProtocol = FetchDataUseCase()) {
        self.fetchDataUseCase = fetchDataUseCase
    }

    func loadData() async {
        isLoading = true
        errorMessage = nil

        do {
            let result = try await fetchDataUseCase.execute()
            data = result
        } catch {
            errorMessage = error.localizedDescription
        }

        isLoading = false
    }
}

5. View

Finally, the view observes the view model and updates the UI accordingly. ContentView observes DataViewModel and displays a loading indicator, the fetched data, or an error message based on the state of the view model.

import SwiftUI

struct ContentView: View {
    @StateObject private var viewModel = DataViewModel()

    var body: some View {
        VStack {
            if viewModel.isLoading {
                ProgressView()
            } else if let errorMessage = viewModel.errorMessage {
                Text("Error: \(errorMessage)")
            } else {
                Text(viewModel.data)
            }
        }
        .onAppear {
            Task {
                await viewModel.loadData()
            }
        }
        .padding()
    }
}


This content originally appeared on DEV Community and was authored by Tamas Dancsi


Print Share Comment Cite Upload Translate Updates
APA

Tamas Dancsi | Sciencx (2024-06-20T12:28:34+00:00) Async/await and SwiftUI. Retrieved from https://www.scien.cx/2024/06/20/async-await-and-swiftui/

MLA
" » Async/await and SwiftUI." Tamas Dancsi | Sciencx - Thursday June 20, 2024, https://www.scien.cx/2024/06/20/async-await-and-swiftui/
HARVARD
Tamas Dancsi | Sciencx Thursday June 20, 2024 » Async/await and SwiftUI., viewed ,<https://www.scien.cx/2024/06/20/async-await-and-swiftui/>
VANCOUVER
Tamas Dancsi | Sciencx - » Async/await and SwiftUI. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2024/06/20/async-await-and-swiftui/
CHICAGO
" » Async/await and SwiftUI." Tamas Dancsi | Sciencx - Accessed . https://www.scien.cx/2024/06/20/async-await-and-swiftui/
IEEE
" » Async/await and SwiftUI." Tamas Dancsi | Sciencx [Online]. Available: https://www.scien.cx/2024/06/20/async-await-and-swiftui/. [Accessed: ]
rf:citation
» Async/await and SwiftUI | Tamas Dancsi | Sciencx | https://www.scien.cx/2024/06/20/async-await-and-swiftui/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.