Skip to content

Instantly share code, notes, and snippets.

@jdauphant
Created August 31, 2016 12:05
Show Gist options
  • Save jdauphant/0c7b28df645cff6254efe6340c9dc31e to your computer and use it in GitHub Desktop.
Save jdauphant/0c7b28df645cff6254efe6340c9dc31e to your computer and use it in GitHub Desktop.
Foursquare API search venues in swift (Dependency on Result)
//
// 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)
}
}
//
// 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()
}
}
//
// 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