Errore nella funzione di supporto generica per la lettura di JSON da un URL
In uno dei miei progetti, voglio leggere JSON da diversi URL in diverse viste in diverse strutture, quindi ho deciso di scrivere una piccola, ma generica funzione di supporto.
Questa funzione dovrebbe essere chiamata ad es
Vista 1:
let call1 = Bundle.main.decode(iobrokerSection.self, from: "http://192.168.1.205:8087/get/javascript.0.Fahrzeiten.Dauer")
Vista 2:
let call2 = Bundle.main.decodeURL(iobrokerweather.self, from: "http://192.168.1.205:8087/get/javascript.0.Fahrzeiten.Weather")
e così via.
Per il primo esempio la struttura iobrokerSection
è
struct iobrokerNumberDataPoint: Codable {
var val: Int
var ack: Bool
var ts: Int
var q: Int
var from: String
var user: String
var lc: Int
var _id: String
var type: String
}
Ed ecco la mia funzione di aiuto
extension Bundle {
func decodeURL<T: Decodable>(_ type: T.Type, from urlString: String) -> T {
guard let url = URL(string: urlString) else {
fatalError("Placeholder for a good error message")
}
let request = URLRequest(url: url)
URLSession.shared.dataTask(with: request) { data, response, error in
guard let loaded = try? JSONDecoder().decode(T.self, from: data!) else {
fatalError("Placeholder for a good error message")
}
}.resume()
return loaded
}
}
Penso di capire perché ricevo il messaggio del compilatore "Impossibile convertire l'espressione di ritorno di tipo Bool per restituire il tipo T" a "ritorno caricato".
Ma non ho idea di come risolvere questo problema.
Qualcuno può darmi un suggerimento?
Risposte
Innanzitutto è la convenzione di denominazione di Swift denominare tutte le strutture, classi e protocolli che iniziano con una lettera maiuscola. Secondo, non puoi aspettare che un metodo asincrono finisca per restituire un valore. Devi aggiungere un gestore di completamento al tuo metodo. Terzo, non è necessario utilizzare un URLRequest se il tuo intento è solo quello di ottenere alcuni dati. Puoi semplicemente usare un URL e passare un URL al tuo metodo invece di una stringa. Quarto, non forzare a scartare i dati restituiti che potrebbero essere nulli. È necessario scartare in modo sicuro i dati facoltativi e in caso di errore passarli al gestore del completamento. Il tuo metodo di decodifica dovrebbe essere simile a questo:
extension Bundle {
func decode<T: Decodable>(_ type: T.Type, from url: URL, completion: @escaping (T?, Error?) -> Void) {
URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data else {
completion(nil, error)
return
}
do {
try completion(JSONDecoder().decode(T.self, from: data), nil)
} catch {
completion(nil, error)
}
}.resume()
}
}
Quando si chiama questo metodo è necessario ottenere il risultato asincrono all'interno della chiusura risultante:
struct IOBrokerNumberDataPoint: Codable {
var val: Int
var ack: Bool
var ts: Int
var q: Int
var from: String
var user: String
var lc: Int
var id: String
var type: String
enum CodingKeys: String, CodingKey {
case val, ack, ts, q, from, user, lc, id = "_id", type
}
}
let url = URL(string: "http://192.168.1.205:8087/get/javascript.0.Fahrzeiten.Dauer")!
Bundle.main.decode(IOBrokerNumberDataPoint.self, from: url) { brokerNumberDataPoint, error in
guard let brokerNumberDataPoint = brokerNumberDataPoint else {
print("error", error ?? "")
return
}
print("brokerNumberDataPoint", brokerNumberDataPoint)
// use brokerNumberDataPoint here
}
Un'altra opzione è utilizzare l' Result
enumerazione generica di Swift 5 .
extension Bundle {
func decode<T: Decodable>(from url: URL, completion: @escaping (Result<T, Error>) -> Void) {
URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data, error == nil else {
if let error = error { completion(.failure(error)) }
return
}
do {
try completion(.success(JSONDecoder().decode(T.self, from: data)))
} catch {
completion(.failure(error))
}
}.resume()
}
}
Utilizzo:
let url = URL(string: "http://192.168.1.205:8087/get/javascript.0.Fahrzeiten.Dauer")!
Bundle.main.decode(from: url) { (result: Result<IOBrokerNumberDataPoint, Error>) in
switch result {
case let .success(brokerNumberDataPoint):
print("brokerNumberDataPoint", brokerNumberDataPoint)
// use brokerNumberDataPoint here
case let .failure(error):
print("error:", error)
}
}