Ritornare un valore da URLSession in Swift

Mattepuffo's logo
Ritornare un valore da URLSession in Swift

Ritornare un valore da URLSession in Swift

Trovo che in molti linguaggi moderni tendano a complicare alcune cose inutilmente.

Ad esempio come ritornare un valore usando URLSession in Swift.

In questo articolo vediamo un esempio su come fare.

Partiamo dalla struct che rappresenta il JSON di riposta dalla API:

import Foundation

struct LoginResponse: Decodable {
    let res: String?
    let message: String?
    let jwt: String?
    let email: String?
    let userid: String?
    let ruolo: String?
    let iban: String?
    let expireAt: Int?
}

Nulla di particolare; passiamo al servizio che interroga l'API, che in questo caso è l'url a cui inviare username e password per l'autenticazione:

import Foundation
import CryptoKit

struct AuthService {
    
    func hashStr(value: String) -> String {
        let hashed = SHA512.hash(data: Data(value.utf8))
        let hp = hashed.compactMap { String(format: "%02x", $0) }.joined()
        return hp
    }
    
    func doLogin(email: String, password: String, userCompletionHandler: @escaping (LoginResponse?, Error?) -> Void) {
        let url = URL(string: "https:/www.sito.com/login")!
        
        let body: [String: String] = ["user": email, "password": hashStr(value: password)]
        let jsonBody = try? JSONSerialization.data(withJSONObject: body, options: .prettyPrinted)
        
        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.httpBody = jsonBody
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        request.addValue("application/json",forHTTPHeaderField: "Accept")
        
        let task = URLSession.shared.dataTask(with: request, completionHandler: { data, response, error in
            guard let data = data, let dataString = String(data: data, encoding: .utf8) else { return }
            
            let jsonData = dataString.data(using: .utf8)!
            let decoder = try? JSONDecoder().decode(LoginResponse.self, from: jsonData)
            userCompletionHandler(decoder, nil)
        })
        
        task.resume()
    }
    
}

In pratica il gioco viene svolto da un completionHandler (che potete chiamare come volete, nel mio caso userCompletionHandler).

Ovviamente si può affinare la cosa......

Poi la nostra schermata:

import SwiftUI

struct ContentView: View {
    
    @State var email: String = ""
    @State var password: String = ""
    
    var body: some View {
        VStack {
            Header()
            TxtEmail(email: $email)
            TxtPassword(password: $password)
            BtnLogin(email: $email, password: $password)
        }.padding()
    }
    
}

struct Header: View {
    var body: some View {
        return Text("BeMyBank")
            .font(.largeTitle)
            .fontWeight(.semibold)
            .padding(.bottom, 20)
    }
}

struct TxtEmail: View {
    
    @Binding var email: String
    
    var body: some View {
        return TextField("Email", text: $email)
            .padding()
            .background(Color(red: 239/255, green: 243/255, blue: 244/255))
            .overlay(
                RoundedRectangle(cornerRadius: 5)
                    .stroke(Color.gray.opacity(0.3), lineWidth: 1)
            )
            .frame(height: 40.0)
            .cornerRadius(5.0)
            .padding(.bottom, 20)
    }
}

struct TxtPassword: View {
    
    @Binding var password: String
    
    var body: some View {
        return SecureField("Password", text: $password)
            .padding()
            .background(Color(red: 239/255, green: 243/255, blue: 244/255))
            .overlay(
                RoundedRectangle(cornerRadius: 5)
                    .stroke(Color.gray.opacity(0.3), lineWidth: 1)
            )
            .frame(height: 40.0)
            .cornerRadius(5.0)
            .padding(.bottom, 20)
    }
}

struct BtnLogin: View {
    
    @Binding var email: String
    @Binding var password: String
    
    @State private var showAlert = false
    @State private var showingAlert = false
    @State private var errMessage: String? = ""
    
    var body: some View {
        return  Button(
            action: {
                if (!email.isEmpty && !password.isEmpty) {
                    let authService = AuthService()
                    authService.doLogin(email: email, password: password, userCompletionHandler: { lr, error in
                        if lr != nil {
                            let res: String? = lr?.res
                            let msg: String? = lr?.message
                            
                            if res == "ok" {
                                print("OK")
                            } else {
                                showingAlert = true
                                errMessage = msg
                            }
                        }
                    })
                } else {
                    showingAlert = true
                    errMessage = "Email e password obbligatori"
                }
            }
        ) {
            Text("LOGIN")
                .font(.headline)
                .foregroundColor(.white)
                .padding()
                .frame(width: 250, height: 40)
                .background(Color.blue)
                .cornerRadius(15.0)
        }
        .alert(isPresented: $showingAlert) {
            Alert(title: Text("ATTENZIONE"), message: Text(errMessage!), dismissButton: .default(Text("OK")))
        }
    }
    
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

class AlertService {
    static func showAlert(style: UIAlertController.Style, title: String?, message: String?, actions: [UIAlertAction] = [UIAlertAction(title: "Ok", style: .cancel, handler: nil)], completion: (() -> Swift.Void)? = nil) {
        let alert = UIAlertController(title: title, message: message, preferredStyle: style)
        for action in actions {
            alert.addAction(action)
        }
        UIApplication.shared.delegate?.window??.rootViewController?.present(alert, animated: true, completion: completion)
    }
}

Nel componente BtnLogin richiamiamo il service e recuperiamo il nostro completionHandler controllando quale sia la risposta.

Enjoy!


Condividi

Commentami!