Search code examples
swiftflickr

FlickrAPI trouble understanding


I have to make an app that has the user place a pin on a map and then pulls pictures from Flickr based off the pin's location. My professor gave us a FlickrAPI file to use but I am having trouble understanding what it is doing.

import Foundation

class FlickrAPI: NSObject {
static let sharedInstance = FlickrAPI()    // Create a Singleton for the class

var photos: Array<String> = []
var errorLevel = 0

// Flickr Constants
let BASE_URL = "https://api.flickr.com/services/rest/"
let METHOD_NAME = "flickr.photos.search"
let API_KEY = "<API Key Here>"

func fetchPhotosFromFlickrBasedOn(Latitude lat: Double, Longitude lng: Double, PageToFetch pageToFetch: Int, completion: @escaping (_ error: Int, _ pg: Int, _ pgs: Int) -> Void) {
    // Empty our Photos Array
    photos.removeAll(keepingCapacity: true)

    // Build Aurgument List
    let methodArguments = [
        "method": METHOD_NAME,
        "api_key": API_KEY,
        "lat": String(format: "%f", lat),
        "lon": String(format: "%f", lng),
        "accuracy": "15",       // Accuracy (Street Level)
        "radius": "1",          // Distance
        "radius_units": "km",   // in Kilometers,
        "safe_search": "1",     // Safe (G Rated),
        "content_type": "1",    // Photos Only
        "per_page": "100",      // Photos per Page
        "page": "\(pageToFetch)",
        "extras": "url_m",      // Return Photo URLs
        "format": "json",       // Request JSON data format
        "nojsoncallback": "1"   // No JSON Callback
    ]

    // Initialize Shared Session
    let session = URLSession.shared
    let url = URL(string: BASE_URL + escapeUrlParameters(methodArguments))!
    let request = URLRequest(url: url)
    var page = 0
    var pages = 0

    // Setup Session Handler
    let task = session.dataTask(with: request, completionHandler: {data, response, error in
        self.errorLevel = 0  // Initialize Error Level
        if error != nil {
            self.errorLevel = 1  //***** Network Error
        } else {
            // Okay to Parse JSON
            do {
                let parsedResult: AnyObject = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.allowFragments) as AnyObject
                if let photosDictionary = parsedResult.value(forKey: "photos") as? NSDictionary {
                    if let photoArray = photosDictionary.value(forKey: "photo") as? [[String: AnyObject]] {
                        page = photosDictionary["page"] as! Int
                        pages = photosDictionary["pages"] as! Int
                        for photoDictionary in photoArray {
                            if let photoUrl = photoDictionary["url_m"] as? NSString {
                                let ext = photoUrl.pathExtension
                                let noExt = photoUrl.deletingPathExtension
                                let addThumbDesignation = (noExt + "_q_d") as NSString
                                let thumbUrl = addThumbDesignation.appendingPathExtension(ext)
                                self.photos.append(thumbUrl!)
                            } else {
                                NSLog("***** Could not obtain an Image URL at Index:%d for Owner:%@", self.photos.count, photoDictionary["owner"] as! String)
                            }
                        }
                    } else {
                        self.errorLevel = 4  //***** No "Photo" Array key present
                    }
                } else {
                    self.errorLevel = 3  //***** No "Photos" Dictionary key present
                }
            } catch {
                self.errorLevel = 2  //***** Parsing Error
            }
            completion(self.errorLevel, page, pages)
        }
    }) 
    task.resume()
}

// Escape URL Parameters
func escapeUrlParameters(_ parms: [String : String]) -> String {
    var urlParms = [String]()
    for (key, value) in parms {
        let escapedValue = value.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)
        urlParms += [key + "=" + "\(escapedValue!)"]
    }
    return urlParms.isEmpty ? "" : "?" + urlParms.joined(separator: "&")
}
}

In my code I have this function:

@IBAction func addPin(_ gestureRecognizer: UILongPressGestureRecognizer) {
    var touchPoint = gestureRecognizer.location(in: Map)
    var newCoordinates = Map.convert(touchPoint, toCoordinateFrom: Map)
    let annotation = MKPointAnnotation()
    annotation.coordinate = newCoordinates
    Map.addAnnotation(annotation)

    flickr.fetchPhotosFromFlickrBasedOn(Latitude: annotation.coordinate.latitude, Longitude: annotation.coordinate.longitude, PageToFetch: 1, completion: {error,pg,pgs in
print("Error: \(error)")
print("Page: \(pg)")
print("Pages: \(pgs)")
})
    //flickr.escapeUrlParameters(<#T##parms: [String : String]##[String : String]#>)
}

I understand that the code should be sending json information that I am supposed to use to display the image results on my app but like I said I am having a hard time understanding my professor's code. My main problem is after calling the function it skips everything that needs to run to pull the photos. I am clearly missing what I need to have so that my code will execute what it needs.


Solution

  • The radius parameter you had in FlickrAPI specified a radius that was not valid for Flickr's API. It should be something lower than 50. You'll have to play around with to see what the max you can get away with is, if that's the route you want to go.

    I would recommend using Postman to test URLs, rather than fiddling around debugging API issues in Xcode. Get your API call-oriented poop in a group before making calls in Xcode. It's a lot easier that way. In this case, just put a breakpoint at wherever the URL is, type po url in the debugger, copy the URL into Postman and see what comes back in Postman. API's like Flickr are pretty good at telling you what ****ed up. Also, don't be afraid to look at documentation for stuff. It makes life a lot easier. The documentation for this endpoint is located here.

    When you get to the completion call, put another breakpoint and type po photos. It'll print out an array of strings, which are URLs for the photos.

    You probably don't need the location manager or the usage description thing in your info.plist if you're just tapping on the map. Lastly, don't chuck an API key out there in an open forum like GitHub or SO.