Skip to main content
Swift for iOS Development
CHAPTER 18 Beginner

Networking and API Calls in Swift

Updated: May 16, 2026
7 min read

# CHAPTER 18

Networking and API Calls in Swift

1. Introduction

A mobile app without the internet is just a calculator. Every major app—Instagram, Spotify, Uber—functions by downloading data from external servers (APIs) and displaying it on the screen. In iOS, the engine responsible for sending HTTP requests to the internet is called URLSession. In this chapter, we will master Networking and API Calls in Swift. We will learn how to construct valid URLs, execute asynchronous GET requests using modern async/await syntax, and handle the raw binary data returned by RESTful APIs.

2. Learning Objectives

By the end of this chapter, you will be able to:
  • Understand the concept of REST APIs and JSON endpoints.
  • Construct safe URL objects in Swift.
  • Understand the necessity of Asynchronous programming (async/await).
  • Use URLSession.shared.data(from:) to fetch internet data.
  • Handle networking errors gracefully using do-catch.

3. What is an API?

An API (Application Programming Interface) is a messenger. Your iPhone asks the API, *"What is the weather in New York?"* The server processes the request and sends back raw text data (usually formatted as JSON) containing the temperature. *Example Endpoint:* https://jsonplaceholder.typicode.com/users (A free, public API that returns dummy user data).

4. Asynchronous Programming (async/await)

If you ask the internet for data, it might take 3 seconds to arrive. If your app stops and waits for those 3 seconds, the entire UI will freeze, and the OS will crash the app for being unresponsive. We must tell Swift: *"Go fetch this data in the background. I am going to pause this specific function (await), but keep the rest of the app running. Wake this function back up when the data arrives."*

Functions that perform background tasks must be marked with the async keyword.

5. Constructing the Network Request

Let's build a function in our ViewModel to download data from a URL.
swift
12345678910111213141516171819202122232425262728293031
import Foundation

class NetworkManager: ObservableObject {
    
    // 1. Mark the function as 'async' and 'throws' (because the internet might fail)
    func fetchRawData() async throws {
        
        // 2. Create a safe URL. (We use guard because a URL with weird spaces can be nil!)
        let urlString = "https://jsonplaceholder.typicode.com/users"
        guard let url = URL(string: urlString) else {
            print("Error: Invalid URL")
            return
        }
        
        print("Starting download...")
        
        // 3. The Network Call! 
        // We 'try' because it might fail. We 'await' because it takes time!
        // URLSession returns a Tuple: The raw (data) and the HTTP (response).
        let (data, response) = try await URLSession.shared.data(from: url)
        
        // 4. Check the HTTP Status Code (200 means OK!)
        guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
            print("Error: Bad Server Response")
            return
        }
        
        // 5. Print the raw binary data size
        print("Successfully downloaded \(data.count) bytes of data!")
    }
}

6. Executing the Request from SwiftUI

We cannot just call a background async function inside a standard UI button. We must wrap the call in a Task block. A Task creates a safe, isolated background bubble for the asynchronous work to happen.
swift
1234567891011121314151617181920212223242526
import SwiftUI

struct DownloadView: View {
    @StateObject private var manager = NetworkManager()
    
    var body: some View {
        VStack {
            Text("Network Tester")
                .font(.largeTitle)
            
            Button("Fetch Data") {
                // We MUST wrap async calls inside a Task!
                Task {
                    do {
                        // We try to await the brain's function!
                        try await manager.fetchRawData()
                        print("Complete!")
                    } catch {
                        print("Fatal Error: \(error.localizedDescription)")
                    }
                }
            }
            .buttonStyle(.borderedProminent)
        }
    }
}

7. Main Actor (Thread Safety)

CRITICAL RULE: UI changes can ONLY happen on the "Main Thread". If your background network Task finishes, and you try to update a @Published variable to redraw the screen, Xcode will throw a massive purple warning: *"Publishing changes from background threads is not allowed."*

To fix this, you must force the ViewModel to jump back to the Main Thread before touching the UI. You do this by marking the ViewModel class with @MainActor.

swift
123456
// By adding @MainActor, any UI variables modified by this class are safely updated on the Main Thread!
@MainActor 
class NetworkManager: ObservableObject {
    @Published var isLoading = false
    // ...
}

8. Mini Project: Weather API Skeleton

swift
12345678910111213141516
@MainActor
class WeatherViewModel: ObservableObject {
    @Published var weatherStatus = "Unknown"
    
    func checkWeather() async {
        guard let url = URL(string: "https://fake-weather-api.com/now") else { return }
        
        do {
            let (data, _) = try await URLSession.shared.data(from: url)
            // Imagine we convert the 'data' into a String here...
            weatherStatus = "Sunny (Downloaded!)" 
        } catch {
            weatherStatus = "Failed to connect to weather satellite."
        }
    }
}

9. Common Mistakes

  • Forgetting App Transport Security (ATS): Apple rigidly forces all network traffic to be encrypted (https://). If you try to fetch data from an old http:// website, Apple will silently block the request to protect the user's security. You must either use HTTPS, or specifically configure an ATS exception in the Info.plist file.

10. Best Practices

  • Never Hardcode URLs in Views: Your UI buttons should never contain string URLs. The View should only know Task { await viewModel.fetch() }. All URL strings, headers, and URLSession logic MUST live securely inside your isolated networking class or ViewModel.

11. Exercises

  1. 1. Write a guard let statement that safely converts the string "https://google.com" into a URL object.
  1. 2. Inside a Button action, write a Task block.

12. Coding Challenges

Challenge: Modify the fetchRawData() function to purposely hit a broken URL (e.g., "https://thiswebsitedoesnotexist123.com"). Run the app, click the button, and observe how the do-catch block safely intercepts the catastrophic network failure without crashing the app!

13. MCQ Quiz with Answers

Question 1

What is the explicit purpose of the await keyword in modern Swift networking?

Question 2

If a background networking task attempts to directly modify a @Published property that controls the UI, the app will generate a threading error. Which attribute must be applied to the ViewModel class to ensure all UI updates happen safely on the main thread?

14. Interview Questions

  • Q: Explain the necessity of asynchronous programming in mobile architecture. What happens to the user experience if a massive URLSession data download is executed synchronously on the Main Thread?
  • Q: Detail the structural components returned by URLSession.shared.data(from: url). Why is validating the HTTPURLResponse status code (e.g., verifying it is 200) a critical step before processing the raw data?
  • Q: Describe the architectural purpose of the @MainActor attribute when orchestrating complex background networking tasks within an MVVM architecture.

15. FAQs

Q: Can I use third-party libraries like Alamofire instead of URLSession? A: You can, and historically, many developers did because URLSession used to be very ugly to write. However, with the introduction of modern async/await syntax, native URLSession is incredibly clean. Most modern apps no longer require heavy third-party networking libraries.

16. Summary

In Chapter 18, we broke out of the local device and connected our application to the global internet. We constructed safe REST API endpoints utilizing the URL object and orchestrated non-blocking background downloads using native URLSession. We mastered modern asynchronous concurrency architecture, utilizing the async/await syntax to prevent UI freezing, wrapping executions in isolated Task blocks, and guaranteeing thread safety for UI mutations via the powerful @MainActor attribute.

17. Next Chapter Recommendation

Our code downloaded 5,000 bytes of raw, unreadable binary data. You cannot display binary data in a Text view. We must decode that binary blob into usable Swift Structs. Proceed to Chapter 19: JSON Parsing and Codable.

Finish this Chapter

Save your progress on your learning path and prepare for coding interview challenges.

Discussion

Join the discussion

Log in or create a free account to participate.

Sort: ·