Created
August 31, 2016 12:05
-
-
Save jdauphant/0c7b28df645cff6254efe6340c9dc31e to your computer and use it in GitHub Desktop.
Foursquare API search venues in swift (Dependency on Result)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// Foursquare.swift | |
// | |
// | |
import Foundation | |
import Result | |
public struct FoursquareService { | |
private enum Constants { | |
static let clientId = "" | |
static let clientSecret = "" | |
static let searchVenuesEndpoint = "https://api.foursquare.com/v2/venues/search" | |
static let version = "20160810" | |
static let locale = "en" | |
} | |
public enum Error: ErrorType { | |
case UnexpectedError(message: String) | |
} | |
public struct Location { | |
public let address: String? | |
public let crossStreet: String? | |
public let city: String? | |
public let state: String? | |
public let postalCode: String? | |
public let country: String? | |
public let lat: Double? | |
public let lng: Double? | |
public let distance: Double? | |
} | |
public struct Category { | |
public let id: String | |
public let name: String | |
public let iconPrefix: String | |
public let iconSuffix: String | |
public let pluralName: String | |
public let shortName: String | |
public let primary: Bool | |
} | |
public struct Venue { | |
public let id: String | |
public let name: String | |
public let location: Location | |
public let categories: [Category] | |
public let verified: Bool | |
} | |
struct Response { | |
let venues: [Venue] | |
} | |
public static func searchVenues(coordinates: Coordinates, | |
withRadius radius: Double? = nil, | |
withLimit limit: Int, | |
withIntent intent: String = "checkin", | |
withQuery query: String? = nil, | |
complete: (Result<[Venue], Error>) -> ()) { | |
var parameters = [ | |
"v": Constants.version, | |
"client_id": Constants.clientId, | |
"client_secret": Constants.clientSecret, | |
"locale": Constants.locale, | |
"ll": "\(coordinates.latitude),\(coordinates.longitude)", | |
"limit": "\(limit)", | |
"intent": intent | |
] | |
if let radius = radius { | |
parameters["radius"] = "\(radius)" | |
} | |
if let query = query { | |
parameters["query"] = "\(query))" | |
} | |
HTTP.request(Constants.searchVenuesEndpoint, withMethod: .GET, withParameters: parameters) { result in | |
let venues = result | |
.mapError { Error.UnexpectedError(message: "Http error \($0)") } | |
.flatMap { (_, data) in | |
JSON.fromNSDataToDictionary(data) | |
.mapError { Error.UnexpectedError(message: $0.localizedDescription) } | |
} | |
.flatMap { dict -> Result<[Venue], Error> in | |
let response = dict["response"].flatMap{ Response(anyObject: $0) } | |
return Result(response?.venues, failWith: Error.UnexpectedError(message: "Fail to find venues in result json")) | |
} | |
complete(venues) | |
} | |
} | |
} | |
private extension FoursquareService.Category { | |
init?(anyObject: AnyObject) { | |
guard let dict = anyObject as? [String:AnyObject], | |
id = dict["id"] as? String, | |
name = dict["name"] as? String, | |
iconDict = dict["icon"] as? [String:String], | |
iconSuffix = iconDict["suffix"], | |
iconPrefix = iconDict["prefix"], | |
pluralName = dict["pluralName"] as? String, | |
shortName = dict["shortName"] as? String | |
else { | |
return nil | |
} | |
self.id = id | |
self.name = name | |
self.iconPrefix = iconPrefix | |
self.iconSuffix = iconSuffix | |
self.pluralName = pluralName | |
self.shortName = shortName | |
self.primary = dict["primary"] as? Bool ?? false | |
} | |
} | |
private extension FoursquareService.Location { | |
init?(anyObject: AnyObject) { | |
guard let dict = anyObject as? [String:AnyObject] else { | |
return nil | |
} | |
address = dict["address"] as? String | |
crossStreet = dict["crossStreet"] as? String | |
city = dict["city"] as? String | |
state = dict["state"] as? String | |
postalCode = dict["postalCode"] as? String | |
country = dict["country"] as? String | |
lat = dict["lat"] as? Double | |
lng = dict["lng"] as? Double | |
distance = dict["distance"] as? Double | |
} | |
} | |
private extension FoursquareService.Venue { | |
init?(anyObject: AnyObject) { | |
guard let dict = anyObject as? [String:AnyObject], | |
id = dict["id"] as? String, | |
name = dict["name"] as? String, | |
locationAnyObject = dict["location"], | |
location = FoursquareService.Location(anyObject: locationAnyObject), | |
categoriesList = dict["categories"] as? [AnyObject], | |
verified = dict["verified"] as? Bool | |
else { | |
return nil | |
} | |
self.id = id | |
self.name = name | |
self.location = location | |
self.categories = categoriesList.flatMap(FoursquareService.Category.init) | |
self.verified = verified | |
} | |
} | |
private extension FoursquareService.Response { | |
init?(anyObject: AnyObject) { | |
guard let dict = anyObject as? [String:AnyObject], | |
venues = dict["venues"] as? [AnyObject] | |
else { | |
return nil | |
} | |
self.venues = venues.flatMap(FoursquareService.Venue.init) | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// HTTP.swift | |
// | |
// | |
import Foundation | |
import Result | |
public struct HTTP { | |
public enum HTTPError: ErrorType { | |
case MalformedURL | |
case MalformedParameters | |
case NoDataReturn | |
case UnexpectedError(NSError) | |
} | |
public enum Method { | |
case GET | |
case POST | |
} | |
public static func request(stringURL: String, | |
withMethod method: Method, | |
withParameters parameters: [String: String] = [:], | |
headers: [String: String] = [:], | |
withBody body: NSData? = nil, | |
complete: (Result<(NSHTTPURLResponse, NSData), HTTPError> -> ())) { | |
guard let urlComponents = NSURLComponents(string: stringURL) else { | |
complete(.Failure(HTTPError.MalformedURL)) | |
return | |
} | |
urlComponents.queryItems = parameters.map { (name, value) in NSURLQueryItem(name: name, value: value) } | |
guard let url = urlComponents.URL else { | |
complete(.Failure(HTTPError.MalformedParameters)) | |
return | |
} | |
let request: NSMutableURLRequest = NSMutableURLRequest(URL: url) | |
request.HTTPMethod = "\(method)" | |
request.HTTPBody = body | |
headers.forEach { (header, value) in | |
request.addValue(value, forHTTPHeaderField: header) | |
} | |
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { data, response, error -> Void in | |
if let error = error { | |
complete(.Failure(HTTPError.UnexpectedError(error))) | |
} else if let data = data, response = response, urlResponse = response as? NSHTTPURLResponse { | |
complete(.Success(urlResponse, data)) | |
} else { | |
complete(.Failure(HTTPError.NoDataReturn)) | |
} | |
} | |
task.resume() | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// JSON.swift | |
// | |
// | |
import Foundation | |
import Result | |
public struct JSON { | |
public static func fromNSDataToDictionary(data: NSData) -> Result<[String:AnyObject],NSError> { | |
return Result<AnyObject,NSError>(try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers)) | |
.flatMap { Result<[String:AnyObject],NSError>($0 as? [String:AnyObject], failWith: NSError(domain: "JSON", code: 1, userInfo: nil)) } | |
} | |
public static func fromAnyObjectToNSData(anyObject: AnyObject) -> Result<NSData,NSError> { | |
return Result<NSData,NSError>(try NSJSONSerialization.dataWithJSONObject(anyObject, options: [])) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment