Design+Code logo

Quick links

Suggested search

The API

For this tutorial, we'll make an HTTP get request to the Random Data API. We'll call the food endpoint, to get a random food item for every HTTP request. The link to the API is https://random-data-api.com/api/food/random_food.

Food model

We'll create a food model that follows the JSON object we find on the random food endpoint from Random Data API website.

// Food.swift

struct Food: Identifiable, Decodable {
    var id: Int
    var uid: String
    var dish: String
    var description: String
    var ingredient: String
    var measurement: String
}

With completion handlers

In the function below, we create an HTTP request using the old method - that is, with completion handler. You can see that the function spans many lines of code, and it can be hard to read. You can learn more about how to create an HTTP request with completion handlers in the HTTP request tutorial.

// Network.swift

func getRandomFoodWithCompletionHandler() {
    guard let url = URL(string: "https://random-data-api.com/api/food/random_food") else { fatalError("Missing URL") }

    let urlRequest = URLRequest(url: url)

    let dataTask = URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
        if let error = error {
            print("Request error: ", error)
            return
        }

        guard (response as? HTTPURLResponse)?.statusCode == 200 else { return }
        guard let data = data else { return }
				do {
             let decodedFood = try JSONDecoder().decode(Food.self, from: data)
             print("Completion handler decodedFood", decodedFood)
         } catch {
		         print("Error decoding", error)
         }
    }

    dataTask.resume()
}

In our view, let's initialize the network class at the top of the file.

// ContentView.swift

let network = Network()

Then, we can call the above function in the onAppear modifier to get our data printed on the console.

// ContentView.swift

.onAppear {
		network.getRandomFoodWithCompletionHandler()
}

Now when you build your application, you should see data printed on the console from the completion handler function!

Target iOS 15

For the purposes of this tutorial, make sure that your project targets iOS 15, because async await is only available for iOS 15 and later. To do so, go into your project settings > targets > General > Deployment Info and select iOS 15.0 in the dropdown menu. This will allow us to write concurrent code, without having to always wrap the code in an if available statement.

Target iOS15

With async await

With async await, the same function is so much cleaner. First, let's define our function as asynchronous.

// Network.swift

func getRandomFood() async {

}

Note: async await functions are still in beta as of July 2021. For now, it's only available if you're under the Apple Developer Program. It should be available to all when Xcode 13 will be released, in Fall 2021.

Next, we'll keep the guard statement that makes sure we do have a url before continuing. We'll also keep the urlRequest variable.

// Network.swift

func getRandomFood() async {
    guard let url = URL(string: "https://random-data-api.com/api/food/random_food") else { fatalError("Missing URL") }
		let urlRequest = URLRequest(url: url)
}

Then, we'll call URLSession.shared.data to get the data from the URL we created. Since this is a network call, it can take some time to fetch the data. So we'll need to add the await keyword in front of the URLSession. Moreover, the URLSession call might throw an error, so we'll need to add the try keyword before the await. It might return us two things: some data, and a response, so we'll save them in their corresponding variable.

// Network.swift

func getRandomFood() async {
    guard let url = URL(string: "https://random-data-api.com/api/food/random_food") else { fatalError("Missing URL") }
		let urlRequest = URLRequest(url: url)
		let (data, response) = try await URLSession.shared.data(for: urlRequest)
}

Xcode will complain that the errors thrown within the function aren't handled, so we need to mark the getRandomFood function as throws, to let the program know that this function might throw an error.

// Network.swift

func getRandomFood() async throws {
    guard let url = URL(string: "https://random-data-api.com/api/food/random_food") else { fatalError("Missing URL") }
		let urlRequest = URLRequest(url: url)
		let (data, response) = try await URLSession.shared.data(for: urlRequest)
}

After getting the data and/or the response, we need to make sure that the status code of the response is 200 OK before continuing, so let's add a guard statement.

// Network.swift

func getRandomFood() async throws {
    guard let url = URL(string: "https://random-data-api.com/api/food/random_food") else { fatalError("Missing URL") }
		let urlRequest = URLRequest(url: url)
		let (data, response) = try await URLSession.shared.data(for: urlRequest)

		guard (response as? HTTPURLResponse)?.statusCode == 200 else { fatalError("Error while fetching data") }
}

Finally, we'll decode the data that we get into the Food model and print the decoded food.

// Network.swift

func getRandomFood() async throws {
    guard let url = URL(string: "https://random-data-api.com/api/food/random_food") else { fatalError("Missing URL") }
		let urlRequest = URLRequest(url: url)
		let (data, response) = try await URLSession.shared.data(for: urlRequest)

		guard (response as? HTTPURLResponse)?.statusCode == 200 else { fatalError("Error while fetching data") }
		let decodedFood = try JSONDecoder().decode(Food.self, from: data)
    print("Async decodedFood", decodedFood)
}

Call the function

In our view, we'll need to call the getRandomFood function in a task modifier, since onAppear doesn't support asynchronous code.

// ContentView.swift

.task {
		network.getRandomFood()
}

