How to cast a subscript member to NSArray Swift 3 - json

Returns an error message: Type ‘Any’ has no subscript members in last 2 lines of code. I can^t solve this for NSArray in Swift 3: Any thoughts?
let weatherDictionary: NSDictionary = (try! JSONSerialization.jsonObject(with: dataObject!, options: [])) as! NSDictionary
_ = try JSONSerialization.jsonObject(with: dataObject!, options: .allowFragments) as! [String:Any]
then
struct Weekly{
var dayZeroTemperatureMax: Int
var dayZeroTemperatureMin: Int
init (weatherDictionary: NSDictionary) {
let weeklyWeather = weatherDictionary["daily"] as! NSDictionary
let weeklyForcast = weeklyWeather["data"] as! NSArray
//DAY ZERO
dayZeroTemperatureMax = weeklyForcast[0]["temperatureMax"] as! Int
dayZeroTemperatureMin = weeklyForcast[0]["temperatureMin"] as! Int
}
}

Try changing your code to this :
let weeklyWeather = weatherDictionary["daily"] as! [String: AnyObject]
let weeklyForcast = weeklyWeather["data"] as! [[String: Any]]
in this same project you might need to change this too (in WeatherAlerts) :
init (weatherDictionary: [String: Any]) {
if let weatheralerts = (weatherDictionary["alerts"] as? [[String: Any]]) {
userAlert = weatheralerts[0]["title"] as! String
} else {
userAlert = ""
}
}
I don't really know the reason to change this , if anyone have an explanation please comment here.
but in the same project I had a "fatal error: unexpectedly found nil while unwrapping an Optional value" error here , do you have any idea? what change about URLSessionDownloadTask ?

Related

Type '[String:Any]' has no subscript members in Swift

I am trying to get information from a json result and append and access certain key values. However, I am getting the error "Type '[String:Any]' has no subscript members.
let json = try? JSONSerialization.jsonObject(with: data!, options: []) as! [String: Any]
for case let studentsInfo in json["results"] {
if let studentInfo = StudentResults(json: result) {
let name = firstName + " " + lastName
StudentsResults.append(name)
}
This is also the struct that I have placed in an extension.
struct StudentResults{
let firstName: String
let lastName: String
let latitude: Double
let longitude: Double
let mapString:String
let mediaURL: String
let objectID:String
let uniqueKey: String
let updatedAt: String
}
convenience init?(json: [String: Any]) {
guard let firstName = json["firstName"] as? String,
let lastName = json["lastName"] as? String
else {
return nil
}
return nil
This is also my GitHub page for this project if you would like to take a look at it
https://github.com/SteveBurgos95/UdacityMapProject
It appears to me that the problem is with this line of code:
let json = try? JSONSerialization.jsonObject(with: data!, options: []) as! [String: Any]
Whilst you are force casting it here as! [String: Any], the try? is giving you an optional value.
You have two options. You can be a crazy person and change try? to try!, or you can take the safer approach and wrap the code like so:
do {
guard let json = try JSONSerialization.jsonObject(with: data!, options: []) as [String: Any] else { return }
…
} catch {
// HANDLE ERROR
}

Swift 3 JSON Bug?

I can't seem to compile my code no matter how much I fiddle with it. I think I need another perspective.
At "let image = data["images"] as! [String : AnyObject]"
xcode keeps telling me "Cannot subscript a value of type '[[String : AnyObject]]' with an index of type 'String'"
func retreiveInstagramMedia() {
let token = user?.token
let urlString = "https://api.instagram.com/v1/users/self/media/recent/?access_token=\(token!)"
let url = URL(string: urlString)
URLSession.shared.dataTask(with: url!, completionHandler: {
(data, response, error) in
if(error != nil){
print("error")
}else{
do{
let json = try JSONSerialization.jsonObject(with: data!, options:.allowFragments) as! [String : AnyObject]
if let data = json["data"] as? [[String : AnyObject]] {
let image = data["images"] as! [String : AnyObject]
let standardResolution = image["standard_resolution"] as! [String : AnyObject]
let url = standardResolution["url"] as! String
print(url)
}
OperationQueue.main.addOperation({
self.tableView.reloadData()
})
}catch let error as NSError{
print(error)
}
}
}).resume()
}
As Martin said, your data is an array of dictionaries. So iterate through those dictionaries:
if let arrayOfData = json["data"] as? [[String : Any]] {
for individualData in arrayOfData {
if let image = individualData["images"] as? [String : Any],
let standardResolution = image["standard_resolution"] as? [String : Any],
let url = standardResolution["url"] as? String {
// now do something with URL
print(url)
}
}
}
I'd suggest you avoid using ! to force cast/unwrap. When dealing with data from remote sources, you should more gracefully handle situations where one of these subscripts fail to retrieve what you expect they would.
First of all in Swift 3 a JSON dictionary is [String:Any]
Your mistake (developer bug) is to use the array data as the index variable of the loop.
Replace the underscore with anItem and get the image with anItem["images"]
let json = try JSONSerialization.jsonObject(with: data!) as! [String : Any]
if let data = json["data"] as? [[String : Any]] {
for anItem in data {
let image = anItem["images"] as! [String : Any]
let standardResolution = image["standard_resolution"] as! [String : Any]
let url = standardResolution["url"] as! String
print(url)
}
}
Note: The key images implies multiple items so the value could be also an array.

Ambiguous use go 'subscript'

I am trying to get data that has been encoded as json in a php script.
My code:
func getJson(completion: #escaping (Array<CarPark>) -> Void) {
activityIndicatior.center = self.view.center
activityIndicatior.hidesWhenStopped = true
activityIndicatior.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.gray
view.addSubview(activityIndicatior)
self.activityIndicatior.startAnimating()
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error != nil
{
print("ERROR")
}
else
{
if let content = data
{
do{
let myJson = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
for index in 0..<myJson.count {
if let entry = myJson[index] as? NSDictionary{
let name = entry["Name"] as! String
let longitude = Double(entry["Longitude"] as! String)
let latitude = Double(entry["Latitude"] as! String)
let quiet = Int(entry["Quiet"] as! String)
let moderate = Int(entry["Moderate"] as! String)
let busy = Int(entry["Busy"] as! String)
let coordinate = CLLocationCoordinate2D( latitude: latitude!, longitude: longitude!)
print("coordinate lat is : \(coordinate.latitude)")
print("coordinate long is : \(coordinate.longitude)")
print("coordinate full is: \(coordinate)")
let tempPark = CarPark(name: name, latitude: latitude!, longitude: longitude!, quiet: quiet!, moderate: moderate!, busy: busy!, coordinate: coordinate, level: "Nil")
let level = tempPark.calcAvailability()
tempPark.level = level
print("Availability is \(tempPark.level)")
self.tempCarParks.append(tempPark)
// print("amount of parks: \(self.carParks.count)")
print("name of parks in array: \(self.tempCarParks[index].name)")
print("Availability is \(tempPark.level)")
}
}
completion(self.tempCarParks)
}
catch
{
print("Error")
}
}
}
}
task.resume()
}
I am getting an error that says 'Ambiguous use of subscript' at the line:
if let entry = myJson[index] as? NSDictionary{
How can I fix this?
Since you know myJson is an array why do you cast the object to unspecified AnyObject?
That causes the error, the compiler needs to know the concrete types of all subscripted objects. Help the compiler then the compiler helps you:
let myJson = try JSONSerialization.jsonObject(with: content) as! [[String:Any]]
for entry in myJson { // don't use old-fashioned index based loops
let name = entry["Name"] as! String
...
.mutableContainers is completely useless in Swift.

Strange behaviour of optionals in Swift 3

I have experienced a strange behaviour when parsing JSON data using Swift 3.
do {
let json = try JSONSerialization.jsonObject(with: data!, options: []) as! NSDictionary
let items:[AnyObject] = (json["items"] as? [AnyObject])!
for item in items {
let id:String = item["id"] as! String
print("ID: \(id)")
let info = item["volumeInfo"] as AnyObject
print(info)
let title = info["title"]
print(title)
}
} catch {
print("error thrown")
}
This produces the following output. Notice that info is an optional but if I try to unwrap it it states it is not an optional! The script crashes on let title = info["title"] As a result I can't access the title key. This behaviour has changed since Swift 2.
ID: lbvUD6LUyV8C
Optional({
publishedDate = 2002;
publisher = "Sams Publishing";
title = "Java Deployment with JNLP and WebStart";
})
You can do something like:
do {
let json = try JSONSerialization.jsonObject(with: data!) as! [String: Any]
let items = json["items"] as! [[String: Any]]
for item in items {
let id = item["id"] as! String
let info = item["volumeInfo"] as! [String: Any]
let title = info["title"] as! String
print(id)
print(info)
print(title)
}
} catch {
print("error thrown: \(error)")
}
I might suggest excising the code of the ! forced unwrapping (if the JSON was not in the form you expected, do you really want this to crash?), but hopefully this illustrates the basic idea.
The runtime type of info is Optional<Something>, but the compile time type (as you explicitly cast it) is AnyObject. How is the compiler supposed to know that the AnyObject will happen to be an Optional<Something> unless you tell it (in the form of a cast)?
Try:
let info = item["volumeInfo"] as AnyObject?
Now the compiler knows it's an Optional<AnyObject>, so you can unwrap it.
In Swift 3 the compiler must know the types of all subscripted objects if it's an array or dictionary. AnyObject – which has been changed to Any in Swift 3 – is not sufficient.
Since you know that the value for key volumeInfo is a dictionary cast it accordingly preferably using optional bindings
let info = item["volumeInfo"] as? [String:Any] {
print(info)
let title = info["title"] as! String
print(title)
}
This should do:
guard let json = try? JSONSerialization.jsonObject(with: data!, options: []) as! [String: AnyObject],
let items = json["items"] as! Array<AnyObject>? else {
return
}
for item in items {
let id = item["id"] as! String
if let info = item["volumeInfo"] as? [String: AnyObject] {
let title = info["title"] as! String
} else {
// do something
}
}

Swift Errors Thrown from here are not handled

I have had my code working in Xcode 6 but since I got Xcode 7 I cannot figure out how to fix this. The let jsonresult line has an error that says Error thrown from here are not handled. Code is below:
func connectionDidFinishLoading(connection: NSURLConnection!) {
let jsonresult:NSDictionary = try NSJSONSerialization.JSONObjectWithData(self.bytes, options: NSJSONReadingOptions.MutableContainers) as! NSDictionary
print(jsonresult)
let number:Int = jsonresult["count"] as! Int
print(number)
numberElements = number
let results: NSDictionary = jsonresult["results"] as! NSDictionary
let collection1: NSArray = results["collection1"] as! NSArray
Thanks
If you look at the definition of JSONObjectWithData method in swift 2 it throws error.
class func JSONObjectWithData(data: NSData, options opt: NSJSONReadingOptions) throws -> AnyObject
In swift 2 if some function throws an error you have to handle it with do-try-catch block
Here is how it works
func connectionDidFinishLoading(connection: NSURLConnection!) {
do {
let jsonresult:NSDictionary = try NSJSONSerialization.JSONObjectWithData(self.bytes, options: NSJSONReadingOptions.MutableContainers) as! NSDictionary
print(jsonresult)
let number:Int = jsonresult["count"] as! Int
print(number)
numberElements = number
let results: NSDictionary = jsonresult["results"] as! NSDictionary
let collection1: NSArray = results["collection1"] as! NSArray
} catch {
// handle error
}
}
Or if you don't want handle error you can force it with try! keyword.
let jsonresult:NSDictionary = try! NSJSONSerialization.JSONObjectWithData(self.bytes, options: NSJSONReadingOptions.MutableContainers) as! NSDictionary
print(jsonresult)
As with other keywords that ends ! this is a risky operation. If there is an error your program will crash.