Vai al contenuto

CHIAMATE DI RETE

Cosa imparerò:

  • URLSession
  • HTTPS
  • GET, POST, DELETE
  • JSON
  • CodingKeys
  • Error

In Swift, URLSession è una classe che fornisce un'interfaccia per la comunicazione con server tramite HTTPS. Si tratta di una classe potente e flessibile che consente di eseguire richieste in modo asincrono, sia con closure sia con async/await.

// Definizione dell'URL della richiesta
let url = URL(string: "https://jsonplaceholder.typicode.com/posts/1")!
// Creazione della richiesta
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
    // Gestione della risposta
    if let error = error {
        print("Errore: \(error)")
        return
    }
    guard let httpResponse = response as? HTTPURLResponse,
          (200...299).contains(httpResponse.statusCode) else {
        print("Errore di risposta del server")
        return
    }
    if let data = data,
       let responseData = String(data: data, encoding: .utf8) {
        print("Risposta del server:")
        print(responseData)
    }
}
// Avvio della richiesta
task.resume()

Output

Risposta del server:
{
    "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"
}

In questo esempio, viene eseguita una richiesta GET all'URL "https://jsonplaceholder.typicode.com/posts/1" utilizzando URLSession.shared.dataTask(with:). Quando la richiesta viene completata, la closure passata a dataTask(with:) viene chiamata con i dati, la risposta e gli eventuali errori.

HTTPURLResponse viene utilizzato per verificare che la risposta del server sia nell'intervallo di successo (status code 200-299). Se la risposta è valida, i dati vengono stampati sulla console.

URLSession può essere utilizzato anche per eseguire richieste POST, PUT, DELETE, ecc., fornendo i dati del corpo della richiesta, gli header HTTP appropriati e così via.

Utilizzando queryItems, è possibile creare facilmente una stringa di query ben formata e aggiungerla all’URL:

var urlComponents =  URLComponents(string: "https://api.github.com/users")!
urlComponents.queryItems = [
    "since": "135"
].map { URLQueryItem(name: $0.key, value: $0.value)}
// Creazione dell'URL dall'URLComponents
if let url = urlComponents.url {
    print(url)
} else {
    print("Impossibile creare l'URL")
}

Output

https://api.github.com/users?since=135

Quando la richiesta GET va a buon fine nella maggior parte dei casi i dati restituiti sono in formato JSON. Utilizzando il metodo JSONDecoder() si possono convertire i dati JSON in un tipo di Swift.

Oltre a convertire i dati in tipi di Swift si può creare il proprio modello e attraverso il protocollo Codable convertire i dati.

// Creazione della richiesta
import Foundation

struct User: Codable {
    let login: String
    let id: Int
    let node_id: String
    let avatar_url: String
    let gravatar_id: String
    let url: String
    let html_url: String
    let followers_url: String
}
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
    // Decodifica del JSON in un oggetto User
    do {
    guard let data = data else { return }
    let responseData = try JSONDecoder().decode([User].self, from: data)
      print(responseData)
    } catch {
      print("Errore durante la decodifica del JSON:", error)
    }
}
// Avvio della richiesta
task.resume()

Output

[__lldb_expr_149.User(login: "simonjefford", id: 136, nodeId: "MDQ6VXNlcjEzNg==", avatarUrl: "https://avatars.githubusercontent.com/u/136?v=4", gravatarId: "", url: "https://api.github.com/users/simonjefford", htmlUrl: "https://github.com/simonjefford", followersUrl: "https://api.github.com/users/simonjefford/followers"), __lldb_expr_149.User(login: "josh", id: 137, nodeId: "MDQ6VXNlcjEzNw==", avatarUrl: "https://avatars.githubusercontent.com/u/137?v=4", gravatarId: "", url: "https://api.github.com/users/josh", htmlUrl: "https://github.com/josh", followersUrl: "https://api.github.com/users/josh/followers"), ...
]

Le CodingKeys in Swift vengono utilizzate per personalizzare la codifica e la decodifica delle proprietà della struttura. Permettono di mappare i nomi delle chiavi nel JSON ai nomi delle proprietà della struttura durante l'operazione di codifica e decodifica. Devono essere utilizzate quando il nome della proprietà Swift non coincide con la chiave JSON.

struct User2: Codable {
    let userID: Int
    let login: String
    let avatarUrl: String
    // Definizione delle chiavi custom per la codifica e la decodifica
    enum CodingKeys: String, CodingKey {
        case userID = "id"
        case username
        case avatarUrl = "avatar_url"
    }
}
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
    // Decodifica del JSON in un oggetto User
    do {
    guard let data = data else { return }
    let responseData = try JSONDecoder().decode([User].self, from: data)
      print(responseData)
    } catch {
      print("Errore durante la decodifica del JSON:", error)
    }
}
// Avvio della richiesta
task.resume()

Output

[__lldb_expr_159.User2(userID: 140, login: "jwreagor", avatarUrl: "https://avatars.githubusercontent.com/u/140?v=4"), __lldb_expr_159.User2(userID: 141, login: "technomancy", avatarUrl: "https://avatars.githubusercontent.com/u/141?v=4"), __lldb_expr_159.User2(userID: 142, login: "kenphused", avatarUrl: "https://avatars.githubusercontent.com/u/142?v=4"), __lldb_expr_159.User2(userID: 143, login: "rubyist", avatarUrl: "https://avatars.githubusercontent.com/u/143?v=4"), __lldb_expr_159.User2(userID: 144, login: "ogc", avatarUrl: "https://avatars.githubusercontent.com/u/144?v=4"), ...
]

Il tipo Error in Swift è un protocollo che le tipiche implementazioni del linguaggio di errore (come NSError e le Error personalizzate) devono adottare. Possiamo creare un enum personalizzato che adotti il protocollo Error per gestire gli errori in modo più specifico e personalizzato.

// Definizione di un enum personalizzato che rappresenta gli errori di un'applicazione
enum NetworkError: Error {
    case invalidURL
    case serverError(Int)
    case noData
    case decodingError
}