However, since this function is asynchronous and can throw an error, we'll need to add try await in front:

// ContentView.swift

.task {
		try await network.getRandomFood()
}

Now, Xcode will complain that errors thrown from the function aren't handled. So let's wrap our code in a do catch statement, and print the error if we ever run into one.

// ContentView.swift

.task {
		do {
				try await network.getRandomFood()
		} catch {
				print("Error", error)
		}
}

Build your application

Great, now the last step is simply to make sure we get data from the asynchronous function. Build your application, and you should see two decodedFood printed on the console: one from the completion handler function, and the other one from the asynchronous function!

Asynchronous functions make our code cleaner and easier to read, as they require less lines of code. Moreover, we can simultaneously run other code while we wait for the data to be fetched, freeing CPU usage and making our application more performant. You can learn more about concurrency in my course SwiftUI Concurrency.

Learn with videos and source files. Available to Pro subscribers only.

Purchase includes access to 50+ courses, 320+ premium tutorials, 300+ hours of videos, source files and certificates.

BACK TO

Offline Data with Realm

READ NEXT

Xcode Cloud

Templates and source code

Download source files

Download the videos and assets to refer and learn offline without interuption.

check

Design template

check

Source code for all sections

check

Video files, ePub and subtitles

Browse all downloads

1

Firebase Auth

How to install Firebase authentification to your Xcode project

8:18

2

Read from Firestore

Install Cloud Firestore in your application to fetch and read data from a collection

8:01

3

Write to Firestore

Save the data users input in your application in a Firestore collection

5:35

4

Join an Array of Strings

Turn your array into a serialized String

3:33

5

Data from JSON

Load data from a JSON file into your SwiftUI application

5:08

6

HTTP Request

Create an HTTP Get Request to fetch data from an API

6:31

7

WKWebView

Integrate an HTML page into your SwiftUI application using WKWebView and by converting Markdown into HTML

5:25

8

Code Highlighting in a WebView

Use Highlight.js to convert your code blocks into beautiful highlighted code in a WebView

5:11

9

Test for Production in the Simulator

Build your app on Release scheme to test for production

1:43

10

Debug Performance in a WebView

Enable Safari's WebInspector to debug the performance of a WebView in your application

1:57

11

Debug a Crash Log

Learn how to debug a crash log from App Store Connect in Xcode

2:22

12

Simulate a Bad Network

Test your SwiftUI application by simulating a bad network connection with Network Link Conditionner

2:11

13

Archive a Build in Xcode

Archive a build for beta testing or to release in the App Store

1:28

14

Apollo GraphQL Part I

Install Apollo GraphQL in your project to fetch data from an API

6:21

15

Apollo GraphQL Part 2

Make a network call to fetch your data and process it into your own data type

6:43

16

Apollo GraphQL Part 3

Display the data fetched with Apollo GraphQL in your View

5:08

17

Configuration Files in Xcode

Create configuration files and add variables depending on the environment - development or production

4:35

18

App Review

Request an app review from your user for the AppStore

5:43

19

ImagePicker

Create an ImagePicker to choose a photo from the library or take a photo from the camera

5:06

20

Compress a UIImage

Compress a UIImage by converting it to JPEG, reducing its size and quality

3:32

21

Firebase Storage

Upload, delete and list files in Firebase Storage

11:11

22

Search Feature

Implement a search feature to filter through your content in your SwiftUI application

9:13

23

Push Notifications Part 1

Set up Firebase Cloud Messaging as a provider server to send push notifications to your users

5:59

24

Push Notifications Part 2

Create an AppDelegate to ask permission to send push notifications using Apple Push Notifications service and Firebase Cloud Messaging

6:30

25

Push Notifications Part 3

Tie everything together and test your push notifications feature in production

6:13

26

Network Connection

Verify the network connection of your user to perform tasks depending on their network's reachability

6:49

27

Download Files Locally Part 1

Download videos and files locally so users can watch them offline

6:05

28

Download Files Locally Part 2

Learn how to use the DownloadManager class in your views for offline video viewing

6:02

29

Offline Data with Realm

Save your SwiftUI data into a Realm so users can access them offline

10:20

30

HTTP Request with Async Await

Create an HTTP get request function using async await

6:11

31

Xcode Cloud

Automate workflows with Xcode Cloud

9:23

32

SceneStorage and TabView

Use @SceneStorage with TabView for better user experience on iPad

3:52

33

Network Connection Observer

Observe the network connection state using NWPathMonitor

4:37

34

Apollo GraphQL Caching

Cache data for offline availability with Apollo GraphQL

9:42

35

Create a model from an API response

Learn how to create a SwiftUI model out of the response body of an API

5:37

36

Multiple type variables in Swift

Make your models conform to the same protocol to create multiple type variables

4:23

37

Parsing Data with SwiftyJSON

Make API calls and easily parse data with this JSON package

9:36

38

ShazamKit

Build a simple Shazam clone and perform music recognition

12:38

39

Firebase Remote Config

Deliver changes to your app on the fly remotely

9:05