0
votes

I'm attempting to follow a tutorial from Ray Wenderlich's website to learn to use Instruments. The sample code is written is Swift 2 I believe so before I'm allowed to run it, I have to migrate the code to the latest Swift version. I've gotten half-way through the conversion errors but I'm stumped on the following area of code:

class Flickr {

  let processingQueue = OperationQueue()

  func searchFlickrForTerm(_ searchTerm: String, completion : @escaping (_ results: FlickrSearchResults?, _ error : NSError?) -> Void){

    let searchURL = flickrSearchURLForSearchTerm(searchTerm)
    let searchRequest = URLRequest(url: searchURL)

    NSURLConnection.sendAsynchronousRequest(searchRequest, queue: processingQueue) {response, data, error in
      if error != nil {
        completion(nil,error as! NSError)
        return
      }

      var JSONError : NSError?


        let resultsDictionary = try JSONSerialization.jsonObject(with: data!, options: []) as? NSDictionary

      if JSONError != nil {
        completion(nil, JSONError)
        return
      }

      switch (resultsDictionary!["stat"] as! String) {
      case "ok":
        print("Results processed OK")
      case "fail":
        let APIError = NSError(domain: "FlickrSearch", code: 0, userInfo: [NSLocalizedFailureReasonErrorKey:resultsDictionary!["message"]!])
        completion(results: nil, error: APIError)
        return
      default:
        let APIError = NSError(domain: "FlickrSearch", code: 0, userInfo: [NSLocalizedFailureReasonErrorKey:"Unknown API response"])
        completion(nil, APIError)
        return
      }

      let photosContainer = resultsDictionary!["photos"] as! NSDictionary
      let photosReceived = photosContainer["photo"] as! [NSDictionary]

      let flickrPhotos : [FlickrPhoto] = photosReceived.map {
        photoDictionary in

        let photoID = photoDictionary["id"] as? String ?? ""
        let title = photoDictionary["title"] as? String ?? ""
        let farm = photoDictionary["farm"] as? Int ?? 0
        let server = photoDictionary["server"] as? String ?? ""
        let secret = photoDictionary["secret"] as? String ?? ""

        let flickrPhoto = FlickrPhoto(photoID: photoID, title: title, farm: farm, server: server, secret: secret)

        return flickrPhoto
      }

      DispatchQueue.main.async(execute: {
        completion(FlickrSearchResults(searchTerm: searchTerm, searchResults: flickrPhotos), nil)
      })
    }
  }

  fileprivate func flickrSearchURLForSearchTerm(_ searchTerm:String) -> URL {
    let escapedTerm = searchTerm.addingPercentEscapes(using: String.Encoding.utf8)!
    let URLString = "https://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=\(apiKey)&text=\(escapedTerm)&per_page=30&format=json&nojsoncallback=1"
    return URL(string: URLString)!
  }


}

I get the error on the following line of code:

NSURLConnection.sendAsynchronousRequest(searchRequest, queue: processingQueue) {response, data, error in
      if error != nil {
        completion(nil,error as! NSError)
        return
      }

I'm a little confused on how this should be amended using do, try, catch so any help would be appreciated just so I can get the app running to play around with Instruments.

Here is a link to the tutorial: https://www.raywenderlich.com/97886/instruments-tutorial-with-swift-getting-started

1
The problem is that you have try which can throw errors, but you're not catching it, so it's assuming that the closure would therefore need to allow throwing of error, which it doesn't. Just do-catch the error thrown by try in the closure or use try?, which doesn't throw errors. - Rob

1 Answers

1
votes

replace your Flickr class with this.

class Flickr {

  let processingQueue = OperationQueue()

  func searchFlickrForTerm(_ searchTerm: String, completion : @escaping (_ results: FlickrSearchResults?, _ error : NSError?) -> Void){

let searchURL = flickrSearchURLForSearchTerm(searchTerm)
let searchRequest = URLRequest(url: searchURL)

NSURLConnection.sendAsynchronousRequest(searchRequest, queue: processingQueue) {response, data, error in
    guard let data = data, error == nil else {
        completion(nil, error as NSError?)
        return
    }

    guard let jsonObject = try? JSONSerialization.jsonObject(with: data,
                                                                    options: JSONSerialization.ReadingOptions(rawValue: 0)),
    let resultsDictionary = jsonObject as? Dictionary<String, Any>
        else
    {
        return
    }


    switch (resultsDictionary["stat"] as! String) {
    case "ok":
        print("Results processed OK")
    case "fail":
        let APIError = NSError(domain: "FlickrSearch", code: 0, userInfo: [NSLocalizedFailureReasonErrorKey:resultsDictionary["message"]!])
        completion(nil, APIError)
        return
    default:
        let APIError = NSError(domain: "FlickrSearch", code: 0, userInfo: [NSLocalizedFailureReasonErrorKey:"Unknown API response"])
        completion(nil, APIError)
        return
    }

    let photosContainer = resultsDictionary["photos"] as! NSDictionary
    let photosReceived = photosContainer["photo"] as! [NSDictionary]

    let flickrPhotos : [FlickrPhoto] = photosReceived.map {
        photoDictionary in

        let photoID = photoDictionary["id"] as? String ?? ""
        let title = photoDictionary["title"] as? String ?? ""
        let farm = photoDictionary["farm"] as? Int ?? 0
        let server = photoDictionary["server"] as? String ?? ""
        let secret = photoDictionary["secret"] as? String ?? ""

        let flickrPhoto = FlickrPhoto(photoID: photoID, title: title, farm: farm, server: server, secret: secret)

        return flickrPhoto
    }

    DispatchQueue.main.async(execute: {
        completion(FlickrSearchResults(searchTerm: searchTerm, searchResults: flickrPhotos), nil)
    })
}
  }

  fileprivate func flickrSearchURLForSearchTerm(_ searchTerm:String) -> URL {
let escapedTerm = searchTerm.addingPercentEscapes(using: String.Encoding.utf8)!
let URLString = "https://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=\(apiKey)&text=\(escapedTerm)&per_page=30&format=json&nojsoncallback=1"
return URL(string: URLString)!
  }


}

Sorry spacing is all messed up but the issue is you're not doing error handling correctly. When you do JSONSerialization it can throw so you have to wrap it in a do catch block or you can use ! to ignore the throw and crash if it throws an error or ? to return nil if it fails.