Created
November 18, 2016 17:07
-
-
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
This file contains 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
// 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) | |
} | |
} |
This file contains 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
// 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)" | |
} | |
} | |
This file contains 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
// 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()) | |
} | |
} |
This file contains 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
// 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