Image for post
Image for post

Swift 5: How to do Async/Await with Result and GCD

Why wait for Apple to add Async/Await to Swift when you can have it now?

Overview

    func load() {
DispatchQueue.global(qos: .utility).async {
let result = self.makeAPICall()
.flatMap { self.anotherAPICall($0) }
.flatMap { self.andAnotherAPICall($0) }

DispatchQueue.main.async {
switch result {
case let .success(data):
print(data)
case let .failure(error):
print(error)
}
}
}
}

Using Result

enum NetworkError: Error {
case url
case server
}
func makeAPICall() -> Result<String?, NetworkError> {
// our network code
}
func load() {
DispatchQueue.global(qos: .utility).async {
let result = self.makeAPICall() DispatchQueue.main.async {
switch result {
case let .success(data):
print(data)
case let .failure(error):
print(error)
}
}
}
}

Making API Calls Using Result

func makeAPICall() -> Result<String?, NetworkError> {    let path = "https://jsonplaceholder.typicode.com/todos/1"    guard let url = URL(string: path) else {
return .failure(.url)
}
var result: Result<String?, NetworkError>! // API Call Goes Here return result
}
    URLSession.shared.dataTask(with: url) { (data, _, _) in
if let data = data {
result = .success(String(data: data, encoding: .utf8))
} else {
result = .failure(.server)
}
}.resume()

Making API Calls Using CGD Semaphores

func makeAPICall() -> Result<String?, NetworkError> {    let path = "https://jsonplaceholder.typicode.com/todos/1"
guard let url = URL(string: path) else {
return .failure(.url)
}
var result: Result<String?, NetworkError>!

let semaphore = DispatchSemaphore(value: 0)
URLSession.shared.dataTask(with: url) { (data, _, _) in
if let data = data {
result = .success(String(data: data, encoding: .utf8))
} else {
result = .failure(.server)
}
semaphore.signal()
}.resume()
_ = semaphore.wait(wallTimeout: .distantFuture) return result
}

Chaining Multiple API Calls

func processImageData1() async -> Image {
let dataResource = await loadWebResource("dataprofile.txt")
let imageResource = await loadWebResource("imagedata.dat")
let imageTmp = await decodeImage(dataResource, imageResource)
let imageResult = await dewarpAndCleanupImage(imageTmp)
return imageResult
}
func anotherAPICall(_ param: String?) -> Result<Int, NetworkError>
{ ... }
func andAnotherAPICall(_ param: Int) -> Result<User, NetworkError>
{ ... }
    func load() {
DispatchQueue.global(qos: .utility).async {
let result = self.makeAPICall()
.flatMap { self.anotherAPICall($0) }
.flatMap { self.andAnotherAPICall($0) }

DispatchQueue.main.async {
switch result {
case let .success(data):
print(data)
case let .failure(error):
print(error)
}
}
}
}

FlatMap?

Ummm… What About Errors?

Threading

Written by

Michael Long is a Senior Lead iOS engineer at CRi Solutions, a leader in cutting edge iOS, Android, and mobile corporate and financial applications.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store