Downloading JSON Data with URLSession
Learn how to fetch and decode JSON data from a remote URL using URLSession and JSONDecoder in Swift, a fundamental skill for working with APIs.
Note:
This tutorial explains how to download data from websites or APIs using the URLSession framework and parse it with JSONDecoder.
Overview
A common task in mobile development is fetching data from a remote server or API. Typically, this data is formatted as JSON. In Swift, we use the URLSession API for networking and JSONDecoder to parse JSON data into our custom Swift types.
For this example, we'll fetch a JSON file that contains a list of news articles, each with a title and a story.
Reference:
Step 1: Create the Data Model
First, we need to create a Swift struct that matches the structure of our JSON objects. By making our struct conform to the Decodable protocol, we can use JSONDecoder to automatically convert the JSON data into instances of our struct.
Create a new file News.swift:
// In News.swift
import Foundation
struct News: Decodable {
let title: String
let story: String
}
Step 2: Create a Networking Service
To make our code reusable, we'll create a new class or struct to handle the data downloading logic. This service will have a function that takes a URL and a completion handler. The @escaping keyword indicates that the completion handler will be called after the function returns, which is necessary for asynchronous operations.
// In a new file, e.g., WebService.swift
import Foundation
class WebService {
func downloadNews(from url: URL, completion: @escaping ([News]?) -> ()) {
// Networking code will go here
}
}
Step 3: Fetch and Decode the Data
Inside our downloadNews function, we'll use URLSession.shared.dataTask to perform the network request.
- Create a data task with the specified URL.
- In the completion handler, check for errors.
- If data is received, use
JSONDecoder().decode(_:from:)to try and parse it into an array ofNewsobjects. - Call the function's completion handler with the decoded data (or
nilif an error occurred). - Don't forget to call
.resume()on the data task to start it.
// Inside the WebService class
func downloadNews(from url: URL, completion: @escaping ([News]?) -> ()) {
URLSession.shared.dataTask(with: url) { data, response, error in
if let error = error {
print("Error downloading data: \(error)")
completion(nil)
return
}
guard let data = data else {
print("No data received.")
completion(nil)
return
}
do {
let newsArray = try JSONDecoder().decode([News].self, from: data)
completion(newsArray)
} catch {
print("Error decoding data: \(error)")
completion(nil)
}
}.resume()
}
Step 4: Using the Networking Service
Now, from any ViewController, you can create an instance of WebService and call the downloadNews function. The results will be delivered asynchronously in the completion block.
// In your ViewController
class NewsViewController: UIViewController {
private var newsArray = [News]()
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "https://your-api-url.com/news.json")!
WebService().downloadNews(from: url) { (news) in
if let news = news {
self.newsArray = news
// Since this completion is on a background thread,
// update UI on the main thread.
DispatchQueue.main.async {
// self.tableView.reloadData()
print(self.newsArray)
}
}
}
}
}
Note:
This example provides a solid foundation for working with any JSON API. Remember to always handle UI updates on the main thread.