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)