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!
swift ios json urlsession completionhandler
Commentami!