Alamofire is a swift http request lib. By default, we need use completion handler to handle response. Today we are going to find out how to use it with async and await.
And we will learn how to achieve this by fetching a appliance list from this url. It's provided by random-data-api.com
Install Alamofire
First we should create an new iOS project in XCode, and install Alamofire in our project with Swift package manger
https://github.com/Alamofire/Alamofire.git
Define Model Appliance
Models/Appliance.swift
struct Appliance: Identifiable, Codable {
    let id: Int
    let uid: String
    let brand: String
    let equipment: String
}
Create Network Manager
Network/NetworkManager.swift
import Foundation
import Alamofire
private let API_BASE_URL = "https://random-data-api.com"
actor NetworkManager: GlobalActor {
    static let shared = NetworkManager()
    private init() {}
    private let maxWaitTime = 15.0
    var commonHeaders: HTTPHeaders = [
        "user_id": "123",
        "token": "xxx-xx"
    ]
    func get(path: String, parameters: Parameters?) async throws -> Data {
       // You must resume the continuation exactly once
        return try await withCheckedThrowingContinuation { continuation in
            AF.request(
                API_BASE_URL + path,
                parameters: parameters,
                headers: commonHeaders,
                requestModifier: { $0.timeoutInterval = self.maxWaitTime }
            )
            .responseData { response in
                switch(response.result) {
                case let .success(data):
                    continuation.resume(returning: data)
                case let .failure(error):
                    continuation.resume(throwing: self.handleError(error: error))
                }
            }
        }
    }
    private func handleError(error: AFError) -> Error {
        if let underlyingError = error.underlyingError {
            let nserror = underlyingError as NSError
            let code = nserror.code
            if code == NSURLErrorNotConnectedToInternet ||
                code == NSURLErrorTimedOut ||
                code == NSURLErrorInternationalRoamingOff ||
                code == NSURLErrorDataNotAllowed ||
                code == NSURLErrorCannotFindHost ||
                code == NSURLErrorCannotConnectToHost ||
                code == NSURLErrorNetworkConnectionLost
            {
                var userInfo = nserror.userInfo
                userInfo[NSLocalizedDescriptionKey] = "Unable to connect to the server"
                let currentError = NSError(
                    domain: nserror.domain,
                    code: code,
                    userInfo: userInfo
                )
                return currentError
            }
        }
        return error
    }
}
Create Network API
Network/NetworkAPI.swift
import Foundation
class NetworkAPI {
    static func getAppliances() async -> [Appliance]? {
        do {
            let data = try await NetworkManager.shared.get(
                path: "/api/v2/appliances?size=4", parameters: nil
            )
            let result: [Appliance] = try self.parseData(data: data)
            return result
        } catch let error {
            print(error.localizedDescription)
            return nil
        }
    }
    private static func parseData<T: Decodable>(data: Data) throws -> T{
        guard let decodedData = try? JSONDecoder().decode(T.self, from: data)
        else {
            throw NSError(
                domain: "NetworkAPIError",
                code: 3,
                userInfo: [NSLocalizedDescriptionKey: "JSON decode error"]
            )
        }
        return decodedData
    }
}
Create view model for ContentView
ContentView.swift
class ContentViewViewModel: ObservableObject {
    @MainActor @Published var errorMessage = ""
    @MainActor @Published var appliances: [Appliance] = []
    func fetchAppliances() async {
        await MainActor.run {
            self.errorMessage = ""
        }
        if let res = await NetworkAPI.getAppliances() {
            await MainActor.run {
                self.appliances = res
            }
        } else {
            await MainActor.run {
                self.errorMessage = "Fetch data failed"
            }
        }
    }
}
Render data in ContentView
ContentView.swift
struct ContentView: View {
    @StateObject var viewModel = ContentViewViewModel()
    var body: some View {
        VStack(spacing: 16) {
            Image(systemName: "globe")
                .imageScale(.large)
                .foregroundColor(.accentColor)
            if viewModel.errorMessage != "" {
                Text(viewModel.errorMessage)
            }
            Button("Fetch") {
                Task {
                    await viewModel.fetchAppliances()
                }
            }
            List {
                ForEach(viewModel.appliances, id: \.id) { item in
                    Text("\(item.brand) - \(item.equipment)")
                }
            }
            .listStyle(.inset)
        }
        .padding()
        .onAppear {
            Task {
                await viewModel.fetchAppliances()
            }
        }
    }
}
And the job is done. Source code
              

    
Top comments (0)