Swift: Fetching photos from Flickr always fetch the same photos - ios

I am writing an app for a Udacity portfolio project which would fetch photos from Flickr and display in a collectionView. When a button is pressed, it will refresh the photos in the collectionView. However I am unable to get new sets of photos despite the fact that the search parameters have different page number after every call. My code as follows:
func getPhotosURLFromFlickr(_ lat: AnyObject, lon: AnyObject, _ completionHandlerForGetPhotosURL: #escaping (_ imageURL: [String]?, _ error: NSError?) -> Void) {
taskForGetPagesFromFlickr(lat, lon: lon) { (parameters, error) in
if let error = error {
completionHandlerForGetPhotosURL(nil, error as NSError)
} else {
if let parameters = parameters {
print(parameters)
self.taskForGetPhotos(parameters, { (imageURLArray, error) in
if let error = error {
completionHandlerForGetPhotosURL(nil, error as NSError)
} else {
if let imageURLArray = imageURLArray {
completionHandlerForGetPhotosURL(imageURLArray, nil)
}
}
})
}
}
}
}
func taskForGetPagesFromFlickr(_ lat: AnyObject, lon: AnyObject, _ completionHandlerForGetPhotosURL: #escaping (_ parameters: [String: AnyObject]?, _ error: NSError?) -> Void) {
let parameters = [
Constants.ParametersKey.Method: Constants.Methods.PhotoSearch as AnyObject,
Constants.ParametersKey.FlickrAPIKey : Constants.APIInfo.APIKey as AnyObject,
Constants.ParametersKey.Format: Constants.ParametersValues.JSON as AnyObject,
Constants.ParametersKey.NoJSONCallback: Constants.ParametersValues.DisableJSONCallback as AnyObject,
Constants.ParametersKey.PerPage: Constants.ParametersValues.Fifteen as AnyObject,
Constants.ParametersKey.Extras: Constants.ParametersValues.MediumURL as AnyObject]
var parametersWithCoord = parameters
parametersWithCoord[Constants.ParametersKey.lat] = lat
parametersWithCoord[Constants.ParametersKey.lon] = lon
let url = flickrURLFromParameters(parametersWithCoord)
let request = NSMutableURLRequest(url: url)
let task = session.dataTask(with: request as URLRequest) { (data, response, error) in
if let error = error {
completionHandlerForGetPhotosURL(nil, error as NSError)
} else {
self.convertDataWithCompletionHandler(data!, completionHandlerForConvertData: { (results, error) in
if let error = error {
completionHandlerForGetPhotosURL(nil, error as NSError)
} else {
// print(results)
guard let photosDict = results?["photos"] as? [String: AnyObject] else {
let userInfo = [NSLocalizedDescriptionKey : "NoPhotosFound"]
completionHandlerForGetPhotosURL(nil, NSError(domain: "NoPhotosFound", code: 1, userInfo: userInfo))
return
}
guard let pages = photosDict[Constants.ParametersKey.Pages] as? Int else {
let userInfo = [NSLocalizedDescriptionKey : "NoPagesFound"]
completionHandlerForGetPhotosURL(nil, NSError(domain: "NoPagesFound", code: 1, userInfo: userInfo))
return
}
let randomPageIndex = Int(arc4random_uniform(UInt32(pages)))
var searchParameters = parametersWithCoord
searchParameters[Constants.ParametersKey.Page] = randomPageIndex as AnyObject?
print("Number of pages: \(pages)")
print("Random page index: \(randomPageIndex)")
completionHandlerForGetPhotosURL(searchParameters, nil)
}
})
}
}
task.resume()
}
func taskForGetPhotos(_ parameters: [String: AnyObject], _ completionHandlerForGetPhotosURL: #escaping (_ imageURL: [String]?, _ error: NSError?) -> Void) {
let url = flickrURLFromParameters(parameters)
let request = NSMutableURLRequest(url: url)
print(url)
let task = session.dataTask(with: request as URLRequest) { (data, response, error) in
if let error = error {
completionHandlerForGetPhotosURL(nil, error as NSError)
} else {
self.convertDataWithCompletionHandler(data!, completionHandlerForConvertData: { (results, error) in
if let error = error {
completionHandlerForGetPhotosURL(nil, error as NSError)
} else {
// print(results)
guard let photosDict = results?["photos"] as? [String: AnyObject] else {
let userInfo = [NSLocalizedDescriptionKey : "NoPhotosFound"]
completionHandlerForGetPhotosURL(nil, NSError(domain: "NoPhotosFound", code: 1, userInfo: userInfo))
return
}
guard let photosArray = photosDict["photo"] as? [[String: AnyObject]] else {
let userInfo = [NSLocalizedDescriptionKey : "NoPhotosArrayFound"]
completionHandlerForGetPhotosURL(nil, NSError(domain: "NoPhotosArray", code: 1, userInfo: userInfo))
return
}
var imageURLArray: [String] = []
if photosArray.count != 0 {
for pics in photosArray {
guard let imageURLString = pics[Constants.ParametersValues.MediumURL] as? String else {
print("NoImageURLString Found")
return
}
imageURLArray.append(imageURLString)
}
}
print("ImageURLArray : \(imageURLArray)")
completionHandlerForGetPhotosURL(imageURLArray, nil)
}
})
}
}
task.resume()
}
When my refresh button is tapped, the above set of networking code runs, and if I tap the button twice, my print statements are as such:
FETCHING NEW COLLECTION...
Number of items 0 Number of pages: 5712
Random page index: 490
https://api.flickr.com/services/rest?page=490&method=flickr.photos.search&format=json&lon=103.7940514843148&api_key=APIKEY&per_page=15&lat=1.411935988414726&extras=url_m&nojsoncallback=1
ImageURLArray :
["https://farm3.staticflickr.com/2059/32542810620_37312d07c9.jpg",
"https://farm3.staticflickr.com/2330/32102051403_46e30a5eec.jpg",
"https://farm3.staticflickr.com/2104/32529883860_17558a0acf.jpg",
"https://farm3.staticflickr.com/2595/32778162441_db4a98d3cd.jpg",
"https://farm3.staticflickr.com/2466/32087213853_257910d32d.jpg",
"https://farm3.staticflickr.com/2029/32891916665_2d2d177e71.jpg",
"https://farm4.staticflickr.com/3685/32878992915_38baaf513e.jpg",
"https://farm1.staticflickr.com/631/32062787803_ed58defea5.jpg",
"https://farm3.staticflickr.com/2332/32873171215_c807db5364.jpg",
"https://farm3.staticflickr.com/2788/32825584606_7d2bff507c.jpg",
"https://farm3.staticflickr.com/2479/32052120503_1317f70f1a.jpg",
"https://farm3.staticflickr.com/2909/32023922664_d276f52369.jpg",
"https://farm1.staticflickr.com/376/32023912434_9b89fc3d7b.jpg",
"https://farm1.staticflickr.com/455/32018845514_22681384ae.jpg",
"https://farm3.staticflickr.com/2833/32734093131_0e8da333f4.jpg"]
FETCHING NEW COLLECTION...
Number of items 0 Number of pages: 5712
Random page index: 5383
https://api.flickr.com/services/rest?page=5383&method=flickr.photos.search&format=json&lon=103.7940514843148&api_key=APIKEY&per_page=15&lat=1.411935988414726&extras=url_m&nojsoncallback=1
ImageURLArray :
["https://farm3.staticflickr.com/2059/32542810620_37312d07c9.jpg",
"https://farm3.staticflickr.com/2330/32102051403_46e30a5eec.jpg",
"https://farm3.staticflickr.com/2104/32529883860_17558a0acf.jpg",
"https://farm3.staticflickr.com/2595/32778162441_db4a98d3cd.jpg",
"https://farm3.staticflickr.com/2466/32087213853_257910d32d.jpg",
"https://farm3.staticflickr.com/2029/32891916665_2d2d177e71.jpg",
"https://farm4.staticflickr.com/3685/32878992915_38baaf513e.jpg",
"https://farm1.staticflickr.com/631/32062787803_ed58defea5.jpg",
"https://farm3.staticflickr.com/2332/32873171215_c807db5364.jpg",
"https://farm3.staticflickr.com/2788/32825584606_7d2bff507c.jpg",
"https://farm3.staticflickr.com/2479/32052120503_1317f70f1a.jpg",
"https://farm3.staticflickr.com/2909/32023922664_d276f52369.jpg",
"https://farm1.staticflickr.com/376/32023912434_9b89fc3d7b.jpg",
"https://farm1.staticflickr.com/455/32018845514_22681384ae.jpg",
"https://farm3.staticflickr.com/2833/32734093131_0e8da333f4.jpg"]
You would realise that despite that the search page is different for both times, but the imageURLArray is actually the same. I can't seem to identify the reason why.
Some help is much appreciated pls, thanks!

Somehow I manage to solve my problem. I limit the randomPageIndex with a maximum number of 100 and it successfully refreshes the page.
Perhaps Flickr has some maximum search pages which I may not be aware of.

Related

After URLSession.shared.dataTask its either not returning error or success

After URLSession.shared.dataTask it's not either returning error or success.
The completion handler is not getting called. How can I check or how can I proceed further. There is no error the app is working as such, but without data on the screen which is displayed.
func getPromotionsData() {
ConnectionManager.sharedInstance()?.getPromotions(PROMOTIONS, withCompletion: {
result, error in
if let result = result {
print("result: \(result)")
}
var arrPromotions: [Any] = []
if let object = result?["promotions"] as? [Any] {
arrPromotions = object
}
self.dataSource = []
if let arrPromotions = arrPromotions as? [AnyHashable] {
self.dataSource = arrPromotions
}
DispatchQueue.main.async(execute: {
self.collectionView.reloadData()
})
})
}
func getPromotions(_ path: String?, withCompletion completion: #escaping (_ result: [AnyHashable : Any]?, _ error: Error?) -> Void) {
let strPath = "/\(API)/\(path ?? "").json"
let url = strPath.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)
makeRequest(BASE_URL, path: url, httpMethod: GET_METHOD, httpBody: nil, completion: completion)
}
func makeRequest(_ url: String?, path: String?, httpMethod: String?, httpBody httpBoday: Data?, completion: #escaping (_ result: [AnyHashable : Any]?, _ error: Error?) -> Void) {
let headers = [
"cache-control": "no-cache",
"Authorization": "Token f491fbe3ec54034d51e141e28aaee87d47bb7e74"
]
var request: URLRequest? = nil
if let url = URL(string: "\(url ?? "")\(path ?? "")") {
request = URLRequest(url: url, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10.0)
}
request?.httpMethod = httpMethod ?? ""
request?.allHTTPHeaderFields = headers
let configuration = URLSessionConfiguration.default
configuration.httpCookieStorage = nil
configuration.requestCachePolicy = NSURLRequest.CachePolicy.reloadIgnoringLocalAndRemoteCacheData
if #available(iOS 11.0, *) {
configuration.waitsForConnectivity = false
}
let session = URLSession(configuration: configuration)
// let session = URLSession.shared
var task: URLSessionDataTask? = nil
print ("Request =======>",request)
if let req = request {
task = session.dataTask(with: request! , completionHandler: {
data, response, error in
var result: Any? = nil
if error != nil {
if let error = error {
print("\(error)")
}
if completion != nil {
completion(nil, error)
}
} else
{
var string: String? = nil
if let data = data {
string = String(data: data, encoding: .utf8)
}
string = self.string(byRemovingControlCharacters: string)
do {
if let data = string?.data(using: .utf8) {
result = try JSONSerialization.jsonObject(with: data, options: []) as! [AnyHashable : Any]
print ("Result ===============>",result)
}
} catch {
}
if completion != nil {
completion(result as! [AnyHashable : Any], error)
}
}
})
}
task?.resume()
}
Actually the completion block is an asynchronous process and i was waiting for the control to go back immediately after the process ends in debugging mode. It works now as expected

Cannot convert value of type '[String]' to expected argument type '([AnyObject]) -> Void'

I have a project code as below, I wish it can get urls from func getUrls(), but Xcode returns error message like title says.
I have search and try some solution to fix it, but all not works. Should I write it with another way, or just fix this error ? Is the declaration for arrUrls was wrong, or somewhere need to make correction?
p.s. If you answer me by a comment, remember to teach me how to make an "Answered mark" for your answer.:) Thank you.
var arrUrls = [String]()
#IBOutlet weak var webView: UIWebView!
override func viewDidLoad() {
super.viewDidLoad()
getUrls(url: url, completion: arrUrls) // Error Message: Cannot convert value of type '[String]' to expected argument type '([AnyObject]) -> Void'
...do something with array 'arrUrl'....but can't, because of the bug!
}
func getUrls(url : URL ,completion: #escaping (([AnyObject]) -> Void)) {
let task = URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) -> Void in
do {
if let jsonResult = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.allowFragments) as? [String : AnyObject] {
if let subjects = jsonResult["subjects"] as? [AnyObject]? {
for subject in subjects! {
if let content = subject["content"] as? [String : AnyObject] {
let s = String(describing: content["url"]!)
arrUrls.append(s)
}
}
}
}
completion(arrUrls as [AnyObject])
}catch {
print("json error: \(error)")
}
})
task.resume()
}
You need to call like this
getUrls(url: url) { (arr) in
}
if you edit the array inside the function then no need to return a completion value
func getUrls(url : URL ,completion: #escaping (() -> Void))
with
getUrls(url: url) {
// refresh ui here
DispatchQueue.main.async {
if firstItem = self.arrUrls.first , let url = URL(string:firstItem) {
webView.load(URLRequest(url: url))
}
}
change code to this
func getUrls(url : URL ,completion: #escaping (() -> Void)) {
let task = URLSession.shared.dataTask(with: url, completionHandler: { (data, response, error) -> Void in
guard let data = data else { return }
do {
if let jsonResult = try JSONSerialization.jsonObject(with: data) as? [String : Any] {
if let subjects = jsonResult["subjects"] as? [[String:Any]] {
for subject in subjects {
if let content = subject["content"] as? [String : Any] {
if let s = content["url"] as? String
{
self.arrUrls.append(s)
}
}
}
completion()
}
}
}catch {
completion()
}
})
task.resume()
}

Cannot figure out why Error is always turning out to be 'nil'

I am creating an Event Manager App, It requires to input valid Event Code to check the Event Details. I am working on the Sign in page where in the user will input the event code. When the page is error free, I tried to clean, build and run the app, insert breakpoints to check the executions of my codes. But seems that I am encountering an issue, where in, whether I input valid code or not, It just loaded and after it loads, It goes back to a clear textfield, no error alerts or not event dispatch the Dashboard Page. I really can't figure out what's wrong with my codes. I am new in swift. I really need hep to fix it. Below are my codes for your reference and image of eventDetails from breakpoint. Thankyou
APIService.swift
typealias JSONDictionary = Dictionary<String, AnyObject>
class APIService: NSObject, URLSessionDataDelegate {
enum Path {
case SubmitEventCode
}
typealias APICallback = ((AnyObject?, NSError?) -> ())
let responseData = NSMutableData()
var statusCode: Int = -1
var callback: APICallback! = nil
var path: Path! = nil
func validatePasscode(eventcode: String!, callback: #escaping APICallback)
{
let url = PASSCODE_CHECKER_URL //https://hopprLab.com/API/events/PasscodeChecker
makeHTTPPostRequest(path: Path.SubmitEventCode, callback: callback, url: url)
}
func connection(_ connection: URLSession, didReceive response: URLResponse){
let httpResponse = response as! HTTPURLResponse
statusCode = httpResponse.statusCode
switch (httpResponse.statusCode) {
case 201, 200, 401:
self.responseData.length = 0
default:
print("ignore")
}
}
func connection(_ connection: URLSession, didReceive data: Data) {
self.responseData.append(data)
}
func connectionDidFinishLoading(_ connection: URLSession) {
let error: NSError? = nil
let json = try? JSONSerialization.jsonObject( with: responseData as Data, options:[]) as AnyObject
if ((data) != nil) {
callback(nil, error)
return
}
switch(statusCode, self.path!) {
case(200, Path.SubmitEventCode):
callback(self.handleValidatePasscode(json: json!) as AnyObject,nil)
default:
//UnknownError
callback(nil, nil)
}
}
func handleAuthError(json: AnyObject) -> NSError {
if let eventObj = json as? JSONDictionary {
//
if let messageObj: AnyObject = eventObj["error"] {
if let message = messageObj as? String {
return NSError(domain: "validatePasscode", code: 200, userInfo: ["error": message])
}
}
}
return NSError(domain: "validatePasscode", code: 200, userInfo: ["error": "unknown auth error"])
}
func handleValidatePasscode(json: AnyObject) -> Event? {
if let eventObj = json as? JSONDictionary{
if let eventid: AnyObject = eventObj["event_id"]{
if let eventJson = eventid as? JSONDictionary {
if let eventpass = Event.init(JSON: eventJson){
return eventpass
}
}
}
}
return nil
}
//private
func makeHTTPPostRequest(path: Path, callback: #escaping APICallback, url: String) {
self.path = path
self.callback = callback
var request = URLRequest(url: NSURL(string: url)! as URL)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "content-type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
let configuration = URLSessionConfiguration.default
let session = URLSession(configuration: configuration)
let dataTask = session.dataTask(with: request, completionHandler: {
(data: Data?, response: URLResponse?, error: Error?) -> Void in
if data != nil {
DispatchQueue.main.async {
callback(nil,nil)
}
}
})
dataTask.resume()
}
}
Event.swift
struct Event {
let id: String
let name: String
let location: String
let startDateTime: Date
let endDateTime: String
let deleteFlag: Bool?
let deleteDateTime: String?
let dateCreated: String?
let hasRaffle: Bool?
let registrationReq: Bool?
let participantCount: Int
let closedFlag: Bool?
let closedDateTime: String?
let reopenFlag: Bool?
let reopenDateTime: String?
init?(JSON: [String: AnyObject]) {
guard let eventID = JSON["event_id"] as? String,
let eventName = JSON["event_name"] as? String,
let eventLocation = JSON["event_location"] as? String,
let startDateTime = JSON["start_datetime"] as? String,
let endDateTime = JSON["end_datetime"] as? String,
let participantCount = JSON["participant_count"] as? Int else {
return nil
}
self.id = eventID
self.name = eventName
self.location = eventLocation
self.endDateTime = endDateTime
self.participantCount = participantCount
validatePasscode Function
func validateEventPasscode() {
//Show Loading
self.view.squareLoading.start(0.0)
let api = APIService()
api.validatePasscode(eventcode: eventCode) { (data, error) in
guard let eventDetails = self.event, error == nil else {
if let networkError = error {
if networkError != error {
_ = SCLAlertView(appearance: appearance).showError("Ooops", subtitle: "Please enter valid event code")
else {
_ = SCLAlertView(appearance: appearance).showError("Network Error", subtitle: "Network Error")
}
}
guard eventDetails.deleteFlag else {
_ = SCLAlertView(appearance: appearance).showError("Ooops!", subTitle: "Please enter a valid event passcode")
self.view.squareLoading.stop(0.0)
return
}
if eventDetails.closedFlag == true && eventDetails.reopenFlag == false {
_ = SCLAlertView(appearance: appearance).showError("Closed Event", subTitle: "Please check the status of your event and try again")
self.view.squareLoading.stop(0.0)
return
}
}
}

Invalid conversion from throwing function of type '(_, _, _) throws -> ()' to non-throwing function type '(URLResponse?, Data?, Error?) -> Void

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
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.

Can't get value from Google-Books JSON API

I'm using a bar code scanner to get data on a scanned book using the google books api. I successfully call the API and get a JSON object back.
I'm trying to get the book title which follows the path items.volumeInfo.title.
When I call valueForPath on the JSON object returned by the API and attempt to print it (the title), I end up printing:
Optional((
"A Dance with Dragons"
))
I can't seem to figure out how to actually get the string out of the printed optional. I tried as! String and jsonResult.valueForKeyPath("items.volumeInfo.title")!, but the first simply complained to me and the second only removed the optional and outside set of parentheses.
func getBookInfo(isbn: String) {
var url: String = "https://www.googleapis.com/books/v1/volumes?q=isbn:" + isbn;
var request: NSMutableURLRequest = NSMutableURLRequest()
request.URL = NSURL(string: url)
request.HTTPMethod = "GET"
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue(), completionHandler:{ (response:NSURLResponse!, data: NSData!, error: NSError!) -> Void in
var error: AutoreleasingUnsafeMutablePointer<NSError?> = nil
let jsonResult: NSDictionary! = NSJSONSerialization.JSONObjectWithData(data, options:NSJSONReadingOptions.MutableContainers, error: error) as? NSDictionary
if (jsonResult != nil) {
println(jsonResult.valueForKeyPath("items.volumeInfo.title"))
//self.json.setValue(jsonResult.valueForKeyPath("items.volumeInfo.title")!, forKey: "title")
} else {
GlobalConstants.AlertMessage.displayAlertMessage("Error fetching data from barcode, please try again.", view: self)
}
})
}
The response you get from the API is an array of titles.
I suggest using if let to unwrap the Optional value you get from KVC, and typecasting the result as a Swift array of Strings.
Swift 1
func getBookInfo(isbn: String) {
var url: String = "https://www.googleapis.com/books/v1/volumes?q=isbn:" + isbn
var request: NSMutableURLRequest = NSMutableURLRequest()
request.URL = NSURL(string: url)
request.HTTPMethod = "GET"
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue(), completionHandler:{ (response:NSURLResponse!, data: NSData!, error: NSError!) -> Void in
var error: AutoreleasingUnsafeMutablePointer<NSError?> = nil
if let jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: error) as? NSDictionary {
if let arrayOfTitles = jsonResult.valueForKeyPath("items.volumeInfo.title") as? [String] {
let titles = ", ".join(arrayOfTitles)
println(titles)
} else {
// error: no title found
}
} else {
GlobalConstants.AlertMessage.displayAlertMessage("Error fetching data from barcode, please try again.", view: self)
}
})
}
getBookInfo("0553283685") // prints "Hyperion"
Swift 2
For this version we're using NSURLSession because NSURLConnection is now deprecated.
func getBookInfo(isbn: String) {
let urlString = "https://www.googleapis.com/books/v1/volumes?q=isbn:" + isbn
if let url = NSURL(string: urlString) {
NSURLSession.sharedSession().dataTaskWithURL(url, completionHandler: {data, _, error -> Void in
if let error = error {
print(error.localizedDescription)
} else {
if let data = data,
jsonResult = try? NSJSONSerialization.JSONObjectWithData(data, options: []),
arrayOfTitles = jsonResult.valueForKeyPath("items.volumeInfo.title") as? [String] {
let titles = arrayOfTitles.joinWithSeparator(", ")
print(titles)
} else {
GlobalConstants.AlertMessage.displayAlertMessage("Error fetching data from barcode, please try again.", view: self)
}
}
}).resume()
}
}
getBookInfo("0553283685") // prints "Hyperion"
Swift 3
Same as Swift 2 with some syntax changes. I've also added the "authors" example, and I'm now using guard. Just for the sake of showing something different from the previous example.
func getBookInfo(isbn: String) {
guard let url = URL(string: "https://www.googleapis.com/books/v1/volumes?q=isbn:\(isbn)") else {
print("the url is not valid")
return
}
URLSession.shared().dataTask(with: url, completionHandler: {data, response, error -> Void in
guard error == nil else {
print(response)
print(error!.localizedDescription)
return
}
guard let data = data else {
print("no error but no data")
print(response)
return
}
guard let jsonResult = try? JSONSerialization.jsonObject(with: data, options: []) else {
print("the JSON is not valid")
return
}
if let arrayOfTitles = jsonResult.value(forKeyPath: "items.volumeInfo.title") as? [String] {
print(arrayOfTitles)
}
if let arrayOfAuthors = jsonResult.value(forKeyPath: "items.volumeInfo.authors") as? [[String]] {
print(arrayOfAuthors)
}
}).resume()
}
getBookInfo(isbn: "0553283685")

Resources