Working with REST APIs in SwiftUI: Tips, Tricks, and Best Practices
I am using a REST API to fetch data. You will see how to use URL sessions in Swift to get JSON files and images.
This is a very important topic, especially if you are planning to land a job as an iOS developer soon.
Using an API in a take-home project is a very common task.
Overview
- Introduction
- URLSession and JSON
- Fetching images
- Response and status code
- Decoding JSON
- Renaming of properties and CodingKey
- Parsing different types with init(from decoder)
- Nested JSON data
- Fetching more images
Create a URL
let url = URL(string: "https://www.swiftuiio.com")!
Fetching String using URLSession
- Create and execute a network request
- Create a data task
let url = URL(string: "https://www.swiftuiio.com/")!
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
if let data = data,
let string = String(data: data, encoding: .utf8){
print(string)
}
}
task.resume()
The output will be an HTML response
Fetching Image using URLSession
import Foundation
import UIKit
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true// write this at the beginning
let url = URL(string: "https://cdn2.thecatapi.com/images/bq1.jpg")!
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
if let data = data,
let image = UIImage(data: data){
print("success")
let i = image
}
PlaygroundPage.current.finishExecution()// To finish execution
}
task.resume()
Handling Request Method using URLRequest
import UIKit
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
//let url = URL(string: "https://www.swiftuiio.com")!
let url = URL(string: "https://api.thecatapi.com/v1/breeds?limit=1")!
var request = URLRequest(url: url)
request.httpMethod = "GET"
//request.allHTTPHeaderFields = ["x-api-key": "API_key"]
let task = URLSession.shared.dataTask(with: request) { data, response, error in
if let response = response as? HTTPURLResponse, !(200...299).contains(response.statusCode) {
print(response.statusCode)
//TODO: throw error
}
if let error = error {
print(error.localizedDescription)
}
if let data = data,
let file = String(data: data, encoding: .utf8) {
print(file)
}
PlaygroundPage.current.finishExecution()
}
task.resume()
...
How to parse JSON into model objects with Codable
Let us work with network services and APIs, and how to parse JSON into model objects that can be used in your iOS app.
I will demonstrate how to read basic JSON data and how to write custom Swift model types.
Working with Decoding JSON
//: [Previous](@previous)
import Foundation
/*
{
"userId": 1,
"id": 1,
"title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
"body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
}
*/
struct Post: Codable {
let userId: Int
let id: Int
let title: String
let body: String
}
//let url = URL(string: "https://jsonplaceholder.typicode.com/posts/1")!
let url = URL(string: "https://jsonplaceholder.typicode.com/posts")!
let task = URLSession.shared.dataTask(with: url) { data, response, error in
let decoder = JSONDecoder()
if let data = data{
do {
let posts = try decoder.decode([Post].self, from: data)
print("found \(posts.count) posts")
} catch {
print(error)
}
}
}
task.resume()
//: [Next](@next)
..
How to parse JSON into the model objects with Codable
- Nested JSON data
- Renaming of properties and CodingKey
- Parsing different types with init(from decoder)
- Fetching more images
Convert JSON data to Swift types
JSON data may not string always, let us see some examples of how to handle different data to swift type
Decoding into custom model objects
..
//: [Previous](@previous)
import Foundation
/*
[{"weight":{"imperial":"7 - 10","metric":"3 - 5"},"id":"abys","name":"Abyssinian","cfa_url":"http://cfa.org/Breeds/BreedsAB/Abyssinian.aspx","vetstreet_url":"http://www.vetstreet.com/cats/abyssinian","vcahospitals_url":"https://vcahospitals.com/know-your-pet/cat-breeds/abyssinian","temperament":"Active, Energetic, Independent, Intelligent, Gentle","origin":"Egypt","country_codes":"EG","country_code":"EG","description":"The Abyssinian is easy to care for, and a joy to have in your home. They’re affectionate cats and love both people and other animals.","life_span":"14 - 15","indoor":0,"lap":1,"alt_names":"","adaptability":5,"affection_level":5,"child_friendly":3,"dog_friendly":4,"energy_level":5,"grooming":1,"health_issues":2,"intelligence":5,"shedding_level":2,"social_needs":5,"stranger_friendly":5,"vocalisation":1,"experimental":0,"hairless":0,"natural":1,"rare":0,"rex":0,"suppressed_tail":0,"short_legs":0,"wikipedia_url":"https://en.wikipedia.org/wiki/Abyssinian_(cat)","hypoallergenic":0,"reference_image_id":"0XYvRd7oD","image":{"id":"0XYvRd7oD","width":1204,"height":1445,"url":"https://cdn2.thecatapi.com/images/0XYvRd7oD.jpg"}}]
*/
struct Breed: Codable, CustomStringConvertible {
let id: String
let name: String
let temperament: String
let breedExplaination: String
let energyLevel: Int
let isHairless: Bool//for this we need custom init bez it is integer format
let image: BreedImage
var description: String {
return "breed with name: \(name) and id \(id), energy level: \(energyLevel) isHairless: \(isHairless ? "YES" : "NO")"
}
enum CodingKeys: String, CodingKey {
case id
case name
case temperament
case breedExplaination = "description"
case energyLevel = "energy_level"
case isHairless = "hairless"
case image
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
id = try values.decode(String.self, forKey: .id)
name = try values.decode(String.self, forKey: .name)
temperament = try values.decode(String.self, forKey: .temperament)
breedExplaination = try values.decode(String.self, forKey: .breedExplaination)
energyLevel = try values.decode(Int.self, forKey: .energyLevel)
let hairless = try values.decode(Int.self, forKey: .isHairless)
isHairless = hairless == 1
image = try values.decode(BreedImage.self, forKey: .image)
}
}
/*
"image": {
"height": 1445,
"id": "0XYvRd7oD",
"url": "https://cdn2.thecatapi.com/images/0XYvRd7oD.jpg",
"width": 1204
},
*/
struct BreedImage: Codable {
let height: Int
let id: String
let url: String
let width: Int
}
let url = URL(string: "https://api.thecatapi.com/v1/breeds?limit=1")!
let task = URLSession.shared.dataTask(with: url) { data , response, error in
let decoder = JSONDecoder()
if let data = data {
do {
let breeds = try decoder.decode([Breed].self, from: data)
print(breeds)
}catch {
print(error)
}
}
}
task.resume()
//: [Next](@next)
..
//: [Previous](@previous)
import Foundation
struct Breed: Codable {
let id: String
let name: String
let temperament: String
}
struct BreedImage: Codable {
let breeds: [Breed]
let height: Int
let id: String
let url: String
let width: Int
}
let selectedCatBreedId = "abys"
let url = URL(string: "https://api.thecatapi.com/v1/images/search?limit=9&breed_id=\(selectedCatBreedId)")!
let task = URLSession.shared.dataTask(with: url) { data, response, error in
let decoder = JSONDecoder()
if let data = data {
do {
let images = try decoder.decode([BreedImage].self, from: data)
print("success, fetched \(images.count) image urls")
} catch {
print(error)
}
}
}
task.resume()
//: [Next](@next)
..
Tips:
You can also Convert JSON to Data Class online tool.
Resources:
Cat API
Cat API Documentation
JSON pretty print tool:
Comments
Post a Comment