Antes de tudo, nunca carregue dados de forma síncrona de uma URL remota , use sempre métodos assíncronos como URLSession.
'Any' não possui membros subscritos
ocorre porque o compilador não tem idéia de que tipo são os objetos intermediários (por exemplo, currentlyem ["currently"]!["temperature"]) e como você está usando tipos de coleção do Foundation, como NSDictionaryo compilador não tem idéia do tipo.
Além disso, no Swift 3, é necessário informar o compilador sobre o tipo de todos os objetos subscritos.
Você deve converter o resultado da serialização JSON no tipo real.
Este código usa URLSessione exclusivamente tipos nativos Swift
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()
Para imprimir todos os pares de chave / valor, currentConditionsvocê pode escrever
let currentConditions = parsedData["currently"] as! [String:Any]
for (key, value) in currentConditions {
print("\(key) - \(value) ")
}
Uma observação sobre jsonObject(with data:
Muitos (parece tudo) tutoriais sugerem .mutableContainersou .mutableLeavesopções que são completamente sem sentido no Swift. As duas opções são opções legadas de Objective-C para atribuir o resultado aos NSMutable...objetos. No Swift, qualquer varresponsável é mutável por padrão e passar qualquer uma dessas opções e atribuir o resultado a uma letconstante não tem efeito algum. Além disso, a maioria das implementações nunca está alterando o JSON desserializado de qualquer maneira.
A única opção (raro) que é útil no Swift é .allowFragmentsque é necessário se se o objecto raiz JSON poderia ser um tipo de valor ( String, Number, Boolou null) em vez de um dos tipos de recolha ( arrayou dictionary). Mas normalmente omita o optionsparâmetro que significa Sem opções .
==================================================== =========================
Algumas considerações gerais para analisar JSON
JSON é um formato de texto bem organizado. É muito fácil ler uma string JSON. Leia a string com atenção . Existem apenas seis tipos diferentes - dois tipos de coleção e quatro tipos de valor.
Os tipos de coleção são
- Matriz - JSON: objetos entre colchetes
[]- Swift: [Any]mas na maioria dos casos[[String:Any]]
- Dicionário - JSON: objetos em chaves
{}- Swift:[String:Any]
Os tipos de valor são
- String - JSON: qualquer valor entre aspas duplas
"Foo", par "123"ou "false"- Swift:String
- Número - JSON: valores numéricos que não estão entre aspas duplas
123ou 123.0- Swift: IntouDouble
- Bool - JSON:
trueou false não entre aspas duplas - Swift: trueoufalse
- null - JSON:
null- Rápido:NSNull
De acordo com a especificação JSON, todas as chaves nos dicionários devem ser String.
Basicamente, é sempre recomendável usar ligações opcionais para desembrulhar opcionais com segurança
Se o objeto raiz for um dicionário ( {}), converta o tipo para[String:Any]
if let parsedData = try JSONSerialization.jsonObject(with: data!) as? [String:Any] { ...
e recupere valores por chaves com ( OneOfSupportedJSONTypesé a coleção JSON ou o tipo de valor, conforme descrito acima.)
if let foo = parsedData["foo"] as? OneOfSupportedJSONTypes {
print(foo)
}
Se o objeto raiz for um array ( []), converta o tipo para[[String:Any]]
if let parsedData = try JSONSerialization.jsonObject(with: data!) as? [[String:Any]] { ...
e percorra a matriz com
for item in parsedData {
print(item)
}
Se você precisar de um item em um índice específico, verifique também se o índice existe
if let parsedData = try JSONSerialization.jsonObject(with: data!) as? [[String:Any]], parsedData.count > 2,
let item = parsedData[2] as? OneOfSupportedJSONTypes {
print(item)
}
}
Nos raros casos em que o JSON é simplesmente um dos tipos de valor - em vez de um tipo de coleção - você deve passar a .allowFragmentsopção e converter o resultado no tipo de valor apropriado, por exemplo
if let parsedData = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? String { ...
A Apple publicou um artigo abrangente no Blog Swift: Trabalhando com JSON no Swift
==================================================== =========================
No Swift 4+, o Codableprotocolo fornece uma maneira mais conveniente de analisar o JSON diretamente em estruturas / classes.
Por exemplo, a amostra JSON fornecida na pergunta (ligeiramente modificada)
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}
"""
pode ser decodificado na estrutura Weather. Os tipos Swift são os mesmos descritos acima. Existem algumas opções adicionais:
- Strings representando um
URLpodem ser decodificadas diretamente como URL.
- O
timenúmero inteiro pode ser decodificado como Datecom o dateDecodingStrategy .secondsSince1970.
- As chaves JSON snaked_cased podem ser convertidas em camelCase com o
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)
}
Outras fontes codificáveis: