Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

iOS Displaying Data with Table Views in Swift 2 Enhancing the Networking Stack Finishing Up the Networking Stack

Kevin Murphy
Kevin Murphy
24,380 Points

Confusion about expanding fetchRestaurantsFor/fetch for other endpoints

We set up VenueEndpoint enum to handle other endpoints other than search. However the method fetchRestaurantsFor(:) is set up specifically for the Search endpoint (i.e. the associated values for .Search are arguments for that method).

Also, calling fetch(:) for an Explore endpoint would require tweaking the dictionary keys in parse: as there is no "venues" object returned for the explore endpoint.

Anyone have thoughts on how to account for the different venue endpoints? Seems like an endpoint param would have to be added to getRestaurantsFor(:) and a switch on that endpoint would be involved. Also, another extension of fetch(:) would be required to account for the explore endpoint object. My experiments with that have yet to compile though lol.

5 Answers

David Lin
David Lin
35,864 Points

I think FoursquareClient: APIClient is simply a convenient wrapper class to simplify the call to the networking stack. So, yes, fetchRestaurantsFor is specifically implemented for the Search endpoint, and it's there more as a convenience than as a general solution for all endpoints.

If you want to call the Explore endpoint, I think you'd write another function to do that, something like exploreRestaurantsFor( ... ). And in that method, you would handle the parameter and response structure that's specific to the Explore endpoint, including specifying the desired endpoint object type in fetch.

So, unless there's a lot of common structure that can be clearly abstracted out for all of the endpoints to be wrapped by a reasonably clear generic calling method, it might be clearer just to handle different endpoints with its own specific wrapper method.

Kevin Murphy
Kevin Murphy
24,380 Points

Thats along the lines of what I was leaning toward David but wasn't certain. Thanks a lot for your clarifying points.

Hey if you have a chance, could you take a look at the gist I created to demonstrate/practice using an analogous endpoint process for "An API of Ice and Fire"? https://anapioficeandfire.com If you have any feedback it would be much appreciated. Thank you. https://gist.github.com/murphykevin/f2b0ce6715cdc0eb2732e7478f6d9a4c

David Lin
David Lin
35,864 Points

Sure thing, Kevin. Nicely done w/ the GoT endpoint!

Guys (David Lin/Kevin Murphy), i'm wondering if you can help me please as my question is in relation to yours. This may sound silly, but i'd rather sound silly and know the real reasoning lol.

In a previous tutorial VenueEndpoint conformed to Endpoint. The parameter keys were then assigned to .Search as local constants.

Am I right in thinking that within the fetchRestaurantsFor method, where the search endpoint is assigned to the searchEndpoint constant, clientID is now "client_id", and clientSecret is "client_secret", and so on?

 func fetchRestaurantsFor(location: Coordinate, category: Foursquare.VenueEndpoint.Category, 
query: String? = nil,  searchRadius: Int? = nil, limit: Int? = nil, completion: APIResult<[Venue]> -> Void){

    let searchEndpoint = Foursquare.VenueEndpoint.Search(clientID: self.clientID, 
    clientSecret: self.clientSecret, coordinate: location, category: category, query: query, 
    searchRadius: searchRadius, limit: limit)

    let endpoint = Foursquare.Venues(searchEndpoint)
David Lin
David Lin
35,864 Points

Chris,

Not sure if I understand your question, but the clientID and clientSecret that are used in the .Search as associated enum values will be whatever was passed into the initializer of the FoursquareClient:

    init(configuration: NSURLSessionConfiguration, clientID: String, clientSecret: String) {
        self.configuration = configuration
        self.clientID = clientID
        self.clientSecret = clientSecret
    }

    convenience init(clientID: String, clientSecret: String) {
        self.init(configuration: .defaultSessionConfiguration(), clientID: clientID, clientSecret: clientSecret)
    }

David Lin Sorry, I haven't really asked this question very well. Basically when we assigned the keys to local constants in .Search, are these keys passed through into the fetchRestaurantsFor method?

        var parameters: [String : AnyObject] {
        switch self {
        case .Search(let clientID, let clientSecret, let coordinate, 
        let category, let query, let searchRadius, let limit):

            var parameters: [String: AnyObject] = [
                ParameterKeys.clientID: clientID,
                ParameterKeys.clientSecret: clientSecret,
                ParameterKeys.version: DefaultValues.version,
                ParameterKeys.location: coordinate.description,
                ParameterKeys.category: category.description
David Lin
David Lin
35,864 Points

Chris,

I think I understand your question now. In the following, the parameter keys were simply initialized to some random default values:

        private struct ParameterKeys {
            static let clientID = "client_id"
            static let clientSecret = "client_secret"
            static let version = "v"
            static let category = "categoryId"
            static let location = "ll"
            static let query = "query"
            static let limit = "limit"
            static let searchRadius = "radius"
        }

However, all these keys will be overwritten with actual values that are passed in as associated values for the .Search enum:

        var parameters: JSON {
            switch self {
            case .Search(let clientID, let clientSecret, let coordinate, let category, let query, let searchRadius, let limit):

                var parameters: JSON = [
                    ParameterKeys.clientID: clientID,
                    ParameterKeys.clientSecret: clientSecret,
                    ParameterKeys.version: DefaultValues.version,
                    ParameterKeys.location: coordinate.description,
                    ParameterKeys.category: category.description
                ]

So, if "xyz" was passed into .Search as the argument for the clientID parameter, then ParameterKeys.clientID will become "xyz" now, rather than the default "client_id" that it had been initialized to.

Relating this back to fetchRestaurantsFor, whatever values were passed into fetchRestaurantsFor to create the .Search enum, that is what these final parameter values will become.

Cool, so the parameter keys were assigned to local constants.

So in the fetchRestaurantsFor method, does this mean that the .Search endpoint's clientID, clientSecret, coordinate, category, query, searchRadius & limit are all keys that have values assigned to them?

        let searchEndpoint = Foursquare.VenueEndpoint.Search(clientID: self.clientID, 
        clientSecret: self.clientSecret, coordinate: location, category: category, query: query, 
        searchRadius: searchRadius, limit: limit)
        let endpoint = Foursquare.Venues(searchEndpoint)
David Lin
David Lin
35,864 Points

Yeah, so in fetchRestaurantsFor, whatever values were passed in to it (or the default value if nothing was passed in for a parameter with a default) are what will be used as arguments to create .Search, and then those values will be what will be in turn assigned to the corresponding JSON parameters.

Brill, thanks so much for your help David, much appreciated. :)

David Lin
David Lin
35,864 Points

Sure thing, Chris! Any time!