Skip to content

Instantly share code, notes, and snippets.

@jancassio
Created November 18, 2016 17:07
Show Gist options
  • Save jancassio/63e7ae2194d2db1510cade2aa91cffcf to your computer and use it in GitHub Desktop.
Save jancassio/63e7ae2194d2db1510cade2aa91cffcf to your computer and use it in GitHub Desktop.
A simple protocol and some concrete implementations to generate links to be opened in some of common Map based apps for iOS
// The MIT License (MIT)
// Copyright (c) 2016 Jan Cássio
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
import CoreLocation
struct GoogleMapsLinkBuilder: MapLinkBuilding {
let component: URLComponents
enum DirectionMode: String {
case driving = "driving"
case transit = "transit"
case bicycling = "bicycling"
case walking = "walking"
}
/// Creates a new instance of `GoogleMapsLinkBuilder`.
///
/// - Returns: builder with updated values given in parameters.
static func create() -> GoogleMapsLinkBuilder {
return GoogleMapsLinkBuilder(component: URLComponents(string: "comgooglemaps://")!)
}
/// The query. This parameter is treated as if its value had been typed into the Maps search field by the user. Note that q=* is not supported.
/// The q parameter can also be used as a label if the location is explicitly defined in the ll or address parameters.
///
/// - Parameter query: A URL-encoded string that describes the search object, such as “pizza,” or an address to be geocoded.
/// - Returns: builder with updated values given in parameters.
func query (_ query: String) -> GoogleMapsLinkBuilder {
return param(name: "q", value: query)
}
/// The source address to be used as the starting point for directions.
/// A complete directions request includes the `saddr`, `daddr`, and `dirflg` parameters, but only the `daddr` parameter is required. If you don’t specify a value for `saddr`, the starting point is `“here”`.
///
/// - Parameters:
/// - to: An address string that geolocation can understand.
/// - from: An address string that geolocation can understand.
/// - transport: A transport type, the default is `foot`.
/// - Returns: builder with updated values given in parameters.
func route (to: String, from: String? = nil) -> GoogleMapsLinkBuilder {
var comp = param(name: "daddr", value: to)
if let from = from {
comp = comp.param(name: "saddr", value: from)
}
// Just set a default value for direction mode
return GoogleMapsLinkBuilder(component: comp.component).direction()
}
/// Equivalent to `route(to:from:transport)`, except the first parameter is a `CLLocationCoordinate2D` value.
///
/// - Parameters:
/// - to: A `CLLocationCoordinate2D` value.
/// - from: An address string that geolocation can understand.
/// - transport: A transport type, the default is `foot`.
/// - Returns: builder with updated values given in parameters.
func route (to: CLLocationCoordinate2D, from: String? = nil) -> GoogleMapsLinkBuilder {
var comp = param(name: "daddr", value: to.commaSeparated())
if let from = from {
comp = comp.param(name: "saddr", value: from)
}
// Just set a default value for direction mode
return GoogleMapsLinkBuilder(component: comp.component).direction()
}
/// The kind of direction mode to be used.
///
/// - Parameter type: A kind of direction mode, the default value is `transit`.
/// - Returns: builder with updated values given in parameters.
func direction (mode: DirectionMode = .transit) -> GoogleMapsLinkBuilder {
return param(name: "directionsmode", value: mode.rawValue)
}
}
// The MIT License (MIT)
// Copyright (c) 2016 Jan Cássio
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
import CoreLocation
protocol MapLinkBuilding {
var component: URLComponents { get }
init (component: URLComponents)
static func create () -> Self
func route (to: String, from: String?) -> Self
func route (to: CLLocationCoordinate2D, from: String?) -> Self
func route (to: CLLocationCoordinate2D) -> Self
func build () -> URL?
}
extension MapLinkBuilding {
/// Return an url composed by the builder.
///
/// - Returns: Returns a proper `URL` if is valid, otherwise returns `nil`.
func build () -> URL? {
print("URL to build \(component)")
return component.url
}
/// Defines the url's scheme
///
/// - Parameter scheme: a url scheme string.
/// - Returns: builder with updated values given in parameters.
public func scheme (_ scheme: String) -> Self {
var comp = component
comp.scheme = scheme
return Self(component: comp)
}
/// Extracts the url scheme from given `URL` value.
///
/// - Parameter url: An `URL` with valid scheme
/// - Returns: builder with updated values given in parameters.
public func scheme (url: URL) -> Self {
var comp = component
if let scheme = url.scheme {
comp.scheme = scheme
}
return Self(component: comp)
}
/// Define url host and scheme by given `URL` value.
///
/// - Parameter url: An `URL` with valid host and scheme
/// - Returns: builder with updated values given in parameters.
public func url (_ url: URL) -> Self {
var comp = scheme(url: url).component
if let host = url.host {
comp.host = host
}
return Self(component: comp)
}
/// Trace a route to some geolocation from the current geolocation postion.
///
/// - Parameter to: A `CLLocationCoordinate2D` value.
/// - Returns: builder with updated values given in parameters.
func route (to: CLLocationCoordinate2D) -> Self {
return self.route(to: to, from: nil)
}
/// Replaces a parameter already defined on querystring for another one.
///
/// - Parameters:
/// - param: A parameter to be replaced.
/// - by: A parameter to replace by.
/// - value: A value to be defined for the new parameter.
/// - Returns: builder with updated values given in parameters.
public func replace (param: String, by: String, value: String) -> Self {
var comp = component
if let index = comp.queryItems?.index(where: { $0.name == param }) {
let item = (URLQueryItem(name: by, value: value))
comp.queryItems?.remove(at: index)
comp.queryItems?.insert(item, at: index)
}
else {
comp.queryItems?.append(URLQueryItem(name: by, value: value))
}
return Self(component: comp)
}
/// Defines a new query string value.
///
/// - Parameters:
/// - name: The name or property.
/// - value: The value of property.
/// - Returns: builder with updated values given in parameters.
public func param (name: String, value: String) -> Self {
var comp = component
var queryItems = comp.queryItems ?? [ URLQueryItem ]()
if let index = queryItems.index(where: { $0.name == name }) {
var item = queryItems.remove(at: index)
item.value = value
queryItems.insert(item, at: index)
comp.queryItems = queryItems
}
else {
queryItems.append(URLQueryItem(name: name, value: value))
}
comp.queryItems = queryItems
return Self(component: comp)
}
}
extension CLLocationCoordinate2D {
public func commaSeparated () -> String {
return "\(self.latitude),\(self.longitude)"
}
}
// The MIT License (MIT)
// Copyright (c) 2016 Jan Cássio
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
import CoreLocation
/// Link builder for Apple's Maps app, for more information, see https://developer.apple.com/library/content/featuredarticles/iPhoneURLScheme_Reference/MapLinks/MapLinks.html1
struct MapsLinkBuilder: MapLinkBuilding {
/// Enumerates available map types.
///
/// - standard: Standard map view
/// - satelite: Satelite view
/// - hybrid: Hybrid map view
/// - transit: Transit view
enum MapType: String {
case standard = "s"
case satelite = "k"
case hybrid = "h"
case transit = "t"
}
/// Enumerate available transport types.
///
/// - car: By car.
/// - foot: By foot.
/// - publicTransit: By public transit.
enum TransportType: String {
case car = "d"
case foot = "w"
case publicTransit = "r"
}
/// Creates a new builder instance.
///
/// - Returns: Return a builder instance with empty url components value.
static func create () -> MapsLinkBuilder {
return MapsLinkBuilder(component: URLComponents(string: "http://maps.apple.com/")!)
}
let component: URLComponents
/// Creates a new builder with given components.
///
/// - Parameter component: an url component.
init (component: URLComponents) {
self.component = component
}
/// The type of map to be displayed.
///
/// - Parameter type: A map type to be displayed by Maps app.
/// - Returns: builder with updated values given in parameters.
func type (_ type: MapType) -> MapsLinkBuilder {
return param(name: "t", value: type.rawValue)
}
/// The query. This parameter is treated as if its value had been typed into the Maps search field by the user. Note that q=* is not supported.
/// The q parameter can also be used as a label if the location is explicitly defined in the ll or address parameters.
///
/// - Parameter query: A URL-encoded string that describes the search object, such as “pizza,” or an address to be geocoded.
/// - Returns: builder with updated values given in parameters.
func query (_ query: String) -> MapsLinkBuilder {
return param(name: "q", value: query)
}
/// The address. Using the address parameter simply displays the specified location, it does not perform a search for the location.
///
/// - Parameter addr: An address string that geolocation can understand.
/// - Returns: builder with updated values given in parameters.
func address (_ addr: String) -> MapsLinkBuilder {
return param(name: "address", value: addr)
}
/// A hint used during search. If the `sll` parameter is missing or its value is incomplete, the value of near is used instead.
///
/// - Parameter coord: A `CLLocationCoordinate2D` value.
/// - Returns: builder with updated values given in parameters.
func near (of coord: CLLocationCoordinate2D ) -> MapsLinkBuilder {
return param(name: "near", value: coord.commaSeparated())
}
/// The location around which the map should be centered.
/// The ll parameter can also represent a pin location when you use the q parameter to specify a name.
///
/// - Parameter coord: A `CLLocationCoordinate2D` value.
/// - Returns: builder with updated values given in parameters.
func center (at coord: CLLocationCoordinate2D ) -> MapsLinkBuilder {
return param(name: "ll", value: coord.commaSeparated())
}
/// The zoom level. You can use the z parameter only when you also use the sll parameter; in particular, you can’t use z in combination with the spn or sspn parameters.
///
/// - Parameter zoom: A floating point value between 2 and 21 that defines the area around the center point that should be displayed.
/// - Returns: builder with updated values given in parameters.
func zoom (_ zoom: Float) -> MapsLinkBuilder {
assert(2..<21 ~= zoom, "Zoom should be a float number between 2 and 21.")
return param(name: "z", value: "\(zoom)")
}
/// The area around the center point, or span. The center point is specified by the ll parameter.
/// You can’t use the spn parameter in combination with the z parameter.
///
/// - Parameter coord: A `CLLocationCoordinate2D` value.
/// - Returns: builder with updated values given in parameters.
func around (of coord: CLLocationCoordinate2D ) -> MapsLinkBuilder {
return replace(param: "z", by: "spn", value: coord.commaSeparated())
}
/// The source address to be used as the starting point for directions.
/// A complete directions request includes the `saddr`, `daddr`, and `dirflg` parameters, but only the `daddr` parameter is required. If you don’t specify a value for `saddr`, the starting point is `“here”`.
///
/// - Parameters:
/// - to: An address string that geolocation can understand.
/// - from: An address string that geolocation can understand.
/// - transport: A transport type, the default is `foot`.
/// - Returns: builder with updated values given in parameters.
func route (to: String, from: String? = nil) -> MapsLinkBuilder {
var comp = param(name: "daddr", value: to)
if let from = from {
comp = comp.param(name: "saddr", value: from)
}
// Just set a default value for transport
return MapsLinkBuilder(component: comp.component).transport()
}
/// Equivalent to `route(to:from:transport)`, except the first parameter is a `CLLocationCoordinate2D` value.
///
/// - Parameters:
/// - to: A `CLLocationCoordinate2D` value.
/// - from: An address string that geolocation can understand.
/// - transport: A transport type, the default is `foot`.
/// - Returns: builder with updated values given in parameters.
func route (to: CLLocationCoordinate2D, from: String? = nil) -> MapsLinkBuilder {
var comp = param(name: "daddr", value: to.commaSeparated())
if let from = from {
comp = comp.param(name: "saddr", value: from)
}
// Just set a default value for transport
return MapsLinkBuilder(component: comp.component).transport()
}
/// Defines a kind of transport used to move between two points.
///
/// - Parameter type: A kind of transport (default is `foot`)
/// - Returns: builder with updated values given in parameters.
func transport (type: TransportType = .foot) -> MapsLinkBuilder {
return param(name: "dirflg", value: type.rawValue)
}
/// The search location. You can specify the sll parameter by itself or in combination with the q parameter.
/// Example, `http://maps.apple.com/?sll=50.894967,4.341626&z=10&t=s` is a valid query.
///
/// - Parameter location: A `CLLocationCoordinate2D` value.
/// - Returns: builder with updated values given in parameters.
func search (location coord: CLLocationCoordinate2D ) -> MapsLinkBuilder {
return param(name: "sll", value: coord.commaSeparated())
}
}
// The MIT License (MIT)
// Copyright (c) 2016 Jan Cássio
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
import CoreLocation
struct WazeLinkBuilder: MapLinkBuilding {
let component: URLComponents
static func create() -> WazeLinkBuilder {
return WazeLinkBuilder(component: URLComponents(string:"waze://")!)
}
/// The query. This parameter is treated as if its value had been typed into the Maps search field by the user. Note that q=* is not supported.
/// The q parameter can also be used as a label if the location is explicitly defined in the ll or address parameters.
///
/// - Parameter query: A URL-encoded string that describes the search object, such as “pizza,” or an address to be geocoded.
/// - Returns: builder with updated values given in parameters.
func query (_ query: String) -> WazeLinkBuilder {
return param(name: "q", value: query)
}
/// The source address to be used as the starting point for directions.
/// A complete directions request includes the `saddr`, `daddr`, and `dirflg` parameters, but only the `daddr` parameter is required. If you don’t specify a value for `saddr`, the starting point is `“here”`.
///
/// - Parameters:
/// - to: An address string that geolocation can understand.
/// - from: An address string that geolocation can understand.
/// - transport: A transport type, the default is `foot`.
/// - Returns: builder with updated values given in parameters.
func route (to: String, from: String? = nil) -> WazeLinkBuilder {
return param(name: "ll", value: to)
}
/// Equivalent to `route(to:from:transport)`, except the first parameter is a `CLLocationCoordinate2D` value.
///
/// - Parameters:
/// - to: A `CLLocationCoordinate2D` value.
/// - from: An address string that geolocation can understand.
/// - transport: A transport type, the default is `foot`.
/// - Returns: builder with updated values given in parameters.
func route (to: CLLocationCoordinate2D, from: String? = nil) -> WazeLinkBuilder {
return param(name: "ll", value: to.commaSeparated())
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment