Prima di tutto non caricare mai i dati in modo sincrono da un URL remoto , usa sempre metodi asincroni come URLSession.
"Qualsiasi" non ha membri in pedice
si verifica perché il compilatore non ha idea di quale tipo siano gli oggetti intermedi (ad esempio currentlyin ["currently"]!["temperature"]) e poiché si utilizzano tipi di raccolta Foundation come NSDictionaryil compilatore non ha idea del tipo.
Inoltre in Swift 3 è necessario informare il compilatore sul tipo di tutti gli oggetti sottoscritti.
Devi eseguire il cast del risultato della serializzazione JSON sul tipo effettivo.
Questo codice utilizza URLSessione esclusivamente Swift tipi nativi
let urlString = "https://api.forecast.io/forecast/apiKey/37.5673776,122.048951"
let url = URL(string: urlString)
URLSession.shared.dataTask(with:url!) { (data, response, error) in
if error != nil {
print(error)
} else {
do {
let parsedData = try JSONSerialization.jsonObject(with: data!) as! [String:Any]
let currentConditions = parsedData["currently"] as! [String:Any]
print(currentConditions)
let currentTemperatureF = currentConditions["temperature"] as! Double
print(currentTemperatureF)
} catch let error as NSError {
print(error)
}
}
}.resume()
Per stampare tutte le coppie chiave / valore di currentConditionste potresti scrivere
let currentConditions = parsedData["currently"] as! [String:Any]
for (key, value) in currentConditions {
print("\(key) - \(value) ")
}
Una nota riguardante jsonObject(with data:
Molti tutorial (sembrano tutti) suggeriscono .mutableContainerso .mutableLeavesopzioni che non hanno alcun senso in Swift. Le due opzioni sono opzioni legacy Objective-C per assegnare il risultato agli NSMutable...oggetti. In Swift, qualsiasi variable è modificabile per impostazione predefinita e passare una qualsiasi di queste opzioni e assegnare il risultato a una letcostante non ha alcun effetto. Inoltre, la maggior parte delle implementazioni non modifica mai comunque il JSON deserializzato.
L'unico (raro) opzione che è utile in Swift è .allowFragmentsche è necessario se se l'oggetto principale JSON potrebbe essere un tipo di valore ( String, Number, Boolo null) piuttosto che uno dei tipi di raccolta ( arrayo dictionary). Ma normalmente ometti il optionsparametro che significa Nessuna opzione .
================================================== =========================
Alcune considerazioni generali per analizzare JSON
JSON è un formato di testo ben organizzato. È molto facile leggere una stringa JSON. Leggi attentamente la stringa . Esistono solo sei tipi diversi: due tipi di raccolta e quattro tipi di valore.
I tipi di raccolta sono
- Array - JSON: oggetti tra parentesi quadre
[]- Swift: [Any]ma nella maggior parte dei casi[[String:Any]]
- Dizionario - JSON: oggetti tra parentesi graffe
{}- Swift:[String:Any]
I tipi di valore sono
- String - JSON: qualsiasi valore tra virgolette doppie
"Foo", pari "123"o "false"- Swift:String
- Number - JSON: valori numerici non tra virgolette
123o 123.0- Swift: IntoDouble
- Bool - JSON:
trueo false non tra virgolette doppie - Swift: trueofalse
- null - JSON:
null- Swift:NSNull
Secondo la specifica JSON, tutte le chiavi nei dizionari devono essere String.
Fondamentalmente si consiglia sempre di utilizzare attacchi opzionali per scartare gli optional in modo sicuro
Se l'oggetto radice è un dictionary ( {}), esegui il cast del tipo a[String:Any]
if let parsedData = try JSONSerialization.jsonObject(with: data!) as? [String:Any] { ...
e recuperare i valori tramite chiavi con ( OneOfSupportedJSONTypesè una raccolta JSON o un tipo di valore come descritto sopra).
if let foo = parsedData["foo"] as? OneOfSupportedJSONTypes {
print(foo)
}
Se l'oggetto radice è un array ( []), esegue il cast del tipo a[[String:Any]]
if let parsedData = try JSONSerialization.jsonObject(with: data!) as? [[String:Any]] { ...
e scorrere l'array con
for item in parsedData {
print(item)
}
Se hai bisogno di un elemento in un indice specifico, controlla anche se l'indice esiste
if let parsedData = try JSONSerialization.jsonObject(with: data!) as? [[String:Any]], parsedData.count > 2,
let item = parsedData[2] as? OneOfSupportedJSONTypes {
print(item)
}
}
Nel raro caso in cui JSON sia semplicemente uno dei tipi di valore, piuttosto che un tipo di raccolta, è necessario passare l' .allowFragmentsopzione e trasmettere il risultato al tipo di valore appropriato, ad esempio
if let parsedData = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? String { ...
Apple ha pubblicato un articolo completo nel blog Swift: Working with JSON in Swift
================================================== =========================
In Swift 4+ il Codableprotocollo fornisce un modo più conveniente per analizzare JSON direttamente in strutture / classi.
Ad esempio il dato campione JSON nella domanda (leggermente modificato)
let jsonString = """
{"icon": "partly-cloudy-night", "precipProbability": 0, "pressure": 1015.39, "humidity": 0.75, "precip_intensity": 0, "wind_speed": 6.04, "summary": "Partly Cloudy", "ozone": 321.13, "temperature": 49.45, "dew_point": 41.75, "apparent_temperature": 47, "wind_bearing": 332, "cloud_cover": 0.28, "time": 1480846460}
"""
può essere decodificato nella struttura Weather. I tipi Swift sono gli stessi descritti sopra. Ci sono alcune opzioni aggiuntive:
- Le stringhe che rappresentano un
URLpossono essere decodificate direttamente come URL.
- L'
timeintero può essere decodificato come Datecon il dateDecodingStrategy .secondsSince1970.
- Le chiavi JSON snaked_cased possono essere convertite in camelCase con l' estensione
keyDecodingStrategy .convertFromSnakeCase
struct Weather: Decodable {
let icon, summary: String
let pressure: Double, humidity, windSpeed : Double
let ozone, temperature, dewPoint, cloudCover: Double
let precipProbability, precipIntensity, apparentTemperature, windBearing : Int
let time: Date
}
let data = Data(jsonString.utf8)
do {
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .secondsSince1970
decoder.keyDecodingStrategy = .convertFromSnakeCase
let result = try decoder.decode(Weather.self, from: data)
print(result)
} catch {
print(error)
}
Altre fonti codificabili: