12

I'm trying to create a simple weather app that grabs the user's location and shows simple weather data using the Google Maps api. Everything is working, except for this part where I take the JSON and get the address.

func getAddressForLatLng(latitude: String, longitude: String) {
    let url = NSURL(string: "\(baseUrl)latlng=\(latitude),\(longitude)&key=\(apikey)")
    let data = NSData(contentsOf: url! as URL)
    let json = try! JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments) as! Dictionary
    if let result = json["results"] as? Dictionary {
        if let address = result[0]["address_components"] as? Array {
            let number = address[0]["short_name"] as! String
            let street = address[1]["short_name"] as! String
            let city = address[2]["short_name"] as! String
            let state = address[4]["short_name"] as! String
            let zip = address[6]["short_name"] as! String
            weatherDisplay.text = "\(city),\(state)"
        }
    }
}

On the line:

let json = try! JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments) as! Dictionary 

I get this error:

Cannot invoke 'jsonObject' with an argument list of type '(with: NSData?, options: JSONSerialization.ReadingOptions)'

What am I doing wrong?

Caleb Kleveter
  • 11,170
  • 8
  • 62
  • 92
Eric Phillips
  • 487
  • 1
  • 6
  • 20
  • 5
    No need to use NSData (or NSURL) here at all. `let url = URL(string: ...)`, `let data = Data(contentsOf: ...)` – Martin R Jan 16 '17 at 18:50
  • The problem is that you're passing an optional to `JSONSerialization`. You can force unwrap data, e.g. `JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.allowFragments) as! Dictionary` – Luke Van In Jan 16 '17 at 18:50
  • 2
    Note that using `NSData(contentsOf: url! as URL)` (or `Data(contentsOf:)` in Swift 3) is a bad idea. That's a synchronous call, and will freeze the UI until it complete. You should really rewrite this to use NSURLSession. – Duncan C Jan 16 '17 at 19:00
  • 2
    I'm having a hard time understanding why this was voted down. Seems like a legitimate question well within the scope of the set and associated tags. – Caleb Jan 16 '17 at 19:54
  • @LukeVanIn Why would you be using allowFragments? And you know that force unwrapping will crash if the download failed? – gnasher729 Mar 02 '18 at 17:42
  • gnasher729 This question has lots of problems and my comment was intended to address the error, rather than try correct all the other problems. The problem is not that `NSData` needs to be converted to `Data`, but rather that the object needs to be unwrapped. Force unwrapping the data will crash as you correctly pointed out, although the code would also have crashed in the other places with forced unwrapping. I used `allowFragments` because that is what the question used. It is not usually necessary as most JSON will be an object or an array. – Luke Van In Mar 04 '18 at 13:38

3 Answers3

29
let nsdata = NSData()    
let data = Data(referencing: nsdata)

https://developer.apple.com/documentation/foundation/data/3126627-init

agandi
  • 561
  • 7
  • 15
9

You need to change a couple things. First, you are using NSData. You should be using the Swift type Data. To convert from NSData? to Data?, just add as Data? to the end of the variable declaration.

Also, Your type is optional, but you can't pass in an optional type, so you need to unwrap it (using, in this example, if let data = data { /* stuff here */}):

func getAddressForLatLng(latitude: String, longitude: String) {
    let url = NSURL(string: "\(baseUrl)latlng=\(latitude),\(longitude)&key=\(apikey)")
    let data = NSData(contentsOf: url! as URL) as Data? // <==== Added 'as Data?'
    if let data = data { // <====== Added 'if let'
        let json = try! JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments) as! Dictionary
        if let result = json["results"] as? Dictionary {
            if let address = result[0]["address_components"] as? Array {
                let number = address[0]["short_name"] as! String
                let street = address[1]["short_name"] as! String
                let city = address[2]["short_name"] as! String
                let state = address[4]["short_name"] as! String
                let zip = address[6]["short_name"] as! String
                weatherDisplay.text = "\(city),\(state)"
            }
        }
    }
}

Update:

Another thing you need to change is:

let json = try! JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments) as! Dictionary

When you cast to the type Dictionary, the compiler does not know what you are talking about because Dictionary is a generic type. So you need to cast to Dictionary<String, AnyObject> or [String: AnyObject] (They are the same).

Caleb Kleveter
  • 11,170
  • 8
  • 62
  • 92
  • Thanks for the quick response! Now it's saying (on the let = json line) "Generic Parameter 'key' could not be inferred in cast to 'Dictionary<_,_>" – Eric Phillips Jan 16 '17 at 20:31
  • @EricPhillips Updated my answer! – Caleb Kleveter Jan 16 '17 at 20:43
  • now, on the line `if let address = result[0]["address_components"] as? Array {`, it says "Ambiguous reference to member 'subscript'". Is there something else I should add/change? – Eric Phillips Jan 16 '17 at 21:32
  • @EricPhillips Not sure. I do know that the `Array` type is like the `Dictionary` type. You need to add what type the array is, i.e. `Array`. If you have more questions, I would suggest Googling then asking a new question on SO (Stack Overflow). – Caleb Kleveter Jan 16 '17 at 23:05
1

Just cast: let newData = nsData as Data

  • Isn't that what `let data = NSData(contentsOf: url! as URL) as Data? // <==== Added 'as Data?'` does in [the accepted answer](https://stackoverflow.com/a/41684092/1364007)? – Wai Ha Lee Nov 05 '20 at 08:22