First of all, never download data synchronously from a remote URL ; always use asynchronous methods such as URLSession .
"Any" has no subscribers
this is because the compiler has no idea about the type of intermediate objects (for example, in currently in ["currently"]!["temperature"] NSDictionary ["currently"]!["temperature"] ), and since you are using Foundation collection types such as the NSDictionary compiler has no idea what type it is.
Additionally, in Swift 3, you need to tell the compiler about the type of all signed objects.
You must cast the result of the JSON serialization to the actual type.
This code uses URLSession and exclusively native Swift types
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()
To print all key / value pairs in currentConditions you can write
let currentConditions = parsedData["currently"] as! [String:Any] for (key, value) in currentConditions { print("\(key) - \(value) ") }
Note regarding jsonObject(with data :
Many (it seems, all) tutorials offer .mutableContainers or .mutableLeaves , which is completely pointless in Swift. These two parameters are obsolete Objective-C parameters for assigning a result to NSMutable... objects NSMutable... In Swift, any var iable is mutable by default and passing any of these options without assigning the result to let constant has no effect. In addition, most implementations never modify deserialized JSON.
The only (rare) option that is useful in Swift is .allowFragments which is necessary if the root JSON object can have a value type ( String , Number , Bool or null ), and not one of the collection types ( array or dictionary ). But usually they omit the options parameter, which means "No options."
=================================================== ===========================
Some general considerations for parsing JSON
JSON is a well-organized text format. It is very easy to read the JSON string. Read the line carefully . There are only six different types — two types of collections and four types of values.
Collection Types
- Array - JSON: objects in square brackets
[] - Swift: [Any] but in most cases [[String:Any]] - Dictionary - JSON: objects in braces
{} - Swift: [String:Any]
Value types
- String - JSON: any value in double quotes
"Foo" , even "123" or "false" - Swift: String - Number - JSON: numeric values not in double quotes
123 or 123.0 - Swift: Int or Double - Bool - JSON:
true or false not in double quotes - Swift: true or false - null - JSON:
null - Swift: NSNull
According to the JSON specification, all keys in dictionaries must be String .
In general, it is always recommended to use additional bindings to safely deploy additional
If the root object is a dictionary ( {} ), cast to [String:Any]
if let parsedData = try JSONSerialization.jsonObject(with: data!) as? [String:Any] { ...
and retrieves the values by key with ( OneOfSupportedJSONTypes is either a JSON collection or a value type, as described above.)
if let foo = parsedData["foo"] as? OneOfSupportedJSONTypes { print(foo) }
If the root object is an array ( [] ), cast to [[String:Any]]
if let parsedData = try JSONSerialization.jsonObject(with: data!) as? [[String:Any]] { ...
and iterate over the array with
for item in parsedData { print(item) }
If you need an item with a specific index, check also if the index exists
if let parsedData = try JSONSerialization.jsonObject(with: data!) as? [[String:Any]], parsedData.count > 2, let item = parsedData[2] as? OneOfSupportedJSONTypes { print(item) } }
In the rare case when JSON is just one of the value types, and not a collection type, you need to pass the .allowFragments parameter and cast the result to the corresponding value type, for example.
if let parsedData = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? String { ...
Apple has published a comprehensive Swift blog post: Working with JSON in Swift
=================================================== ===========================
The Swift 4+ on Codable protocol provides a more convenient way to parse JSON directly in / Class Structures.
For example, this example JSON in question (slightly modified)
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} """
can be decrypted into a Weather structure. Swift types are the same as described above. There are several additional options:
- Strings representing
URL can be decoded directly as URL . - The integer
time can be decoded as Date with dateDecodingStrategy .secondsSince1970 . - Snaked_cased JSON keys can be converted to camelCase using
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) }
Other Codable Sources: