├── .github ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .gitmodules ├── .jazzy.yaml ├── .swift-version ├── LICENSE.md ├── Mapzen-ios-sdk.podspec ├── MapzenSDK ├── APPLFrameworkExtensions.swift ├── BundleExtensions.swift ├── DictionaryExtensions.swift ├── Info.plist ├── LocationManager.swift ├── MZMapViewController.swift ├── MapzenManager.swift ├── MapzenSearch.swift ├── Marker.swift ├── PeliasMapkitExtensions.swift ├── RoutingController.swift ├── SearchConfigs.swift ├── SearchDataConverter.swift ├── SearchDataObjects.swift ├── SearchResponse.swift ├── TangramExtensions.swift ├── Themes.swift ├── UIColorExtensions.swift └── dimensions │ └── Dimensions.swift ├── MapzenSDKTests ├── DictionaryExtensionsTests.swift ├── Info.plist ├── MZMapViewControllerTests.swift ├── MapzenManagerTests.swift ├── MapzenSearchTests.swift ├── MarkerTests.swift ├── RoutingControllerTests.swift ├── SearchConfigsTests.swift ├── SearchDataConverterTests.swift ├── SearchDataObjectsTests.swift ├── SearchResponseTests.swift ├── TGMapViewControllerMocks.swift ├── TestAnnotationTarget.swift ├── TestApplication.swift ├── TestLocationManager.swift ├── TestMapzenManager.swift ├── TestNotificationCenter.swift ├── TestSessionDataTask.swift ├── TestStylesAndThemes.swift ├── TestTGMarkerPickResult.swift ├── TestUrlSession.swift └── UIColorExtensionsTests.swift ├── Podfile ├── Podfile.lock ├── README.md ├── SampleApp-Objc ├── AppDelegate.h ├── AppDelegate.m ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Info.plist ├── ViewController.h ├── ViewController.m └── main.m ├── SampleApp-ObjcTests ├── Info.plist └── SampleApp_ObjcTests.m ├── SampleApp ├── AppDelegate.swift ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── mapzen_appicon_ipad.png │ │ ├── mapzen_appicon_ipadonex.png │ │ ├── mapzen_appicon_ipadpro.png │ │ ├── mapzen_appicon_iphone1.png │ │ └── mapzen_appicon_iphone2.png │ ├── Contents.json │ ├── mapzen_logo.imageset │ │ ├── Contents.json │ │ ├── mapzen_logo.png │ │ ├── mapzen_logo@2x.png │ │ └── mapzen_logo@3x.png │ ├── tabbar_map.imageset │ │ ├── Contents.json │ │ ├── maps_50.png │ │ └── maps_75.png │ ├── tabbar_map_active.imageset │ │ ├── Contents.json │ │ ├── maps_active_50.png │ │ └── maps_active_75.png │ ├── tabbar_nav.imageset │ │ ├── Contents.json │ │ ├── navigation_50.png │ │ └── navigation_75.png │ ├── tabbar_nav_active.imageset │ │ ├── Contents.json │ │ ├── navigation_active_50.png │ │ └── navigation_active_75.png │ ├── tabbar_search.imageset │ │ ├── Contents.json │ │ ├── search_50.png │ │ └── search_75.png │ └── tabbar_search_active.imageset │ │ ├── Contents.json │ │ ├── search_50.png │ │ └── search_75.png ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── DemoMapViewController.swift ├── DemoRouteDirectionCell.swift ├── DemoRouteViewController.swift ├── DemoRoutingResultTableVC.swift ├── DemoSearchListViewController.swift ├── DemoSearchPinsViewController.swift ├── Info.plist ├── RouteDisplayViewController.swift ├── RoutingSearchVC.swift ├── RoutingViewController.swift ├── SampleAutocompleteTableViewController.swift ├── SampleMapViewController.swift ├── SampleTableViewController.swift └── StylePickerVC.swift ├── circle.yml ├── docs ├── basic-functions.md ├── documentation.md ├── features.md ├── gesture-delegates.md ├── getting-started.md ├── index.md ├── installation.md ├── location-services.md ├── places.md ├── search.md ├── styles.md └── turn-by-turn.md ├── fastlane ├── Appfile ├── Fastfile └── README.md ├── images ├── ic_find_me_normal.png ├── ic_find_me_normal@2x.png ├── ic_find_me_normal@3x.png ├── ic_find_me_pressed.png ├── ic_find_me_pressed@2x.png └── ic_find_me_pressed@3x.png ├── ios-sdk.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ └── xcschemes │ ├── MapzenSDK.xcscheme │ ├── SampleApp-Objc.xcscheme │ └── ios-sdk.xcscheme ├── ios-sdk.xcworkspace ├── contents.xcworkspacedata └── xcshareddata │ └── WorkspaceSettings.xcsettings ├── release_checklist.md └── scripts ├── build_test_objc.sh ├── build_test_swift.sh ├── deploy.sh ├── perform_nightly.sh └── setup_version.swift /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | #Contributing 2 | Thanks for taking the time to contribute. Here are some steps and guidelines on how to best get your code into master! 3 | 4 | 1. Fork the repository 5 | 6 | 2. Clone your forked copy of the repository 7 | 8 | 3. Initialize the submodules 9 | ``` 10 | git submodule update --init --recursive 11 | ``` 12 | 13 | 4. Install [Cocoapods](https://guides.cocoapods.org/using/getting-started.html) 14 | 15 | 5. Run `pod install` 16 | 17 | 6. Open `ios-sdk.xcworkspace` 18 | 19 | 7. [Configure API Key](https://github.com/mapzen/ios#configure-api-key) 20 | 21 | 8. Write your code! 22 | 23 | 9. Write tests for your code. 24 | - We love well tested code. 25 | 26 | 10. Run your tests and ensure that they pass 27 | 28 | - Use XCode's `Product > Test` menu or the keyboard shortcut: `⌘u` 29 | 30 | 11. Create a new [pull request](https://github.com/mapzen/ios/pulls/new) 31 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Description 2 | 3 | ### Steps to Reproduce 4 | 5 | ### Mapzen SDK & iOS Version 6 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Overview 2 | 3 | ### Proposed Changes 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | .DS_Store 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | !default.xcworkspace 13 | xcuserdata 14 | *.xcscmblueprint 15 | profile 16 | *.moved-aside 17 | DerivedData 18 | .idea/ 19 | # Pods - for those of you who use CocoaPods 20 | Pods 21 | #XCUserData 22 | xcuserdata/ 23 | #For when we generate the version.plist file locally 24 | version.plist -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "styles/bubble-wrap"] 2 | path = housestyles.bundle/bubble-wrap 3 | url = https://github.com/tangrams/bubble-wrap.git 4 | [submodule "styles/cinnabar"] 5 | path = housestyles.bundle/cinnabar 6 | url = https://github.com/tangrams/cinnabar-style.git 7 | [submodule "styles/refill"] 8 | path = housestyles.bundle/refill 9 | url = https://github.com/tangrams/refill-style.git 10 | [submodule "styles/walkabout"] 11 | path = housestyles.bundle/walkabout 12 | url = https://github.com/tangrams/walkabout-style.git 13 | -------------------------------------------------------------------------------- /.jazzy.yaml: -------------------------------------------------------------------------------- 1 | module: MapzenSDK 2 | author: Mapzen 3 | author_url: https://www.mapzen.com/ 4 | output: ./build/ios-docs/ 5 | github_url: https://github.com/mapzen/ios 6 | copyright: '© 2017 [Mapzen](https://www.mapzen.com/).' 7 | head: | 8 | 9 | xcodebuild_arguments: [-workspace,ios-sdk.xcworkspace,-scheme,MapzenSDK] 10 | skip_undocumented: Yes 11 | clean: true 12 | -------------------------------------------------------------------------------- /.swift-version: -------------------------------------------------------------------------------- 1 | 3.0 2 | -------------------------------------------------------------------------------- /Mapzen-ios-sdk.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | 3 | s.name = 'Mapzen-ios-sdk' 4 | s.version = '1.1.1' 5 | 6 | s.summary = 'Mapzen iOS SDK' 7 | s.description = 'The Mapzen iOS SDK is a thin wrapper that packages up everything you need to use Mapzen services in your iOS applications. It also simplifies setup, installation, API key management, and generally makes your life better.' 8 | s.homepage = 'https://mapzen.com/projects/mobile/' 9 | s.license = { :type => 'Apache License, Version 2.0', :file => 'LICENSE.md' } 10 | s.author = { 'Mapzen' => 'ios-support@mapzen.com' } 11 | s.social_media_url = 'https://twitter.com/mapzen' 12 | s.documentation_url = 'https://mapzen.com/documentation/ios/' 13 | s.source = { :git => 'https://github.com/mapzen/ios.git', :tag => "v#{s.version}", :submodules => true } 14 | 15 | s.platform = :ios 16 | s.ios.deployment_target = '9.3' 17 | 18 | s.requires_arc = true 19 | s.default_subspec = 'Core' 20 | 21 | s.prepare_command = "swift scripts/setup_version.swift #{s.version}" 22 | 23 | s.subspec 'Core' do |cs| 24 | cs.dependency 'Pelias', '~> 1.0.2' 25 | cs.dependency 'OnTheRoad', '~> 1.0.1' 26 | cs.dependency 'Tangram-es', '~> 0.8.1' 27 | cs.source_files = ['MapzenSDK/*.swift', 'MapzenSDK/*/*.swift'] 28 | cs.resources = [ 'images/*.png', 'housestyles.bundle', 'version.plist' ] 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /MapzenSDK/APPLFrameworkExtensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ApplicationExtensions.swift 3 | // ios-sdk 4 | // 5 | // Created by Sarah Lensing on 2/24/17. 6 | // Copyright © 2017 Mapzen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol ApplicationProtocol { 12 | func openURL(_ url: URL) -> Bool 13 | } 14 | 15 | extension UIApplication: ApplicationProtocol {} 16 | 17 | protocol NotificationCenterProtocol { 18 | func addObserver(_ observer: Any, selector aSelector: Selector, name aName: NSNotification.Name?, object anObject: Any?) 19 | func removeObserver(_ observer: Any) 20 | } 21 | extension NotificationCenter: NotificationCenterProtocol {} 22 | -------------------------------------------------------------------------------- /MapzenSDK/BundleExtensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BundleExtensions.swift 3 | // ios-sdk 4 | // 5 | // Created by Sarah Lensing on 4/17/17. 6 | // Copyright © 2017 Mapzen. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension Bundle { 12 | public static func mapzenBundle() -> Bundle { 13 | return Bundle.init(for: MZMapViewController.self) 14 | } 15 | 16 | public static func houseStylesBundle() -> Bundle? { 17 | guard let styleBundleUrl = Bundle.mapzenBundle().url(forResource: "housestyles", withExtension: "bundle") else { 18 | return nil 19 | } 20 | return Bundle.init(url: styleBundleUrl) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /MapzenSDK/DictionaryExtensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DictionaryExtensions.swift 3 | // ios-sdk 4 | // 5 | // Created by Sarah Lensing on 2/24/17. 6 | // Copyright © 2017 Mapzen. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension Dictionary where Value: Equatable { 12 | 13 | /* Use when keys/values are not 1:1 */ 14 | public func keysForValue(value: Value) -> [Key] { 15 | return flatMap { (key: Key, val: Value) -> Key? in 16 | value == val ? key : nil 17 | } 18 | } 19 | 20 | /* Use when keys/values are 1:1*/ 21 | public func keyForValue(value: Value) -> Key? { 22 | let results = keysForValue(value: value) 23 | guard !results.isEmpty else { return nil } 24 | return results.first 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /MapzenSDK/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSPrincipalClass 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /MapzenSDK/LocationManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LocationManager.swift 3 | // ios-sdk 4 | // 5 | // Created by Matt Smollinger on 11/22/16. 6 | // Copyright © 2016 Mapzen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CoreLocation 11 | import OnTheRoad 12 | 13 | /// LocationManagerDelegate Protoco 14 | @objc(MZLocationManagerDelegate) 15 | public protocol LocationManagerDelegate { 16 | 17 | /// The user authorized the application to receive location updates 18 | @objc optional func authorizationDidSucceed() 19 | 20 | /// The user denied the application from receiving location updates. 21 | @objc optional func authorizationDenied() 22 | 23 | /// The user is restricted from authorizing the application for location updates. This is normally due to parental control lockout. 24 | @objc optional func authorizationRestricted() 25 | 26 | /** 27 | The device received a new location. 28 | 29 | - parameter location: The new location. 30 | */ 31 | @objc optional func locationDidUpdate(_ location: CLLocation) 32 | } 33 | 34 | /** 35 | The `LocationManager` class is a wrapper around iOS's built in location subsystem. It provides a simpler interface and customization options. 36 | */ 37 | @objc(MZLocationManager) 38 | open class LocationManager: NSObject, CLLocationManagerDelegate, LocationManagerProtocol { 39 | 40 | /// The last received known good location 41 | open var currentLocation: CLLocation? 42 | 43 | /// The delegate to receive the location authorization and status callbacks 44 | open weak var delegate: LocationManagerDelegate? 45 | 46 | internal let coreLocationManager = CLLocationManager() 47 | 48 | override public init(){ 49 | super.init() 50 | coreLocationManager.delegate = self 51 | } 52 | 53 | /// Request the user give the application location access at all times. 54 | open func requestAlwaysAuthorization() { 55 | coreLocationManager.requestAlwaysAuthorization() 56 | } 57 | 58 | /// Request the user give the application location access only when in the foreground. 59 | open func requestWhenInUseAuthorization() { 60 | coreLocationManager.requestWhenInUseAuthorization() 61 | } 62 | 63 | /** 64 | Asks the location subsystem if we're currently authorized for location access while in foreground. 65 | 66 | - returns: Whether or not the application is authorized. 67 | */ 68 | open func isInUseAuthorized() -> Bool { 69 | return CLLocationManager.authorizationStatus() == .authorizedWhenInUse ? true : false 70 | } 71 | 72 | /** 73 | Asks the location subsystem if we're currently authorized for location access at all times. 74 | 75 | - returns: Whether or not the application is authorized. 76 | */ 77 | open func isAlwaysAuthorized() -> Bool { 78 | return CLLocationManager.authorizationStatus() == .authorizedAlways 79 | } 80 | 81 | /** 82 | Asks the location subsystem if we're able to receive background location updates. This should be checked every startup because the user can disable it externally in settings. 83 | 84 | - returns: Whether or not the application is able to receive background location updates. 85 | */ 86 | open func canEnableBackgroundLocationUpdates() -> Bool { 87 | if UIApplication.shared.backgroundRefreshStatus != .available { return false } 88 | return isAlwaysAuthorized() 89 | } 90 | 91 | /** 92 | Enables background location updates to be received. This requires additional parameters in your apps Info.plist be set, which is different depending on the version of iOS you are linking against. Please consult Apple's most current documentation on the matter, or reference our sample application for an example. 93 | 94 | - parameter activityType: The core location activity type that we're requesting background location updates for. 95 | - parameter desiredAccuracy: Controls what systems (GPS, Wi-Fi, Cellular, iBeacon, etc.) are involved with updating locations. 96 | - parameter pausesAutomatically: Whether or not certain activity types will pause sending updates dynamically. Examples include navigation activities will not send updates when the user has not moved significantly. 97 | - returns: True if enabled, or false if we can't enable due to system restrictions 98 | */ 99 | open func enableBackgroundLocationUpdates(forType activityType: CLActivityType, desiredAccuracy: CLLocationAccuracy, pausesLocationAutomatically: Bool) -> Bool { 100 | if !canEnableBackgroundLocationUpdates() { return false } 101 | coreLocationManager.activityType = activityType 102 | coreLocationManager.desiredAccuracy = desiredAccuracy 103 | coreLocationManager.pausesLocationUpdatesAutomatically = pausesLocationAutomatically 104 | coreLocationManager.allowsBackgroundLocationUpdates = true 105 | coreLocationManager.startUpdatingLocation() 106 | coreLocationManager.startUpdatingHeading() 107 | return true 108 | } 109 | 110 | /// Disables background location updates. If you want to continue receiving foreground location updates, you must call startUpdatingLocation() again. 111 | open func disableBackgroundLocationUpdates() { 112 | coreLocationManager.allowsBackgroundLocationUpdates = false 113 | coreLocationManager.stopUpdatingHeading() 114 | coreLocationManager.stopUpdatingLocation() 115 | } 116 | 117 | /** 118 | Refreshes the stored current location and also returns it to the caller 119 | 120 | -returns: The most current location the location system has, or nil if it has no current location. 121 | */ 122 | open func refreshCurrentLocation() -> CLLocation? { 123 | if CLLocationManager.locationServicesEnabled() && 124 | (CLLocationManager.authorizationStatus() == .authorizedAlways || CLLocationManager.authorizationStatus() == .authorizedWhenInUse) { 125 | currentLocation = coreLocationManager.location 126 | return coreLocationManager.location 127 | } 128 | return nil 129 | } 130 | 131 | /// The difference between this function and `refreshCurrentLocation()` is `requestLocation()` immediately returns and will serve the location via the delegate 132 | open func requestLocation() { 133 | coreLocationManager.requestLocation() 134 | } 135 | 136 | /// Starts the location manager callbacks for location updates 137 | open func startUpdatingLocation() { 138 | coreLocationManager.startUpdatingLocation() 139 | } 140 | 141 | /// Stops the location manager callbacks 142 | open func stopUpdatingLocation() { 143 | coreLocationManager.stopUpdatingLocation() 144 | } 145 | 146 | /// Begin tracking heading 147 | open func startUpdatingHeading() { 148 | coreLocationManager.startUpdatingHeading() 149 | } 150 | 151 | // Stop tracking heading 152 | open func stopUpdatingHeading() { 153 | coreLocationManager.stopUpdatingHeading() 154 | } 155 | 156 | //MARK: - CLLocationManagerDelegate 157 | 158 | open func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) { 159 | switch status { 160 | case .authorizedAlways: 161 | delegate?.authorizationDidSucceed?() 162 | case .authorizedWhenInUse: 163 | manager.startUpdatingLocation() 164 | delegate?.authorizationDidSucceed?() 165 | case .denied: 166 | delegate?.authorizationDenied?() 167 | case .restricted: 168 | delegate?.authorizationRestricted?() 169 | default: 170 | print("Not Authorized") 171 | } 172 | } 173 | 174 | open func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { 175 | if let location = locations.last { 176 | currentLocation = location 177 | delegate?.locationDidUpdate?(location) 178 | } 179 | } 180 | 181 | open func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) { 182 | print("Error from location manager: \(error)") 183 | } 184 | 185 | } 186 | 187 | /// Protocol for LocationManager's api so that actual implementation can be switched out in testing contexts. 188 | @objc(MZLocationManagerProtocol) 189 | public protocol LocationManagerProtocol : class { 190 | weak var delegate: LocationManagerDelegate? { get set } 191 | var currentLocation: CLLocation? { get set } 192 | 193 | func requestAlwaysAuthorization() 194 | func requestWhenInUseAuthorization() 195 | func isInUseAuthorized() -> Bool 196 | func isAlwaysAuthorized() -> Bool 197 | func refreshCurrentLocation() -> CLLocation? 198 | func requestLocation() 199 | func startUpdatingLocation() 200 | func stopUpdatingLocation() 201 | func startUpdatingHeading() 202 | func stopUpdatingHeading() 203 | func canEnableBackgroundLocationUpdates() -> Bool 204 | func enableBackgroundLocationUpdates(forType activityType: CLActivityType, desiredAccuracy: CLLocationAccuracy, pausesLocationAutomatically: Bool) -> Bool 205 | } 206 | 207 | -------------------------------------------------------------------------------- /MapzenSDK/MapzenManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MapzenManager.swift 3 | // ios-sdk 4 | // 5 | // Created by Matt Smollinger on 1/12/17. 6 | // Copyright © 2017 Mapzen. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Pelias 11 | import OnTheRoad 12 | 13 | protocol MapzenManagerProtocol { 14 | var apiKey: String? { set get } 15 | } 16 | 17 | /** 18 | `MapzenManager` is a singleton object used for managing state between the various dependencies. Right now, it only manages the API key system. 19 | */ 20 | open class MapzenManager: NSObject, MapzenManagerProtocol { 21 | /// The single object to be used for all access 22 | open static let sharedManager = MapzenManager() 23 | static let SDK_VERSION_KEY = "sdk_version" 24 | 25 | /// The Mapzen API key. If this is not set, exceptions will get raised by the various objects in use. 26 | dynamic open var apiKey: String? 27 | 28 | fileprivate override init(){ 29 | super.init() 30 | } 31 | 32 | //MARK: - Http Headers 33 | func httpHeaders() -> [String:String] { 34 | var headers = [String:String]() 35 | headers["User-Agent"] = buildUserAgent() 36 | return headers 37 | } 38 | 39 | fileprivate func buildUserAgent() -> String { 40 | let systemVersion = UIDevice.current.systemVersion 41 | var sdkVersion = "0" 42 | //Now for the fun - we grab the current bundle 43 | let bundle = Bundle(for: MapzenManager.self) 44 | // Assuming cocoapods did its thing and ran setup_version.swift, there will be a version.plist in our bundle 45 | if let pathForVersionPlist = bundle.path(forResource: "version", ofType: "plist") { 46 | if let versionDict = NSDictionary(contentsOfFile: pathForVersionPlist) { 47 | if let version = versionDict[MapzenManager.SDK_VERSION_KEY] { 48 | sdkVersion = version as! String 49 | } 50 | } 51 | } 52 | return "ios-sdk;\(sdkVersion),\(systemVersion)" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /MapzenSDK/MapzenSearch.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MapzenSearch.swift 3 | // Pods 4 | // 5 | // Created by Sarah Lensing on 3/27/17. 6 | // 7 | // 8 | 9 | import Pelias 10 | 11 | /// Main entry point for interacting with Mapzen Search. 12 | public class MapzenSearch : NSObject { 13 | /// Returns the shared 'MapzenSearch' instance. 14 | public static let sharedInstance = MapzenSearch() 15 | public var apiKey: String? { 16 | didSet { 17 | guard let apiKey = apiKey else { 18 | if let queryItems = urlQueryItems { 19 | for (index, queryItem) in queryItems.enumerated() { 20 | if queryItem.name == "api_key" { 21 | urlQueryItems?.remove(at: index) 22 | } // if queryItem 23 | } // for (index, queryItem) 24 | } // if let queryItems 25 | return 26 | } 27 | 28 | urlQueryItems = [URLQueryItem(name: "api_key", value: apiKey)] 29 | } // didSet 30 | } 31 | 32 | private let peliasSearchManager = PeliasSearchManager.sharedInstance 33 | private var myContext = 0 34 | 35 | /// Delay in seconds that the manager should wait between keystrokes to fire a new autocomplete request. Default is 0.3 36 | public var autocompleteTimeDelay: Double { 37 | get { 38 | return peliasSearchManager.autocompleteTimeDelay 39 | } 40 | set(delay) { 41 | peliasSearchManager.autocompleteTimeDelay = delay 42 | } 43 | } 44 | 45 | /// Base url to execute requests against. Default value is https://search.mapzen.com. 46 | public var baseUrl: URL { 47 | get { 48 | return peliasSearchManager.baseUrl 49 | } 50 | set(url) { 51 | peliasSearchManager.baseUrl = url 52 | } 53 | } 54 | 55 | /// The query items that should be applied to every request (such as an api key). 56 | public var urlQueryItems: [URLQueryItem]? { 57 | get { 58 | return peliasSearchManager.urlQueryItems 59 | } 60 | set(queryItems) { 61 | peliasSearchManager.urlQueryItems = queryItems 62 | } 63 | } 64 | 65 | fileprivate override init() { 66 | super.init() 67 | autocompleteTimeDelay = 1.0 68 | defer { 69 | setupAPIKeyObservance() 70 | apiKey = MapzenManager.sharedManager.apiKey 71 | peliasSearchManager.additionalHttpHeaders = MapzenManager.sharedManager.httpHeaders() 72 | } 73 | } 74 | /** Perform an asyncronous search request given parameters defined by the search config. Returns the queued operation. 75 | - parameter config: Object holding search request parameter information. 76 | */ 77 | public func search(_ config: SearchConfig) -> Operation { 78 | return peliasSearchManager.performSearch(config.peliasConfig); 79 | } 80 | /** Perform an asyncronous reverse geocode request given parameters defined by the config. Returns the queued operation. 81 | - parameter config: Object holding reverse geo request parameter information. 82 | */ 83 | public func reverseGeocode(_ config: ReverseConfig) -> Operation { 84 | return peliasSearchManager.reverseGeocode(config.peliasConfig) 85 | } 86 | /** Perform an asyncronous autocomplete request given parameters defined by the config. Returns the queued operation. 87 | - parameter config: Object holding autocomplete request parameter information. 88 | */ 89 | public func autocompleteQuery(_ config: AutocompleteConfig) -> Operation { 90 | return peliasSearchManager.autocompleteQuery(config.peliasConfig) 91 | } 92 | /** Perform an asyncronous place request given parameters defined by the search config. Returns the queued operation. 93 | - parameter config: Object holding place request parameter information. 94 | */ 95 | public func placeQuery(_ config: PlaceConfig) -> Operation { 96 | return peliasSearchManager.placeQuery(config.peliasConfig) 97 | } 98 | /// Cancel all requests 99 | public func cancelOperations() { 100 | peliasSearchManager.cancelOperations() 101 | } 102 | 103 | // KVO Observance for API Keys because singletons are funky 104 | func setupAPIKeyObservance() { 105 | MapzenManager.sharedManager.addObserver(self, forKeyPath: 106 | #keyPath(MapzenManager.apiKey), options: .new, context: &myContext) 107 | } 108 | 109 | override public func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { 110 | if context == &myContext { 111 | if (change?[.newKey]) != nil && keyPath == #keyPath(MapzenManager.apiKey){ 112 | apiKey = MapzenManager.sharedManager.apiKey 113 | } 114 | } else { 115 | super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context) 116 | } 117 | } 118 | 119 | deinit { 120 | MapzenManager.sharedManager.removeObserver(self, forKeyPath: #keyPath(MapzenManager.apiKey), context: &myContext) 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /MapzenSDK/PeliasMapkitExtensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MapkitExtensions.swift 3 | // pelias-ios-sdk 4 | // 5 | // Created by Matt on 7/12/16. 6 | // Copyright © 2016 Mapzen. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import MapKit 11 | import CoreLocation 12 | import Pelias 13 | 14 | /* 15 | 16 | Typical search response format is in GeoJSON - http://geojson.org/geojson-spec.html 17 | 18 | Example: 19 | { 20 | "geocoding": { 21 | "version": "0.1", 22 | "attribution": "https://search.mapzen.com/v1/attribution", 23 | "query": { 24 | "text": "cool", 25 | "size": 1, 26 | "private": false 27 | }, 28 | "engine": { 29 | "name": "Pelias", 30 | "author": "Mapzen", 31 | "version": "1.0" 32 | }, 33 | "timestamp": 1451940120154 34 | }, 35 | "type": "FeatureCollection", 36 | "features": [{ 37 | "type": "Feature", 38 | "properties": { 39 | "id": "3577127899", 40 | "gid": "osm:venue:3577127899", 41 | "layer": "venue", 42 | "source": "osm", 43 | "name": "Cool", 44 | "country_a": "GRC", 45 | "country": "Greece", 46 | "region": "ΚΡΗΤΗ", 47 | "locality": "ΡΕΘΥΜΝΟΝ", 48 | "confidence": 0.711, 49 | "label": "Cool, ΡΕΘΥΜΝΟΝ, Greece" 50 | }, 51 | "geometry": { 52 | "type": "Point", 53 | "coordinates": [24.474645, 35.371769] 54 | } 55 | }], 56 | "bbox": [-98.01126, 32.8000597029494, 67.34516, 56.034896] 57 | } 58 | 59 | */ 60 | 61 | public let PeliasIDKey: String = "PeliasOSMIDKey" 62 | public let PeliasDataSourceKey: String = "PeliasDataSourceKey" 63 | 64 | /** 65 | `PeliasMapkitAnnotation` is a data object class that provides MapKit compatible annotation objects. It conforms to `MKAnnotation` protocol and is also used by the Mapzen iOS SDK for annotations (markers in Tangram-es). 66 | */ 67 | open class PeliasMapkitAnnotation: NSObject, MKAnnotation { 68 | 69 | open let coordinate: CLLocationCoordinate2D 70 | open let title: String? 71 | open let subtitle: String? 72 | open let data: [String: AnyObject]? 73 | open weak var target: UIResponder? 74 | open var selector: Selector? 75 | 76 | /** 77 | Create a fully formed `PeliasMapkitAnnotation` 78 | 79 | - parameter coordinate: A CLLocationCoordinate2D object for lat/long placement 80 | - parameter title: An optional title for the annotation 81 | - parameter subtitle: An optional subtitle for the annotation 82 | - parameter data: An optional data dictionary useful for communicating additional metadata about an annotation 83 | 84 | - returns: A fully formed `PeliasMapkitAnnotation` 85 | */ 86 | public init(coordinate: CLLocationCoordinate2D, title: String?, subtitle: String?, data: [String:AnyObject]?) { 87 | self.coordinate = coordinate 88 | self.title = title 89 | self.subtitle = subtitle 90 | self.data = data 91 | } 92 | 93 | /** 94 | Sets a target for the selector which will be invoked when the annotation is clicked. 95 | 96 | - parameter actionTarget: A target to invoke the selector on when the annotation is clicked 97 | - parameter action: An selector to be invoked on the target when the annotation is clicked 98 | */ 99 | public func setTarget(target actionTarget: UIResponder, action: Selector) { 100 | target = actionTarget 101 | selector = action 102 | } 103 | } 104 | 105 | public extension PeliasResponse { 106 | /** 107 | Produces an array of PeliasMapkitAnnotations based off the response from Pelias servers. 108 | 109 | This is currently the only method for producing fully formed native objects. In the future there will be additional functions and data types for this class to produce additional / more detailed objects. 110 | */ 111 | public func parsedMapItems() -> [PeliasMapkitAnnotation]? { 112 | return parsedMapItems(target: nil, action: nil) 113 | } 114 | 115 | /** 116 | Produces an array of PeliasMapkitAnnotations based off the response from Pelias servers. 117 | 118 | This is currently the only method for producing fully formed native objects. In the future there will be additional functions and data types for this class to produce additional / more detailed objects. 119 | 120 | - Parameter target: An optional target to invoke the selector on when the annotations are clicked. 121 | - Parameter action: An optional selector to be invoked when the created annotations are clicked. 122 | */ 123 | public func parsedMapItems(target: UIResponder?, action: Selector?) -> [PeliasMapkitAnnotation]? { 124 | //TODO: This should get refactored into eventually being a real GeoJSON decoder, and split out the MapItem creation 125 | var mapItems = [PeliasMapkitAnnotation]() 126 | guard let jsonDictionary: Dictionary = parsedResponse?.parsedResponse else { return nil } 127 | guard let featuresArray = jsonDictionary["features"] as? [Dictionary] else { 128 | return nil 129 | } 130 | for feature in featuresArray { 131 | //Address Dictionary for Placemark Creation 132 | let featureProperties = feature["properties"] as? [String:AnyObject] 133 | var addressDictionary = [String:AnyObject]() 134 | addressDictionary[PeliasIDKey] = featureProperties?["id"] 135 | addressDictionary[PeliasDataSourceKey] = featureProperties?["source"] 136 | 137 | //Coordinate Creation 138 | let featureGeometry = feature["geometry"] as? [String:AnyObject] 139 | let geometryPosition = featureGeometry?["coordinates"] as? [Double] 140 | let lat = geometryPosition?[1] ?? 0.0 141 | let lng = geometryPosition?[0] ?? 0.0 142 | let coordinate = CLLocationCoordinate2DMake(lat, lng) 143 | 144 | //MKPlacemark 145 | let name = featureProperties?["label"] as? String 146 | let mapAnnotation = PeliasMapkitAnnotation(coordinate: coordinate, title: name, subtitle: nil, data: addressDictionary) 147 | if let target = target, let action = action { 148 | mapAnnotation.setTarget(target: target, action: action) 149 | } 150 | 151 | mapItems.append(mapAnnotation) 152 | } 153 | return mapItems; 154 | } 155 | } 156 | /// Extension that applies conformance to MKMapItem for older iOS versions (conformance was added it seems in iOS 10) 157 | extension MKMapItem: MKAnnotation { 158 | public var coordinate: CLLocationCoordinate2D{ 159 | get { 160 | return self.placemark.coordinate 161 | } 162 | } 163 | 164 | public var title: String?{ 165 | get { 166 | return self.name 167 | } 168 | } 169 | } 170 | 171 | public extension SearchBoundaryRect { 172 | /** 173 | Initializer for creating a boundary rect based off a Mapkit view's rectangle. It does a bit of math to determine the bounding box based off of map kit's available data. 174 | 175 | - parameter mapRect: An MKMapRect representing the rectangle we wish to bind to (usually whatever the map is displaying currently full screen on the device) 176 | 177 | - returns: A SearchBoundaryRect structure useful for limiting Pelias searches to a particular area 178 | */ 179 | public init(mapRect: MKMapRect){ 180 | //Since we get a coordinate and a size, we need to convert this into the bounding box pelias expects. 181 | //First convert the origin point to the min lat/long 182 | let minCoordinate = MKCoordinateForMapPoint(mapRect.origin) 183 | 184 | //Now we need to figure out the other map point that represents the max 185 | let mapPointMaxX = mapRect.origin.x + mapRect.size.width 186 | let mapPointMaxY = mapRect.origin.y + mapRect.size.height 187 | let mapPointMax = MKMapPoint(x: mapPointMaxX, y: mapPointMaxY) 188 | let maxCoordinate = MKCoordinateForMapPoint(mapPointMax) 189 | 190 | //We use the origin point latitude for max, and subsequently the computed maxLat for pelias's minimum, because pelias wants lower left and upper right points of the rect. 191 | self.maxLatLong = Pelias.GeoPoint(latitude: minCoordinate.latitude, longitude: maxCoordinate.longitude) 192 | self.minLatLong = Pelias.GeoPoint(latitude: maxCoordinate.latitude, longitude: minCoordinate.longitude) 193 | } 194 | } 195 | 196 | public extension Pelias.GeoPoint { 197 | /** 198 | Creates a GeoPoint based of CoreLocation data 199 | 200 | - parameter location: A CLLocation object for lat/long data 201 | 202 | - returns: a full formed GeoPoint 203 | */ 204 | public init? (location: CLLocation?) { 205 | guard let unwrappedLocation = location else { return nil } 206 | self.init(latitude: unwrappedLocation.coordinate.latitude, longitude: unwrappedLocation.coordinate.longitude) 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /MapzenSDK/RoutingController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RoutingController.swift 3 | // ios-sdk 4 | // 5 | // Created by Matt Smollinger on 1/13/17. 6 | // Copyright © 2017 Mapzen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import OnTheRoad 11 | 12 | /** 13 | `RoutingController` is a subclass of On The Road's routing controller and is the main access point in the SDK for querying for routes. 14 | - Note: The routing controller provides API key management and as such is expected to be retrieved via the `controller()` function so everything gets setup correctly. 15 | */ 16 | @objc(MZRoutingController) 17 | open class RoutingController: OTRRoutingController { 18 | 19 | private static let kApiKey = "api_key" 20 | private static let kLanguageKey = "language" 21 | 22 | private var locale = Locale.current 23 | 24 | fileprivate override init() { 25 | super.init() 26 | } 27 | 28 | fileprivate override init(sessionManager : URLSession) { 29 | super.init(sessionManager: sessionManager) 30 | } 31 | 32 | /// Static function that vends a properly configured routing controller. 33 | open static func controller() throws -> RoutingController { 34 | let configuration = URLSessionConfiguration.default 35 | configuration.httpAdditionalHeaders = MapzenManager.sharedManager.httpHeaders() 36 | let session = URLSession.init(configuration: configuration) 37 | return try RoutingController.controller(sessionManager: session) 38 | } 39 | 40 | /** Static function that vends a properly configured routing controller given a session manager. Useful for testing 41 | - parameter sessionManager : URLSession object to use for requests 42 | */ 43 | static func controller(sessionManager : URLSession) throws -> RoutingController { 44 | guard let apiKey = MapzenManager.sharedManager.apiKey else { 45 | throw NSError(domain: MZMapViewController.MapzenGeneralErrorDomain, 46 | code: MZError.apiKeyNotSet.rawValue, 47 | userInfo: nil) 48 | } 49 | let controller = RoutingController.init(sessionManager: sessionManager) 50 | controller.urlQueryComponents.add(URLQueryItem(name: kApiKey, value: apiKey)) 51 | return controller 52 | } 53 | 54 | /** 55 | Set the locale to determine the language the route's directions are in. The default value is Locale.current 56 | 57 | To see a list of supported languages, check the documentation: https://mapzen.com/documentation/mobility/turn-by-turn/api-reference/#directions-options 58 | */ 59 | open func updateLocale(_ l: Locale) { 60 | locale = l 61 | } 62 | 63 | override open func requestRoute(withLocations locations: [OTRRoutingPoint], costingModel costing: OTRRoutingCostingModel, costingOption costingOptions: [String : NSObject]?, directionsOptions: [String : NSObject]? = nil, callback: @escaping (OTRRoutingResult?, Any?, Error?) -> Swift.Void) -> URLSessionDataTask? { 64 | guard let localeLanguage = locale.languageCode else { 65 | return super.requestRoute(withLocations: locations, costingModel: costing, costingOption: costingOptions, directionsOptions: directionsOptions, callback: callback) 66 | } 67 | guard var allDirectionsOptions = directionsOptions else { 68 | let defaultDirectionsOptions = [RoutingController.kLanguageKey : localeLanguage as NSObject] 69 | return super.requestRoute(withLocations: locations, costingModel: costing, costingOption: costingOptions, directionsOptions: defaultDirectionsOptions, callback: callback) 70 | } 71 | 72 | if !allDirectionsOptions.keys.contains(RoutingController.kLanguageKey) { 73 | allDirectionsOptions[RoutingController.kLanguageKey] = localeLanguage as NSObject 74 | } 75 | 76 | return super.requestRoute(withLocations: locations, costingModel: costing, costingOption: costingOptions, directionsOptions: allDirectionsOptions, callback: callback) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /MapzenSDK/SearchDataConverter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SearchDataConverter.swift 3 | // ios-sdk 4 | // 5 | // Created by Sarah Lensing on 3/20/17. 6 | // 7 | // 8 | 9 | import Foundation 10 | import Pelias 11 | 12 | /// Handles converting internal Pelias structs to their cooresponding Mapzen Search objects. 13 | class SearchDataConverter { 14 | 15 | static func unwrapSearchSources(_ sources: [SearchSource]) -> [Pelias.SearchSource] { 16 | var newSources: [Pelias.SearchSource] = [] 17 | for wrapper in sources { 18 | newSources.append(unwrapSearchSource(wrapper)) 19 | } 20 | return newSources 21 | } 22 | 23 | static func unwrapSearchSource(_ source: SearchSource) -> Pelias.SearchSource { 24 | switch source { 25 | case .openStreetMap: 26 | return Pelias.SearchSource.OpenStreetMap 27 | case .openAddresses: 28 | return Pelias.SearchSource.OpenAddresses 29 | case .quattroshapes: 30 | return Pelias.SearchSource.Quattroshapes 31 | case.geoNames: 32 | return Pelias.SearchSource.GeoNames 33 | } 34 | } 35 | 36 | static func wrapSearchSources(_ sources: [Pelias.SearchSource]) -> [SearchSource] { 37 | var newSources: [SearchSource] = [] 38 | for wrapper in sources { 39 | newSources.append(wrapSearchSource(wrapper)) 40 | } 41 | return newSources 42 | } 43 | 44 | static func wrapSearchSource(_ source: Pelias.SearchSource) -> SearchSource { 45 | switch source { 46 | case Pelias.SearchSource.OpenStreetMap: 47 | return SearchSource.openStreetMap 48 | case Pelias.SearchSource.OpenAddresses: 49 | return SearchSource.openAddresses 50 | case Pelias.SearchSource.Quattroshapes: 51 | return SearchSource.quattroshapes 52 | case Pelias.SearchSource.GeoNames: 53 | return SearchSource.geoNames 54 | } 55 | } 56 | 57 | static func unwrapLayerFilters(_ layers: [LayerFilter]) -> [Pelias.LayerFilter] { 58 | var newLayers: [Pelias.LayerFilter] = [] 59 | for wrapper in layers { 60 | newLayers.append(unwrapLayerFilter(wrapper)) 61 | } 62 | return newLayers 63 | } 64 | 65 | static func unwrapLayerFilter(_ layer: LayerFilter) -> Pelias.LayerFilter { 66 | switch layer { 67 | case .address: 68 | return Pelias.LayerFilter.address 69 | case .coarse: 70 | return Pelias.LayerFilter.coarse 71 | case .country: 72 | return Pelias.LayerFilter.country 73 | case .county: 74 | return Pelias.LayerFilter.county 75 | case .localadmin: 76 | return Pelias.LayerFilter.localadmin 77 | case .locality: 78 | return Pelias.LayerFilter.locality 79 | case .neighbourhood: 80 | return Pelias.LayerFilter.neighbourhood 81 | case .region: 82 | return Pelias.LayerFilter.region 83 | case .venue: 84 | return Pelias.LayerFilter.venue 85 | } 86 | } 87 | 88 | static func wrapLayerFilters(_ layers: [Pelias.LayerFilter]) -> [LayerFilter] { 89 | var newLayers: [LayerFilter] = [] 90 | for wrapper in layers { 91 | newLayers.append(wrapLayerFilter(wrapper)) 92 | } 93 | return newLayers 94 | } 95 | 96 | static func wrapLayerFilter(_ layer: Pelias.LayerFilter) -> LayerFilter { 97 | switch layer { 98 | case .address: 99 | return LayerFilter.address 100 | case .coarse: 101 | return LayerFilter.coarse 102 | case .country: 103 | return LayerFilter.country 104 | case .county: 105 | return LayerFilter.county 106 | case .localadmin: 107 | return LayerFilter.localadmin 108 | case .locality: 109 | return LayerFilter.locality 110 | case .neighbourhood: 111 | return LayerFilter.neighbourhood 112 | case .region: 113 | return LayerFilter.region 114 | case .venue: 115 | return LayerFilter.venue 116 | } 117 | } 118 | 119 | static func wrapPoint(_ point: Pelias.GeoPoint) -> GeoPoint { 120 | return GeoPoint(geoPoint: point) 121 | } 122 | 123 | static func unwrapPoint(_ point: GeoPoint) -> Pelias.GeoPoint { 124 | return point.point 125 | } 126 | 127 | } 128 | -------------------------------------------------------------------------------- /MapzenSDK/SearchDataObjects.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SearchDataObjects.swift 3 | // ios-sdk 4 | // 5 | // Created by Sarah Lensing on 3/20/17. 6 | // 7 | // 8 | 9 | import Foundation 10 | import Pelias 11 | 12 | /// Represents a search request's rectangular boundary. 13 | @objc(MZSearchRect) 14 | public class SearchRect: NSObject { 15 | let rect: SearchBoundaryRect 16 | /** 17 | Initialize a rectangle with given min and max points. 18 | - parameter minLatLong: Minimum latitude value. 19 | - parameter maxLatLong: Maximum latitude value. 20 | */ 21 | public init(minLatLong: GeoPoint, maxLatLong: GeoPoint) { 22 | rect = SearchBoundaryRect(minLatLong: minLatLong.point, maxLatLong: maxLatLong.point) 23 | } 24 | 25 | init(boundaryRect: SearchBoundaryRect) { 26 | rect = boundaryRect 27 | } 28 | 29 | public override func isEqual(_ object: Any?) -> Bool { 30 | guard let otherRect = object as? SearchRect else { return false } 31 | return otherRect.rect == rect 32 | } 33 | } 34 | 35 | /// Represents a search request's circular boundary. 36 | @objc(MZSearchCircle) 37 | public class SearchCircle: NSObject { 38 | let circle: SearchBoundaryCircle 39 | /** 40 | Initialize a circle with given center and radius. 41 | - parameter center: Center point. 42 | - parameter radius: Radius in kilometers. 43 | */ 44 | public init(center: GeoPoint, radius: Double) { 45 | circle = SearchBoundaryCircle(center: center.point, radius: radius) 46 | } 47 | 48 | init(boundaryCircle: SearchBoundaryCircle) { 49 | circle = boundaryCircle 50 | } 51 | 52 | public override func isEqual(_ object: Any?) -> Bool { 53 | guard let otherCircle = object as? SearchCircle else { return false } 54 | return otherCircle.circle == circle 55 | } 56 | } 57 | 58 | /// Structure used to represent a coordinate point. 59 | @objc(MZGeoPoint) 60 | public class GeoPoint: NSObject { 61 | let point: Pelias.GeoPoint 62 | /** 63 | Initialize a structure with given latitude and longitude. 64 | - parameter latitude: Latitude. 65 | - parameter longitude: Longitude. 66 | */ 67 | public init(latitude: Double, longitude: Double) { 68 | point = Pelias.GeoPoint(latitude: latitude, longitude: longitude) 69 | } 70 | 71 | init(geoPoint: Pelias.GeoPoint) { 72 | point = geoPoint 73 | } 74 | 75 | public override func isEqual(_ object: Any?) -> Bool { 76 | guard let otherPoint = object as? GeoPoint else { return false } 77 | return otherPoint.point == point 78 | } 79 | } 80 | 81 | /// Represents possible sources for search results to be returned from. 82 | @objc(MZSearchSource) 83 | public enum SearchSource: Int { 84 | case openStreetMap = 1, openAddresses, quattroshapes, geoNames 85 | } 86 | 87 | /// Represents a layer to return results for. 88 | @objc(MZLayerFilter) 89 | public enum LayerFilter: Int { 90 | case venue = 1, address, country, region, county, locality, localadmin, neighbourhood, coarse 91 | } 92 | -------------------------------------------------------------------------------- /MapzenSDK/SearchResponse.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SearchResponse.swift 3 | // ios-sdk 4 | // 5 | // Created by Sarah Lensing on 3/21/17. 6 | // Copyright © 2017 Mapzen. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Pelias 11 | 12 | /// Represents a response for a request executed by 'MapzenSearch' 13 | @objc(MZSearchResponse) 14 | public class SearchResponse : NSObject { 15 | 16 | public let peliasResponse: PeliasResponse 17 | 18 | private lazy var internalParsedResponse: ParsedSearchResponse? = { [unowned self] in 19 | guard let peliasParsedResponse = self.peliasResponse.parsedResponse else { return nil } 20 | return ParsedSearchResponse.init(peliasParsedResponse) 21 | }() 22 | 23 | /// The raw response data 24 | public var data: Data? { 25 | get { 26 | return peliasResponse.data 27 | } 28 | } 29 | /// The url response if the request completed successfully. 30 | public var response: URLResponse? { 31 | get { 32 | return peliasResponse.response 33 | } 34 | } 35 | /// The error if an error occured executing the operation. 36 | public var error: NSError? { 37 | get { 38 | return peliasResponse.error 39 | } 40 | } 41 | 42 | public var parsedResponse: ParsedSearchResponse? { 43 | get { 44 | return internalParsedResponse 45 | } 46 | } 47 | 48 | init(_ response: PeliasResponse) { 49 | peliasResponse = response 50 | } 51 | 52 | public override func isEqual(_ object: Any?) -> Bool { 53 | guard let response = object as? SearchResponse else { return false } 54 | return response.peliasResponse.data == peliasResponse.data && 55 | response.peliasResponse.response == peliasResponse.response && 56 | response.peliasResponse.error == peliasResponse.error 57 | } 58 | } 59 | 60 | @objc(MZParsedSearchResponse) 61 | public class ParsedSearchResponse: NSObject { 62 | 63 | let peliasResponse: PeliasSearchResponse 64 | 65 | public var parsedResponse: Dictionary { 66 | get { 67 | return peliasResponse.parsedResponse 68 | } 69 | } 70 | 71 | init(_ response: PeliasSearchResponse) { 72 | peliasResponse = response 73 | } 74 | 75 | public static func encode(_ response: ParsedSearchResponse) { 76 | PeliasSearchResponse.encode(response.peliasResponse) 77 | } 78 | 79 | public func decode() -> ParsedSearchResponse? { 80 | guard let decoded = PeliasSearchResponse.decode() else { return nil } 81 | return ParsedSearchResponse.init(decoded) 82 | } 83 | } 84 | 85 | -------------------------------------------------------------------------------- /MapzenSDK/TangramExtensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TangramExtensions.swift 3 | // ios-sdk 4 | // 5 | // Created by Matt Smollinger on 1/6/17. 6 | // Copyright © 2017 Mapzen. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import TangramMap 11 | import CoreLocation 12 | import OnTheRoad 13 | 14 | /// Convenience extension to convert between the different coordinate systems in use 15 | public extension TGGeoPoint { 16 | /** 17 | Creates a TGGeoPoint from a CoreLocation coordinate struct. 18 | 19 | - parameter coordinate: The CLLocationCoordinate2D struct to use. 20 | - returns: A TGGeoPoint structure. 21 | */ 22 | public init(coordinate: CLLocationCoordinate2D) { 23 | self.init(longitude: coordinate.longitude, latitude: coordinate.latitude) 24 | } 25 | 26 | /** 27 | Creates a TGGeoPoint from a On The Road Geopoint 28 | 29 | - parameter coordinate: The OTRGeoPoint to use. 30 | - returns: A TGGeoPoint structure. 31 | */ 32 | public init(coordinate: OTRGeoPoint) { 33 | self.init(longitude: coordinate.longitude, latitude: coordinate.latitude) 34 | } 35 | 36 | /** 37 | Create a TGGeoPoint from a CoreLocation location object 38 | 39 | - parameter location: The location object to use 40 | - returns: A TGGeoPoint structure. 41 | */ 42 | public init(location: CLLocation) { 43 | self.init(coordinate: location.coordinate) 44 | } 45 | } 46 | 47 | extension TGGeoPoint: Equatable { 48 | public static func == (lhs: TGGeoPoint, rhs: TGGeoPoint) -> Bool { 49 | return 50 | lhs.latitude == rhs.latitude && 51 | lhs.longitude == rhs.longitude 52 | } 53 | } 54 | 55 | public extension OTRGeoPoint { 56 | /** 57 | Creates a OTRGeoPoint from a TGGeoPoint 58 | 59 | - parameter coordinate: The OTRGeoPoint to use. 60 | - returns: A TGGeoPoint structure. 61 | */ 62 | public init(coordinate: TGGeoPoint) { 63 | self.init(latitude: coordinate.latitude, longitude: coordinate.longitude) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /MapzenSDK/Themes.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Themes.swift 3 | // ios-sdk 4 | // 5 | // Created by Matt Smollinger on 9/28/17. 6 | // Copyright © 2017 Mapzen. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /** 12 | This is the foundational code for custom stylesheet and theme support. This area will likely be the focus of updates & changes over the next several releases, so we would recommend avoiding implementing your own custom stylesheet classes until we have further vetted this implementation. Documentation will be written once we've solidified on the protocol requirements and implementation details. 13 | 14 | We do however welcome suggestions / improvements to this API on our github at https://github.com/mapzen/ios 15 | */ 16 | 17 | @objc(MZStyleSheet) 18 | public protocol StyleSheet : class { 19 | 20 | @objc var fileLocation: URL? { get } 21 | @objc var remoteFileLocation: URL? { get } 22 | @objc var styleSheetRoot: String { get } 23 | @objc var houseStylesRoot: String { get } 24 | @objc var styleSheetFileName: String { get } 25 | @objc var importString: String { get } 26 | @objc var relativePath: String { get } 27 | @objc var mapStyle: MapStyle { get } 28 | @objc var yamlString: String { get } 29 | @objc var detailLevel: Int { get set } 30 | @objc var labelLevel: Int { get set } 31 | @objc var currentColor: String { get set } 32 | 33 | @objc var availableColors: [ String ] { get } 34 | @objc var availableDetailLevels: Int { get } 35 | @objc var availableLabelLevels: Int { get } 36 | } 37 | 38 | @objc(MZBaseStyle) 39 | open class BaseStyle: NSObject, StyleSheet { 40 | 41 | @objc open var detailLevel: Int = 0 42 | @objc open var labelLevel: Int = 0 43 | @objc open var currentColor: String = "" 44 | 45 | @objc open var mapStyle: MapStyle { 46 | get { 47 | return .none 48 | } 49 | } 50 | 51 | @objc open var styleSheetFileName: String { 52 | get { 53 | return "" 54 | } 55 | } 56 | 57 | @objc open var styleSheetRoot: String { 58 | get { 59 | return "" 60 | } 61 | } 62 | 63 | @objc open var fileLocation: URL? { 64 | get { 65 | return Bundle.houseStylesBundle()?.url(forResource: relativePath, withExtension: "yaml") 66 | } 67 | } 68 | 69 | @objc open var remoteFileLocation: URL? { 70 | get { 71 | return nil 72 | } 73 | } 74 | 75 | @objc open var houseStylesRoot: String { 76 | get { 77 | return "housestyles.bundle/" 78 | } 79 | } 80 | 81 | @objc open var importString: String { 82 | get { 83 | return "{ import: [ \(relativePath).yaml, \(yamlString) ] }" 84 | } 85 | } 86 | 87 | @objc open var relativePath: String { 88 | get { 89 | return "\(styleSheetRoot)\(styleSheetFileName)" 90 | } 91 | } 92 | 93 | @objc open var yamlString: String { 94 | get { 95 | return "" 96 | } 97 | } 98 | 99 | @objc open var availableColors: [String] { 100 | get { 101 | return [] 102 | } 103 | } 104 | 105 | @objc open var availableDetailLevels: Int { 106 | get { 107 | return 0 108 | } 109 | } 110 | 111 | @objc open var availableLabelLevels: Int { 112 | get { 113 | return 0 114 | } 115 | } 116 | } 117 | 118 | //MARK:- Bubble Wrap 119 | @objc(MZBubbleWrapStyle) 120 | open class BubbleWrapStyle: BaseStyle { 121 | public override init() { 122 | super.init() 123 | defer { 124 | currentColor = "" // Not used for Bubble Wrap 125 | labelLevel = 5 126 | detailLevel = 0 // Not used for Bubble Wrap 127 | } 128 | } 129 | 130 | @objc open override var mapStyle: MapStyle { 131 | get { 132 | return .bubbleWrap 133 | } 134 | } 135 | 136 | @objc open override var styleSheetFileName: String { 137 | get { 138 | return "bubble-wrap-style" 139 | } 140 | } 141 | 142 | @objc open override var styleSheetRoot: String { 143 | get { 144 | return "bubble-wrap/" 145 | } 146 | } 147 | 148 | @objc override open var availableLabelLevels: Int { 149 | get { 150 | return 12 151 | } 152 | } 153 | 154 | @objc override open var yamlString: String { 155 | get { 156 | return "\(styleSheetRoot)themes/label-\(labelLevel).yaml" 157 | } 158 | } 159 | } 160 | 161 | //MARK:- Cinnnabar 162 | @objc(MZCinnabarStyle) 163 | open class CinnabarStyle: BaseStyle { 164 | public override init() { 165 | super.init() 166 | defer { 167 | currentColor = "" // Not used for Cinnabar 168 | labelLevel = 5 169 | detailLevel = 0 // Not used for Cinnabar 170 | } 171 | } 172 | 173 | @objc open override var mapStyle: MapStyle { 174 | get { 175 | return .cinnabar 176 | } 177 | } 178 | 179 | @objc override open var styleSheetFileName: String { 180 | get { 181 | return "cinnabar-style" 182 | } 183 | } 184 | 185 | @objc override open var styleSheetRoot: String { 186 | get { 187 | return "cinnabar/" 188 | } 189 | } 190 | 191 | @objc override open var availableLabelLevels: Int { 192 | get { 193 | return 12 194 | } 195 | } 196 | 197 | @objc override open var yamlString: String { 198 | get { 199 | return "\(styleSheetRoot)themes/label-\(labelLevel).yaml" 200 | } 201 | } 202 | } 203 | 204 | //MARK:- Refill 205 | @objc(MZRefillStyle) 206 | open class RefillStyle: BaseStyle { 207 | public override init() { 208 | super.init() 209 | defer { 210 | currentColor = "black" 211 | labelLevel = 5 212 | detailLevel = 10 213 | } 214 | } 215 | 216 | @objc open override var mapStyle: MapStyle { 217 | get { 218 | return .refill 219 | } 220 | } 221 | 222 | @objc override open var styleSheetFileName: String { 223 | get { 224 | return "refill-style" 225 | } 226 | } 227 | 228 | @objc override open var styleSheetRoot: String { 229 | get { 230 | return "refill/" 231 | } 232 | } 233 | 234 | @objc override open var availableLabelLevels: Int { 235 | get { 236 | return 12 237 | } 238 | } 239 | 240 | @objc override open var availableDetailLevels: Int { 241 | get { 242 | return 12 243 | } 244 | } 245 | 246 | @objc override open var availableColors: [String] { 247 | get { 248 | return ["black", "blue-gray", "blue", "brown-orange", "gray-gold", "gray", "high-contrast", "inverted", "pink-yellow", "pink", "purple-green", "sepia", "zinc"] 249 | } 250 | } 251 | 252 | @objc override open var yamlString: String { 253 | get { 254 | return "\(styleSheetRoot)themes/label-\(labelLevel).yaml, \(styleSheetRoot)themes/detail-\(detailLevel).yaml, \(styleSheetRoot)themes/color-\(currentColor).yaml" 255 | } 256 | } 257 | } 258 | 259 | //MARK:- Zinc 260 | @objc(MZZincStyle) 261 | open class ZincStyle: RefillStyle { 262 | public override init() { 263 | super.init() 264 | defer { 265 | currentColor = "zinc" 266 | } 267 | } 268 | 269 | @objc open override var mapStyle: MapStyle { 270 | get { 271 | return .zinc 272 | } 273 | } 274 | } 275 | 276 | //MARK:- Walkabout 277 | @objc(MZWalkaboutStyle) 278 | open class WalkaboutStyle: BaseStyle { 279 | public override init() { 280 | super.init() 281 | defer { 282 | currentColor = "" // Not used for Walkabout 283 | labelLevel = 5 284 | detailLevel = 0 // Not used for Walkabout 285 | } 286 | } 287 | 288 | @objc open override var mapStyle: MapStyle { 289 | get { 290 | return .walkabout 291 | } 292 | } 293 | 294 | @objc override open var styleSheetFileName: String{ 295 | get { 296 | return "walkabout-style" 297 | } 298 | } 299 | 300 | @objc override open var styleSheetRoot: String { 301 | get { 302 | return "walkabout/" 303 | } 304 | } 305 | 306 | @objc override open var availableLabelLevels: Int { 307 | get { 308 | return 12 309 | } 310 | } 311 | 312 | @objc override open var yamlString: String { 313 | get { 314 | return "\(styleSheetRoot)themes/label-\(labelLevel).yaml" 315 | } 316 | } 317 | } 318 | -------------------------------------------------------------------------------- /MapzenSDK/UIColorExtensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIColorExtensions.swift 3 | // ios-sdk 4 | // 5 | // Created by Sarah Lensing on 3/22/17. 6 | // Copyright © 2017 Mapzen. All rights reserved. 7 | // 8 | import UIKit 9 | import CoreGraphics 10 | 11 | extension UIColor { 12 | func hexValue() -> String { 13 | var r: CGFloat = 1 14 | var g: CGFloat = 1 15 | var b: CGFloat = 1 16 | var a: CGFloat = 1 17 | getRed(&r, green: &g, blue: &b, alpha: &a) 18 | let red = Float(r * 255) 19 | let green = Float(g * 255) 20 | let blue = Float(b * 255) 21 | let alpha = Float(a * 255) 22 | return String.init(format: "#%02lX%02lX%02lX%02lX", lroundf(alpha), lroundf(red), lroundf(green), lroundf(blue)) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /MapzenSDK/dimensions/Dimensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Dimensions.swift 3 | // ios-sdk 4 | // 5 | // Created by Sarah Lensing on 2/24/17. 6 | // Copyright © 2017 Mapzen. All rights reserved. 7 | // 8 | 9 | import CoreGraphics 10 | 11 | // Holds constant values for UI element properties such as padding and width/height 12 | public class Dimensions { 13 | // Default padding used for map UI elements 14 | public static let defaultPadding : CGFloat = 10.0 15 | // Size of all square map buttons such as the "find me" button 16 | public static let squareMapBtnSize : CGFloat = 48.0 17 | } 18 | -------------------------------------------------------------------------------- /MapzenSDKTests/DictionaryExtensionsTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DictionaryExtensionsTests.swift 3 | // ios-sdk 4 | // 5 | // Created by Sarah Lensing on 2/27/17. 6 | // Copyright © 2017 Mapzen. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import MapzenSDK 11 | 12 | class DictionaryExtensionsTests : XCTestCase { 13 | 14 | let numbers = [1: "1", 2: "2", 3: "3" ] 15 | let phones = ["sarah": "iphone", "matt": "iphone", "chuck": "pixel"] 16 | 17 | func testNumbersKeysForValue() { 18 | let keys = numbers.keysForValue(value: "2") 19 | XCTAssertEqual(keys.count, 1) 20 | XCTAssertEqual(keys.first, 2) 21 | } 22 | 23 | func testNumbersKeysForValueEmpty() { 24 | let keys = numbers.keysForValue(value: "4") 25 | XCTAssertTrue(keys.isEmpty) 26 | } 27 | 28 | func testNumbersKeyForValue() { 29 | let key = numbers.keyForValue(value: "1") 30 | XCTAssertEqual(key, 1) 31 | } 32 | 33 | func testNumbersKeyForValueNil() { 34 | let key = numbers.keyForValue(value: "4") 35 | XCTAssertNil(key) 36 | } 37 | 38 | func testPhonesKeysForValue() { 39 | let keys = phones.keysForValue(value: "iphone") 40 | XCTAssertEqual(keys.count, 2) 41 | XCTAssertTrue(keys.contains("sarah")) 42 | XCTAssertTrue(keys.contains("matt")) 43 | } 44 | 45 | func testPhonesKeysForValueEmpty() { 46 | let keys = phones.keysForValue(value: "razr") 47 | XCTAssertTrue(keys.isEmpty) 48 | } 49 | 50 | func testPhonesKeyForValue() { 51 | let key = phones.keyForValue(value: "pixel") 52 | XCTAssertEqual(key, "chuck") 53 | } 54 | 55 | func testPhonesKeyForValueNil() { 56 | let key = phones.keyForValue(value: "razr") 57 | XCTAssertNil(key) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /MapzenSDKTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /MapzenSDKTests/MapzenManagerTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MapzenManagerTests.swift 3 | // ios-sdk 4 | // 5 | // Created by Matt Smollinger on 1/12/17. 6 | // Copyright © 2017 Mapzen. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import MapzenSDK 11 | import Pelias 12 | 13 | 14 | class MapzenManagerTests: XCTestCase { 15 | func testSingleton() { 16 | XCTAssertTrue(MapzenManager.sharedManager == MapzenManager.sharedManager) 17 | } 18 | 19 | func testApiKeyMapNotSet() { 20 | //Setup 21 | MapzenManager.sharedManager.apiKey = nil 22 | let map = MZMapViewController() 23 | 24 | XCTAssertThrowsError(try map.loadStyle(.bubbleWrap)) { (error) -> Void in 25 | let error = error as NSError 26 | XCTAssertTrue(error.code == MZError.apiKeyNotSet.rawValue) 27 | } 28 | } 29 | 30 | func testApiKeyMapSet() { 31 | //Setup 32 | MapzenManager.sharedManager.apiKey = "1234" 33 | let map = MZMapViewController() 34 | 35 | //Tests 36 | do { 37 | try map.loadStyle(.bubbleWrap) 38 | } catch { 39 | XCTFail() 40 | } 41 | } 42 | 43 | func testApiKeyRouteNotSet() { 44 | //Setup 45 | MapzenManager.sharedManager.apiKey = nil 46 | 47 | //Tests 48 | XCTAssertThrowsError(try RoutingController.controller()) { (error) -> Void in 49 | let error = error as NSError 50 | XCTAssertTrue(error.code == MZError.apiKeyNotSet.rawValue) 51 | } 52 | } 53 | 54 | func testApiKeyRouteSet () { 55 | //Setup 56 | MapzenManager.sharedManager.apiKey = "1234" 57 | 58 | //Tests 59 | do { 60 | let _ = try RoutingController.controller() 61 | } catch { 62 | XCTFail() 63 | } 64 | } 65 | 66 | func testApiKeySearch() { 67 | //Setup 68 | MapzenManager.sharedManager.apiKey = "1234" 69 | 70 | //Tests 71 | //This now implicitly tests the KVO implementation used under the covers. Unsure if we should make this more explicit or not in the future. 72 | guard let queryParams = MapzenSearch.sharedInstance.urlQueryItems else { 73 | XCTFail("urlQueryItems should be initialized by this point, but aren't") 74 | return 75 | } 76 | //There should only be one at this point 77 | XCTAssertTrue(queryParams[0].value == "1234") 78 | 79 | //Now test it gets removed 80 | MapzenManager.sharedManager.apiKey = nil 81 | guard let params = PeliasSearchManager.sharedInstance.urlQueryItems else { 82 | XCTFail("urlQueryItems should be initialized by this point, but aren't") 83 | return 84 | } 85 | XCTAssertTrue(params.count == 0) 86 | } 87 | 88 | func testHttpHeaders() { 89 | let headers = MapzenManager.sharedManager.httpHeaders() 90 | guard let userAgentString = headers["User-Agent"] else { 91 | XCTFail("User-Agent header is nil") 92 | return 93 | } 94 | XCTAssertTrue(userAgentString.contains("ios-sdk")) 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /MapzenSDKTests/MapzenSearchTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MapzenSearchTests.swift 3 | // ios-sdk 4 | // 5 | // Created by Sarah Lensing on 3/28/17. 6 | // Copyright © 2017 Mapzen. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import MapzenSDK 11 | import Pelias 12 | 13 | public class MapzenSearchTests : XCTestCase { 14 | 15 | let mapzenSearch = MapzenSearch.sharedInstance 16 | let peliasSearchManager = PeliasSearchManager.sharedInstance 17 | 18 | func testAutocompleteTimeDelay() { 19 | mapzenSearch.autocompleteTimeDelay = 3 20 | XCTAssertEqual(3, mapzenSearch.autocompleteTimeDelay) 21 | XCTAssertEqual(3, peliasSearchManager.autocompleteTimeDelay) 22 | } 23 | 24 | func testBaseUrl() { 25 | let defaultBaseUrl = URL.init(string: Constants.URL.base) 26 | XCTAssertEqual(mapzenSearch.baseUrl, defaultBaseUrl) 27 | XCTAssertEqual(peliasSearchManager.baseUrl, defaultBaseUrl) 28 | 29 | let baseUrl = URL.init(string: "https://search.custom.com")! 30 | mapzenSearch.baseUrl = baseUrl 31 | XCTAssertEqual(mapzenSearch.baseUrl, baseUrl) 32 | XCTAssertEqual(peliasSearchManager.baseUrl, baseUrl) 33 | } 34 | 35 | func testQueryItems() { 36 | let items = [URLQueryItem.init(name: "test", value: "val")] 37 | mapzenSearch.urlQueryItems = items 38 | XCTAssertEqual(mapzenSearch.urlQueryItems!, items) 39 | XCTAssertEqual(peliasSearchManager.urlQueryItems!, items) 40 | } 41 | 42 | func testSearchQuery() { 43 | let config = SearchConfig.init(searchText: "test") { (response) in 44 | // 45 | } 46 | let operation = mapzenSearch.search(config) 47 | XCTAssertNotNil(operation) 48 | } 49 | 50 | func testReverseQuery() { 51 | let config = ReverseConfig.init(point: GeoPoint.init(latitude: 40, longitude: 40)) { (response) in 52 | // 53 | } 54 | let operation = mapzenSearch.reverseGeocode(config) 55 | XCTAssertNotNil(operation) 56 | } 57 | 58 | func testAutocompleteQuery() { 59 | let config = AutocompleteConfig.init(searchText: "test", focusPoint: GeoPoint.init(latitude: 40, longitude: 40)) { (response) in 60 | // 61 | } 62 | let operation = mapzenSearch.autocompleteQuery(config) 63 | XCTAssertNotNil(operation) 64 | } 65 | 66 | func testPlaceQuery() { 67 | let config = PlaceConfig.init(gids: [""]) { (response) in 68 | // 69 | } 70 | let operation = mapzenSearch.placeQuery(config) 71 | XCTAssertNotNil(operation) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /MapzenSDKTests/RoutingControllerTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RoutingControllerTests.swift 3 | // ios-sdk 4 | // 5 | // Created by Sarah Lensing on 3/13/17. 6 | // Copyright © 2017 Mapzen. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import MapzenSDK 11 | @testable import OnTheRoad 12 | 13 | class RoutingControllerTests : XCTestCase { 14 | 15 | let testSessionManager : TestUrlSession = TestUrlSession() 16 | var router : RoutingController? = nil 17 | 18 | override func setUp() { 19 | MapzenManager.sharedManager.apiKey = "testKey" 20 | try? router = RoutingController.controller(sessionManager: testSessionManager) 21 | } 22 | 23 | func testRouterSendCurrentLocaleLanguageByDefault() { 24 | let loc1 = OTRRoutingPoint() 25 | let loc2 = OTRRoutingPoint() 26 | let _ = router?.requestRoute(withLocations: [loc1, loc2], costingModel: .auto, costingOption: nil, directionsOptions: nil) { (result, any, error) in 27 | // 28 | } 29 | let directionsOptions = testSessionManager.queryParameters?["directions_options"] 30 | XCTAssertNotNil(directionsOptions) 31 | let optionsLanguage = directionsOptions!["language"] as AnyObject 32 | let localeLanguage = Locale.current.languageCode! as String 33 | XCTAssertEqual(optionsLanguage as? String, localeLanguage) 34 | } 35 | 36 | func testPassingDirectionsOptionsSendsCorrectLanguage() { 37 | let loc1 = OTRRoutingPoint() 38 | let loc2 = OTRRoutingPoint() 39 | let options = ["language" : "fr"] 40 | let _ = router?.requestRoute(withLocations: [loc1, loc2], costingModel: .auto, costingOption: nil, directionsOptions: options as [String : NSObject]?) { (result, any, error) in 41 | // 42 | } 43 | let directionsOptions = testSessionManager.queryParameters?["directions_options"] 44 | XCTAssertNotNil(directionsOptions) 45 | let optionsLanguage = directionsOptions!["language"] as AnyObject 46 | XCTAssertEqual(optionsLanguage as? String, "fr") 47 | } 48 | 49 | func testUpdatingLocaleSendsCorrectLanguage() { 50 | let loc1 = OTRRoutingPoint() 51 | let loc2 = OTRRoutingPoint() 52 | let locale = Locale.init(identifier: "fr-FR") 53 | router?.updateLocale(locale) 54 | let _ = router?.requestRoute(withLocations: [loc1, loc2], costingModel: .auto, costingOption: nil, directionsOptions: nil) { (result, any, error) in 55 | // 56 | } 57 | let directionsOptions = testSessionManager.queryParameters?["directions_options"] 58 | XCTAssertNotNil(directionsOptions) 59 | let optionsLanguage = directionsOptions!["language"] as AnyObject 60 | XCTAssertEqual(optionsLanguage as? String, locale.languageCode) 61 | } 62 | 63 | } 64 | 65 | -------------------------------------------------------------------------------- /MapzenSDKTests/SearchConfigsTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SearchConfigsTests.swift 3 | // ios-sdk 4 | // 5 | // Created by Sarah Lensing on 3/23/17. 6 | // Copyright © 2017 Mapzen. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import MapzenSDK 11 | 12 | class AutocompleteConfigTests: XCTestCase { 13 | 14 | var config: AutocompleteConfig = AutocompleteConfig.init(searchText: "test", focusPoint: GeoPoint.init(latitude: 70.0, longitude: 40.0), completionHandler: { (response) in }) 15 | 16 | func testFocusPointIsCorrect() { 17 | XCTAssertEqual(GeoPoint(latitude: 70.0, longitude: 40.0), config.focusPoint) 18 | } 19 | 20 | func testSearchTestIsCorrect() { 21 | XCTAssertEqual("test", config.searchText) 22 | } 23 | 24 | } 25 | 26 | class PlaceConfigTests: XCTestCase { 27 | 28 | var config: PlaceConfig = PlaceConfig.init(gids: ["123"], completionHandler: { (response) in }) 29 | 30 | func testPlacesAreCorrect() { 31 | XCTAssertEqual(config.gids.count, 1) 32 | XCTAssertEqual("123", config.gids.first) 33 | } 34 | } 35 | 36 | class ReverseConfigTests: XCTestCase { 37 | 38 | var config: ReverseConfig = ReverseConfig.init(point: GeoPoint.init(latitude: 70.0, longitude: 40.0), completionHandler: { (response) in }) 39 | 40 | func testPointIsCorrect() { 41 | XCTAssertEqual(GeoPoint(latitude: 70.0, longitude: 40.0), config.point) 42 | } 43 | 44 | func testNumOfResults() { 45 | config.numberOfResults = 8 46 | XCTAssertEqual(8, config.numberOfResults) 47 | } 48 | 49 | func testBoundaryCountry() { 50 | config.boundaryCountry = "US" 51 | XCTAssertEqual("US", config.boundaryCountry) 52 | } 53 | 54 | func testDataSources() { 55 | config.dataSources = [.geoNames, .openAddresses] 56 | XCTAssertEqual([.geoNames, .openAddresses], config.dataSources!) 57 | } 58 | 59 | func testLayers() { 60 | config.layers = [.address, .country] 61 | XCTAssertEqual([.address, .country], config.layers!) 62 | } 63 | } 64 | 65 | class SearchConfigTests: XCTestCase { 66 | 67 | var config: SearchConfig = SearchConfig.init(searchText: "test", completionHandler: { (response) in }) 68 | 69 | func testSearchTextIsCorrect() { 70 | XCTAssertEqual("test", config.searchText) 71 | } 72 | 73 | func testNumOfResults() { 74 | config.numberOfResults = 8 75 | XCTAssertEqual(8, config.numberOfResults) 76 | } 77 | 78 | func testBoundaryCountry() { 79 | config.boundaryCountry = "US" 80 | XCTAssertEqual("US", config.boundaryCountry) 81 | } 82 | 83 | func testBoundaryRect() { 84 | let rect = SearchRect(minLatLong: GeoPoint(latitude: 70.0, longitude: 40.0), maxLatLong: GeoPoint(latitude: 75.0, longitude: 40.0)) 85 | config.boundaryRect = rect 86 | XCTAssertEqual(rect, config.boundaryRect) 87 | } 88 | 89 | func testBoundaryCircle() { 90 | let circle = SearchCircle(center: GeoPoint(latitude: 70.0, longitude: 40.0), radius: 10) 91 | config.boundaryCircle = circle 92 | XCTAssertEqual(circle, config.boundaryCircle) 93 | } 94 | 95 | func testFocusPoint() { 96 | let point = GeoPoint(latitude: 70.0, longitude: 40.0) 97 | config.focusPoint = point 98 | XCTAssertEqual(point, config.focusPoint) 99 | } 100 | 101 | func testDataSources() { 102 | config.dataSources = [.geoNames, .openAddresses] 103 | XCTAssertEqual([.geoNames, .openAddresses], config.dataSources!) 104 | } 105 | 106 | func testLayers() { 107 | config.layers = [.address, .country] 108 | XCTAssertEqual([.address, .country], config.layers!) 109 | } 110 | } 111 | 112 | -------------------------------------------------------------------------------- /MapzenSDKTests/SearchDataConverterTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SearchDataConverterTests.swift 3 | // ios-sdk 4 | // 5 | // Created by Sarah Lensing on 3/21/17. 6 | // Copyright © 2017 Mapzen. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import MapzenSDK 11 | import Pelias 12 | 13 | class SearchDataConverterTests: XCTestCase { 14 | 15 | func testUnwrapSearchSources() { 16 | let sources = [SearchSource.geoNames, SearchSource.openAddresses, SearchSource.openStreetMap, SearchSource.quattroshapes] 17 | let unwrapped = SearchDataConverter.unwrapSearchSources(sources) 18 | XCTAssertTrue(unwrapped.contains(.GeoNames)) 19 | XCTAssertTrue(unwrapped.contains(.OpenAddresses)) 20 | XCTAssertTrue(unwrapped.contains(.OpenStreetMap)) 21 | XCTAssertTrue(unwrapped.contains(.Quattroshapes)) 22 | XCTAssertEqual(unwrapped.count, 4) 23 | } 24 | 25 | func testUnwrapSearchSource() { 26 | XCTAssertEqual(SearchDataConverter.unwrapSearchSource(.openStreetMap), .OpenStreetMap) 27 | XCTAssertEqual(SearchDataConverter.unwrapSearchSource(.openAddresses), .OpenAddresses) 28 | XCTAssertEqual(SearchDataConverter.unwrapSearchSource(.quattroshapes), .Quattroshapes) 29 | XCTAssertEqual(SearchDataConverter.unwrapSearchSource(.geoNames), .GeoNames) 30 | } 31 | 32 | func testWrapSearchSources() { 33 | let sources = [Pelias.SearchSource.GeoNames, Pelias.SearchSource.OpenAddresses, Pelias.SearchSource.OpenStreetMap, Pelias.SearchSource.Quattroshapes] 34 | let wrapped = SearchDataConverter.wrapSearchSources(sources) 35 | XCTAssertTrue(wrapped.contains(.geoNames)) 36 | XCTAssertTrue(wrapped.contains(.openAddresses)) 37 | XCTAssertTrue(wrapped.contains(.openStreetMap)) 38 | XCTAssertTrue(wrapped.contains(.quattroshapes)) 39 | XCTAssertEqual(wrapped.count, 4) 40 | 41 | } 42 | 43 | func testWrapSearchSource() { 44 | XCTAssertEqual(SearchDataConverter.wrapSearchSource(.OpenStreetMap), .openStreetMap) 45 | XCTAssertEqual(SearchDataConverter.wrapSearchSource(.OpenAddresses), .openAddresses) 46 | XCTAssertEqual(SearchDataConverter.wrapSearchSource(.Quattroshapes), .quattroshapes) 47 | XCTAssertEqual(SearchDataConverter.wrapSearchSource(.GeoNames), .geoNames) 48 | } 49 | 50 | func testUnwrapLayerFilters() { 51 | let layers = [MapzenSDK.LayerFilter.venue, LayerFilter.address, LayerFilter.country, LayerFilter.region, LayerFilter.locality, LayerFilter.localadmin, LayerFilter.neighbourhood, LayerFilter.coarse] 52 | let unwrapped = SearchDataConverter.unwrapLayerFilters(layers) 53 | XCTAssertTrue(unwrapped.contains(.venue)) 54 | XCTAssertTrue(unwrapped.contains(.address)) 55 | XCTAssertTrue(unwrapped.contains(.country)) 56 | XCTAssertTrue(unwrapped.contains(.region)) 57 | XCTAssertTrue(unwrapped.contains(.locality)) 58 | XCTAssertTrue(unwrapped.contains(.localadmin)) 59 | XCTAssertTrue(unwrapped.contains(.neighbourhood)) 60 | XCTAssertTrue(unwrapped.contains(.coarse)) 61 | XCTAssertEqual(unwrapped.count, 8) 62 | } 63 | 64 | func testUnwrapLayerFilter() { 65 | XCTAssertEqual(SearchDataConverter.unwrapLayerFilter(.venue), .venue) 66 | XCTAssertEqual(SearchDataConverter.unwrapLayerFilter(.address), .address) 67 | XCTAssertEqual(SearchDataConverter.unwrapLayerFilter(.country), .country) 68 | XCTAssertEqual(SearchDataConverter.unwrapLayerFilter(.region), .region) 69 | XCTAssertEqual(SearchDataConverter.unwrapLayerFilter(.locality), .locality) 70 | XCTAssertEqual(SearchDataConverter.unwrapLayerFilter(.localadmin), .localadmin) 71 | XCTAssertEqual(SearchDataConverter.unwrapLayerFilter(.neighbourhood), .neighbourhood) 72 | XCTAssertEqual(SearchDataConverter.unwrapLayerFilter(.coarse), .coarse) 73 | } 74 | 75 | func testWrapLayerFilters() { 76 | let layers = [Pelias.LayerFilter.venue, LayerFilter.address, LayerFilter.country, LayerFilter.region, LayerFilter.locality, LayerFilter.localadmin, LayerFilter.neighbourhood, LayerFilter.coarse] 77 | let wrapped = SearchDataConverter.wrapLayerFilters(layers) 78 | XCTAssertTrue(wrapped.contains(.venue)) 79 | XCTAssertTrue(wrapped.contains(.address)) 80 | XCTAssertTrue(wrapped.contains(.country)) 81 | XCTAssertTrue(wrapped.contains(.region)) 82 | XCTAssertTrue(wrapped.contains(.locality)) 83 | XCTAssertTrue(wrapped.contains(.localadmin)) 84 | XCTAssertTrue(wrapped.contains(.neighbourhood)) 85 | XCTAssertTrue(wrapped.contains(.coarse)) 86 | XCTAssertEqual(wrapped.count, 8) 87 | } 88 | 89 | func testWrapLayerFilter() { 90 | XCTAssertEqual(SearchDataConverter.wrapLayerFilter(.venue), .venue) 91 | XCTAssertEqual(SearchDataConverter.wrapLayerFilter(.address), .address) 92 | XCTAssertEqual(SearchDataConverter.wrapLayerFilter(.country), .country) 93 | XCTAssertEqual(SearchDataConverter.wrapLayerFilter(.region), .region) 94 | XCTAssertEqual(SearchDataConverter.wrapLayerFilter(.locality), .locality) 95 | XCTAssertEqual(SearchDataConverter.wrapLayerFilter(.localadmin), .localadmin) 96 | XCTAssertEqual(SearchDataConverter.wrapLayerFilter(.neighbourhood), .neighbourhood) 97 | XCTAssertEqual(SearchDataConverter.wrapLayerFilter(.coarse), .coarse) 98 | } 99 | 100 | func testWrapPoint() { 101 | let point = Pelias.GeoPoint(latitude: 70.0, longitude: 40.0) 102 | let wrapped = SearchDataConverter.wrapPoint(point) 103 | XCTAssertEqual(point, wrapped.point) 104 | } 105 | 106 | func testUnwrapPoint() { 107 | let point = MapzenSDK.GeoPoint(latitude: 70.0, longitude: 40.0) 108 | let unwrapped = SearchDataConverter.unwrapPoint(point) 109 | XCTAssertEqual(point.point, unwrapped) 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /MapzenSDKTests/SearchDataObjectsTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MapzenSearchDataObjectsTests.swift 3 | // ios-sdk 4 | // 5 | // Created by Sarah Lensing on 3/21/17. 6 | // Copyright © 2017 Mapzen. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import MapzenSDK 11 | import Pelias 12 | 13 | class MapzenSearchDataObjectsTests: XCTestCase { 14 | 15 | func testSearchRect() { 16 | let min = MapzenSDK.GeoPoint(latitude: 70.0, longitude: 40.0) 17 | let max = MapzenSDK.GeoPoint(latitude: 80.0, longitude: 50.0) 18 | let searchRect = SearchRect(minLatLong: min, maxLatLong: max) 19 | let rect = SearchBoundaryRect(minLatLong: SearchDataConverter.unwrapPoint(min), maxLatLong: SearchDataConverter.unwrapPoint(max)) 20 | XCTAssertEqual(searchRect.rect, rect) 21 | XCTAssertEqual(searchRect, SearchRect(minLatLong: min, maxLatLong: max)) 22 | } 23 | 24 | func testSearchCircle() { 25 | let center = MapzenSDK.GeoPoint(latitude: 70.0, longitude: 40.0) 26 | let searchCircle = SearchCircle(center: center, radius: 8) 27 | let circle = SearchBoundaryCircle(center: SearchDataConverter.unwrapPoint(center), radius: 8) 28 | XCTAssertEqual(searchCircle.circle, circle) 29 | XCTAssertEqual(searchCircle, SearchCircle(center: center, radius: 8)) 30 | } 31 | 32 | func testGeoPoint() { 33 | let point = MapzenSDK.GeoPoint(latitude: 70.0, longitude: 40.0) 34 | let peliasPoint = Pelias.GeoPoint(latitude: 70.0, longitude: 40.0) 35 | XCTAssertEqual(point.point, peliasPoint) 36 | XCTAssertEqual(point, GeoPoint(latitude: 70.0, longitude: 40.0)) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /MapzenSDKTests/SearchResponseTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SearchResponseTests.swift 3 | // ios-sdk 4 | // 5 | // Created by Sarah Lensing on 3/21/17. 6 | // Copyright © 2017 Mapzen. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import MapzenSDK 11 | import Pelias 12 | 13 | class SearchResponseTests: XCTestCase { 14 | var data = Data() 15 | let urlResponse = URLResponse() 16 | let error = NSError(domain:"Test", code: 1, userInfo: nil) 17 | var response: SearchResponse? 18 | var peliasResponse:PeliasResponse? 19 | 20 | override func setUp() { 21 | peliasResponse = PeliasResponse(data: data, response: urlResponse, error: error) 22 | response = SearchResponse(peliasResponse!) 23 | } 24 | 25 | func testData() { 26 | XCTAssertEqual(response?.peliasResponse.data, data) 27 | XCTAssertEqual(response?.data, data) 28 | } 29 | 30 | func testResponse() { 31 | XCTAssertEqual(response?.peliasResponse.response, urlResponse) 32 | XCTAssertEqual(response?.response, urlResponse) 33 | } 34 | 35 | func testError() { 36 | let error = NSError(domain:"Test", code: 1, userInfo: nil) 37 | XCTAssertEqual(response?.peliasResponse.error, error) 38 | XCTAssertEqual(response?.error, error) 39 | } 40 | 41 | func testParsedResponse() { 42 | let dict = ["type":"FeatureCollection"] 43 | try? data = JSONSerialization.data(withJSONObject: dict, options: .prettyPrinted) 44 | let pr = PeliasResponse(data: data, response: urlResponse, error: error) 45 | let r = SearchResponse(pr) 46 | XCTAssertEqual(r.parsedResponse?.parsedResponse.keys.count, 1) 47 | XCTAssertEqual(r.parsedResponse?.parsedResponse["type"] as! String, "FeatureCollection") 48 | } 49 | 50 | func testEquals() { 51 | let otherResponse = SearchResponse(PeliasResponse(data: Data(), response: urlResponse, error: NSError(domain:"Test", code: 1, userInfo: nil))) 52 | XCTAssertEqual(response, otherResponse) 53 | } 54 | } 55 | 56 | class ParsedSearchResponseTests: XCTestCase { 57 | //Disabled for now until we fix this in Pelias 58 | // func testEncodeDecode() { 59 | // let dict = ["test":"result"] 60 | // let pr = PeliasSearchResponse(parsedResponse: dict) 61 | // let parsedResponse = ParsedSearchResponse.init(pr) 62 | // ParsedSearchResponse.encode(parsedResponse) 63 | // let decoded = parsedResponse.decode() 64 | // XCTAssertEqual(decoded?.peliasResponse.parsedResponse.keys.count, 1) 65 | // XCTAssertEqual(decoded?.peliasResponse.parsedResponse["test"] as! String, "result") 66 | // } 67 | } 68 | -------------------------------------------------------------------------------- /MapzenSDKTests/TGMapViewControllerMocks.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestTGMapViewController.swift 3 | // ios-sdk 4 | // 5 | // Created by Sarah Lensing on 2/15/17. 6 | // Copyright © 2017 Mapzen. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import TangramMap 11 | import XCTest 12 | 13 | class FailingTGMapViewController: TestTGMapViewController { 14 | override func animate(toPosition position: TGGeoPoint, withDuration seconds: Float) { 15 | XCTFail() 16 | } 17 | 18 | override func animate(toPosition position: TGGeoPoint, withDuration seconds: Float, with easeType: TGEaseType) { 19 | XCTFail() 20 | } 21 | 22 | override func animate(toZoomLevel zoomLevel: Float, withDuration seconds: Float) { 23 | XCTFail() 24 | } 25 | 26 | override func animate(toZoomLevel zoomLevel: Float, withDuration seconds: Float, with easeType: TGEaseType) { 27 | XCTFail() 28 | } 29 | 30 | override func animate(toRotation radians: Float, withDuration seconds: Float) { 31 | XCTFail() 32 | } 33 | 34 | override func animate(toRotation radians: Float, withDuration seconds: Float, with easeType: TGEaseType) { 35 | XCTFail() 36 | } 37 | override func animate(toTilt radians: Float, withDuration seconds: Float) { 38 | XCTFail() 39 | } 40 | 41 | override func animate(toTilt radians: Float, withDuration seconds: Float, with easeType: TGEaseType) { 42 | XCTFail() 43 | } 44 | } 45 | 46 | class TestTGMapViewController: TGMapViewController { 47 | 48 | var removedAllMarkers = false 49 | var coordinate = TGGeoPoint() 50 | var duration: Float = 0.0 51 | var easeType = TGEaseType.cubic 52 | var scenePath = URL(fileURLWithPath: "") 53 | var sceneUpdates: [TGSceneUpdate] = [] 54 | var sceneUpdateComponentPath = "" 55 | var sceneUpdateValue = "" 56 | var appliedSceneUpdates = false 57 | var lngLatForScreenPosition = TGGeoPoint() 58 | var screenPositionForLngLat = CGPoint() 59 | var labelPickPosition = CGPoint() 60 | var markerPickPosition = CGPoint() 61 | var featurePickPosition = CGPoint() 62 | var mockSceneId: Int32 = 0 63 | var yamlString = "" 64 | 65 | override func markerRemoveAll() { 66 | removedAllMarkers = true 67 | } 68 | 69 | override func loadScene(from url: URL) -> Int32 { 70 | scenePath = url 71 | return mockSceneId 72 | } 73 | 74 | override func loadScene(from url: URL, with updates: [TGSceneUpdate]) -> Int32 { 75 | scenePath = url 76 | sceneUpdates = updates 77 | return mockSceneId 78 | } 79 | 80 | override func loadSceneAsync(from url: URL) -> Int32 { 81 | scenePath = url 82 | return mockSceneId 83 | } 84 | 85 | override func loadSceneAsync(from url: URL, with updates: [TGSceneUpdate]) -> Int32 { 86 | scenePath = url 87 | sceneUpdates = updates 88 | return mockSceneId 89 | } 90 | override func loadScene(fromYAML yaml: String, relativeTo url: URL, with updates: [TGSceneUpdate]) -> Int32 { 91 | scenePath = url 92 | yamlString = yaml 93 | sceneUpdates = updates 94 | return mockSceneId 95 | } 96 | override func loadSceneAsync(fromYAML yaml: String, relativeTo url: URL, with updates: [TGSceneUpdate]) -> Int32 { 97 | scenePath = url 98 | yamlString = yaml 99 | sceneUpdates = updates 100 | return mockSceneId 101 | } 102 | 103 | override func updateSceneAsync(_ updates: [TGSceneUpdate]) -> Int32 { 104 | sceneUpdates = updates 105 | appliedSceneUpdates = true 106 | return mockSceneId 107 | } 108 | 109 | override func lngLat(toScreenPosition lngLat: TGGeoPoint) -> CGPoint { 110 | lngLatForScreenPosition = lngLat 111 | return CGPoint() 112 | } 113 | 114 | override func screenPosition(toLngLat screenPosition: CGPoint) -> TGGeoPoint { 115 | screenPositionForLngLat = screenPosition 116 | return TGGeoPoint() 117 | } 118 | 119 | override func animate(toPosition position: TGGeoPoint, withDuration seconds: Float) { 120 | coordinate = position 121 | duration = seconds 122 | } 123 | 124 | override func animate(toPosition position: TGGeoPoint, withDuration seconds: Float, with easeType: TGEaseType) { 125 | coordinate = position 126 | duration = seconds 127 | self.easeType = easeType 128 | } 129 | 130 | override func animate(toZoomLevel zoomLevel: Float, withDuration seconds: Float) { 131 | zoom = zoomLevel 132 | duration = seconds 133 | } 134 | 135 | override func animate(toZoomLevel zoomLevel: Float, withDuration seconds: Float, with easeType: TGEaseType) { 136 | zoom = zoomLevel 137 | duration = seconds 138 | self.easeType = easeType 139 | } 140 | 141 | override func animate(toRotation radians: Float, withDuration seconds: Float) { 142 | rotation = radians 143 | duration = seconds 144 | } 145 | 146 | override func animate(toRotation radians: Float, withDuration seconds: Float, with easeType: TGEaseType) { 147 | rotation = radians 148 | duration = seconds 149 | self.easeType = easeType 150 | } 151 | 152 | override func animate(toTilt radians: Float, withDuration seconds: Float) { 153 | tilt = radians 154 | duration = seconds 155 | } 156 | 157 | override func animate(toTilt radians: Float, withDuration seconds: Float, with easeType: TGEaseType) { 158 | tilt = radians 159 | duration = seconds 160 | self.easeType = easeType 161 | } 162 | 163 | override func pickLabel(at screenPosition: CGPoint) { 164 | labelPickPosition = screenPosition 165 | } 166 | 167 | override func pickMarker(at screenPosition: CGPoint) { 168 | markerPickPosition = screenPosition 169 | } 170 | 171 | override func pickFeature(at screenPosition: CGPoint) { 172 | featurePickPosition = screenPosition 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /MapzenSDKTests/TestAnnotationTarget.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestAnnotationTarget.swift 3 | // ios-sdk 4 | // 5 | // Created by Sarah Lensing on 3/9/17. 6 | // Copyright © 2017 Mapzen. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import TangramMap 11 | @testable import MapzenSDK 12 | 13 | class AnnotationTestTarget : UIResponder, MapMarkerSelectDelegate { 14 | 15 | var annotationClicked = false 16 | var annotationClickedNoParam = false 17 | var markerSelected = false 18 | 19 | func annotationClicked(annotation : PeliasMapkitAnnotation) { 20 | annotationClicked = true 21 | } 22 | 23 | func annotationClickedNoParams() { 24 | annotationClickedNoParam = true 25 | } 26 | 27 | func mapController(_ controller: MZMapViewController, didSelectMarker marker: GenericMarker, atScreenPosition position: CGPoint) { 28 | markerSelected = true 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /MapzenSDKTests/TestApplication.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestApplication.swift 3 | // ios-sdk 4 | // 5 | // Created by Sarah Lensing on 2/24/17. 6 | // Copyright © 2017 Mapzen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | @testable import MapzenSDK 11 | 12 | class TestApplication: ApplicationProtocol { 13 | 14 | open var urlToOpen : URL? 15 | 16 | func openURL(_ url: URL) -> Bool { 17 | urlToOpen = url 18 | return false 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /MapzenSDKTests/TestLocationManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestLocationManager.swift 3 | // ios-sdk 4 | // 5 | // Created by Sarah Lensing on 3/8/17. 6 | // Copyright © 2017 Mapzen. All rights reserved. 7 | // 8 | 9 | import CoreLocation 10 | @testable import MapzenSDK 11 | 12 | class TestLocationManager : LocationManagerProtocol { 13 | func startUpdatingHeading() { 14 | } 15 | 16 | func stopUpdatingHeading() { 17 | } 18 | 19 | func canEnableBackgroundLocationUpdates() -> Bool { 20 | return true 21 | } 22 | 23 | func enableBackgroundLocationUpdates(forType activityType: CLActivityType, desiredAccuracy: CLLocationAccuracy, pausesLocationAutomatically: Bool) -> Bool { 24 | return true 25 | } 26 | 27 | open weak var delegate: LocationManagerDelegate? 28 | 29 | var currentLocation: CLLocation? 30 | 31 | var requestedInUse = false 32 | 33 | func requestAlwaysAuthorization() { 34 | 35 | } 36 | 37 | func requestWhenInUseAuthorization() { 38 | requestedInUse = true 39 | } 40 | 41 | func isInUseAuthorized() -> Bool { 42 | return true 43 | } 44 | 45 | func isAlwaysAuthorized() -> Bool { 46 | return true 47 | } 48 | 49 | func refreshCurrentLocation() -> CLLocation? { 50 | return nil 51 | } 52 | 53 | func requestLocation() { 54 | 55 | } 56 | 57 | func startUpdatingLocation() { 58 | 59 | } 60 | 61 | func stopUpdatingLocation() { 62 | 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /MapzenSDKTests/TestMapzenManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestMapzenManager.swift 3 | // ios-sdk 4 | // 5 | // Created by Sarah Lensing on 3/9/17. 6 | // Copyright © 2017 Mapzen. All rights reserved. 7 | // 8 | 9 | @testable import MapzenSDK 10 | 11 | class TestMapzenManager : MapzenManagerProtocol { 12 | 13 | open var apiKey: String? 14 | } 15 | -------------------------------------------------------------------------------- /MapzenSDKTests/TestNotificationCenter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestNotificationCenter.swift 3 | // MapzenSDKTests 4 | // 5 | // Created by Matt Smollinger on 11/16/17. 6 | // Copyright © 2017 Mapzen. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | @testable import MapzenSDK 11 | 12 | //This is really, really, REALLY not a fully fledged mock implementation and only does exactly what we need to test existing Notification observance. This is probably a good thing, as mocks shouldn't do that much, but be aware when using it in new tests. 13 | class TestNotificationCenter: NotificationCenterProtocol { 14 | var observer: Any? 15 | var selector: Selector? 16 | var name: NSNotification.Name? 17 | var notificationObserverArray: [NSNotification.Name : Selector] = [:] 18 | var object: Any? 19 | 20 | func addObserver(_ observer: Any, selector aSelector: Selector, name aName: NSNotification.Name?, object anObject: Any?) { 21 | self.observer = observer 22 | notificationObserverArray[aName!] = aSelector 23 | object = anObject 24 | } 25 | 26 | func removeObserver(_ observer: Any) { 27 | self.observer = observer 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /MapzenSDKTests/TestSessionDataTask.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestSessionDataTask.swift 3 | // ios-sdk 4 | // 5 | // Created by Sarah Lensing on 3/13/17. 6 | // Copyright © 2017 Mapzen. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class TestSessionDataTask : URLSessionDataTask { 12 | 13 | override func resume() { 14 | // Do nothing 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /MapzenSDKTests/TestStylesAndThemes.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestStylesAndThemes.swift 3 | // ios-sdk 4 | // 5 | // Created by Matt Smollinger on 10/3/17. 6 | // Copyright © 2017 Mapzen. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import MapzenSDK 11 | 12 | class TestStylesAndThemes: XCTestCase { 13 | 14 | func testBubbleWrapStyleDefaults() { 15 | let style = BubbleWrapStyle() 16 | XCTAssertTrue(style.mapStyle == .bubbleWrap) 17 | XCTAssertTrue(style.styleSheetRoot == "bubble-wrap/") 18 | XCTAssertTrue(style.styleSheetFileName == "bubble-wrap-style") 19 | } 20 | 21 | func testBubbleWrapImportString() { 22 | let style = BubbleWrapStyle() 23 | XCTAssertTrue(style.importString == "{ import: [ bubble-wrap/bubble-wrap-style.yaml, bubble-wrap/themes/label-5.yaml ] }") 24 | } 25 | 26 | func testBubbleWrapThemeDefaults() { 27 | let style = BubbleWrapStyle() 28 | XCTAssertTrue(style.availableLabelLevels == 12) 29 | XCTAssertTrue(style.availableDetailLevels == 0) 30 | XCTAssertTrue(style.availableColors == []) 31 | XCTAssertTrue(style.currentColor == "") 32 | XCTAssertTrue(style.labelLevel == 5) 33 | XCTAssertTrue(style.detailLevel == 0) 34 | XCTAssertTrue(style.yamlString == "bubble-wrap/themes/label-5.yaml") 35 | } 36 | 37 | func testCinnabarStyleDefaults() { 38 | let style = CinnabarStyle() 39 | XCTAssertTrue(style.mapStyle == .cinnabar) 40 | XCTAssertTrue(style.styleSheetRoot == "cinnabar/") 41 | XCTAssertTrue(style.styleSheetFileName == "cinnabar-style") 42 | } 43 | 44 | func testCinnabarImportString() { 45 | let style = CinnabarStyle() 46 | XCTAssertTrue(style.importString == "{ import: [ cinnabar/cinnabar-style.yaml, cinnabar/themes/label-5.yaml ] }") 47 | } 48 | 49 | func testCinnabarThemeDefaults() { 50 | let style = CinnabarStyle() 51 | XCTAssertTrue(style.availableLabelLevels == 12) 52 | XCTAssertTrue(style.availableDetailLevels == 0) 53 | XCTAssertTrue(style.availableColors == []) 54 | XCTAssertTrue(style.currentColor == "") 55 | XCTAssertTrue(style.labelLevel == 5) 56 | XCTAssertTrue(style.detailLevel == 0) 57 | XCTAssertTrue(style.yamlString == "cinnabar/themes/label-5.yaml") 58 | } 59 | 60 | func testRefillStyleDefaults() { 61 | let style = RefillStyle() 62 | XCTAssertTrue(style.mapStyle == .refill) 63 | XCTAssertTrue(style.styleSheetRoot == "refill/") 64 | XCTAssertTrue(style.styleSheetFileName == "refill-style") 65 | } 66 | 67 | func testRefillImportString() { 68 | let style = RefillStyle() 69 | XCTAssertTrue(style.importString == "{ import: [ refill/refill-style.yaml, refill/themes/label-5.yaml, refill/themes/detail-10.yaml, refill/themes/color-black.yaml ] }") 70 | } 71 | 72 | func testRefillThemeDefaults() { 73 | let style = RefillStyle() 74 | XCTAssertTrue(style.availableLabelLevels == 12) 75 | XCTAssertTrue(style.availableDetailLevels == 12) 76 | XCTAssertTrue(style.availableColors == ["black", "blue-gray", "blue", "brown-orange", "gray-gold", "gray", "high-contrast", "inverted", "pink-yellow", "pink", "purple-green", "sepia", "zinc"]) 77 | XCTAssertTrue(style.currentColor == "black") 78 | XCTAssertTrue(style.labelLevel == 5) 79 | XCTAssertTrue(style.detailLevel == 10) 80 | XCTAssertTrue(style.yamlString == "refill/themes/label-5.yaml, refill/themes/detail-10.yaml, refill/themes/color-black.yaml") 81 | } 82 | 83 | func testZincDefaults() { 84 | let style = ZincStyle() 85 | XCTAssertTrue(style.mapStyle == .zinc) 86 | XCTAssertTrue(style.currentColor == "zinc") 87 | XCTAssertTrue(style.yamlString == "refill/themes/label-5.yaml, refill/themes/detail-10.yaml, refill/themes/color-zinc.yaml") 88 | } 89 | 90 | func testWalkaboutStyleDefaults() { 91 | let style = WalkaboutStyle() 92 | XCTAssertTrue(style.mapStyle == .walkabout) 93 | XCTAssertTrue(style.styleSheetRoot == "walkabout/") 94 | XCTAssertTrue(style.styleSheetFileName == "walkabout-style") 95 | } 96 | 97 | func testWalkaboutImportString() { 98 | let style = WalkaboutStyle() 99 | XCTAssertTrue(style.importString == "{ import: [ walkabout/walkabout-style.yaml, walkabout/themes/label-5.yaml ] }") 100 | } 101 | 102 | func testWalkaboutThemeDefaults() { 103 | let style = WalkaboutStyle() 104 | XCTAssertTrue(style.availableLabelLevels == 12) 105 | XCTAssertTrue(style.availableDetailLevels == 0) 106 | XCTAssertTrue(style.availableColors == []) 107 | XCTAssertTrue(style.currentColor == "") 108 | XCTAssertTrue(style.labelLevel == 5) 109 | XCTAssertTrue(style.detailLevel == 0) 110 | XCTAssertTrue(style.yamlString == "walkabout/themes/label-5.yaml") 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /MapzenSDKTests/TestTGMarkerPickResult.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TGMarkerPickResultExtensions.swift 3 | // ios-sdk 4 | // 5 | // Created by Sarah Lensing on 3/14/17. 6 | // Copyright © 2017 Mapzen. All rights reserved. 7 | // 8 | 9 | import TangramMap 10 | 11 | class TestTGMarkerPickResult : TGMarkerPickResult { 12 | 13 | private let internalMarker: TGMarker 14 | 15 | override public var marker : TGMarker { 16 | get { 17 | return internalMarker 18 | } 19 | } 20 | 21 | init(marker: TGMarker) { 22 | internalMarker = marker 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /MapzenSDKTests/TestUrlSession.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestUrlSession.swift 3 | // ios-sdk 4 | // 5 | // Created by Sarah Lensing on 3/13/17. 6 | // Copyright © 2017 Mapzen. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | @testable import MapzenSDK 11 | 12 | class TestUrlSession : URLSession { 13 | var queryParameters : [String : AnyObject]? 14 | 15 | override func dataTask(with url: URL, completionHandler: @escaping (_ data: Data?, _ response: URLResponse?, _ error: Error?) -> Void) -> URLSessionDataTask { 16 | let urlComponents = URLComponents.init(string: url.absoluteString) 17 | let queryItems = urlComponents?.queryItems 18 | let items = queryItems?.filter({ (queryItem) -> Bool in 19 | return queryItem.name == "json" 20 | }) 21 | let jsonItem = items?[0] 22 | let decoded = jsonItem?.value?.removingPercentEncoding?.replacingOccurrences(of: "json=", with: "") 23 | if let data = decoded?.data(using: .utf8) { 24 | if let params = try? JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String : AnyObject] { 25 | queryParameters = params 26 | } 27 | } 28 | return TestSessionDataTask() 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /MapzenSDKTests/UIColorExtensionsTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIColorExtensionsTests.swift 3 | // ios-sdk 4 | // 5 | // Created by Sarah Lensing on 3/31/17. 6 | // Copyright © 2017 Mapzen. All rights reserved. 7 | // 8 | 9 | @testable import MapzenSDK 10 | import XCTest 11 | 12 | class UIColorExtensionsTests : XCTestCase { 13 | 14 | func testHexNoAlpha() { 15 | let hex = UIColor.red.hexValue() 16 | XCTAssertEqual(hex, "#FFFF0000") 17 | } 18 | 19 | func testHexAlpha() { 20 | let hex = UIColor.blue.withAlphaComponent(0.5).hexValue() 21 | XCTAssertEqual(hex, "#800000FF") 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '9.3' 2 | use_frameworks! 3 | 4 | def shared_pods 5 | pod 'Mapzen-ios-sdk', :path => '.' 6 | end 7 | 8 | target "ios-sdk" do 9 | shared_pods 10 | pod "HockeySDK", '~> 4.1.4', :subspecs => ['CrashOnlyLib'] 11 | end 12 | 13 | target "MapzenSDK" do 14 | shared_pods 15 | end 16 | 17 | target "MapzenSDKTests" do 18 | shared_pods 19 | end 20 | 21 | target "SampleApp-Objc" do 22 | shared_pods 23 | end 24 | 25 | target "SampleApp-ObjcTests" do 26 | shared_pods 27 | end 28 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - HockeySDK/CrashOnlyLib (4.1.6) 3 | - Mapzen-ios-sdk (1.1.1): 4 | - Mapzen-ios-sdk/Core (= 1.1.1) 5 | - Mapzen-ios-sdk/Core (1.1.1): 6 | - OnTheRoad (~> 1.0.1) 7 | - Pelias (~> 1.0.2) 8 | - Tangram-es (~> 0.8.1) 9 | - OnTheRoad (1.0.1) 10 | - Pelias (1.0.2): 11 | - Pelias/Core (= 1.0.2) 12 | - Pelias/Core (1.0.2) 13 | - Tangram-es (0.8.1) 14 | 15 | DEPENDENCIES: 16 | - HockeySDK/CrashOnlyLib (~> 4.1.4) 17 | - Mapzen-ios-sdk (from `.`) 18 | 19 | EXTERNAL SOURCES: 20 | Mapzen-ios-sdk: 21 | :path: . 22 | 23 | SPEC CHECKSUMS: 24 | HockeySDK: 95db557d54489a570dcdefae0d02f98eecc279a3 25 | Mapzen-ios-sdk: 4c6f9d8a35adcba54b762470d2869d00409661dc 26 | OnTheRoad: 8327bc6f0c2c698103b632a035438cc3c69b62d7 27 | Pelias: 3cda3c78c6c1882a1163e500c76a1ee35c5e0edd 28 | Tangram-es: 4d6d08e8d25900899073133756b35b4cfe2e97f7 29 | 30 | PODFILE CHECKSUM: cb8163d99be0602b23d29016872a6211a1841360 31 | 32 | COCOAPODS: 1.3.1 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The Mapzen iOS SDK 2 | [![Circle CI](https://circleci.com/gh/mapzen/ios.svg?style=shield&circle-token=158f79f566b88fb913ad153ee8b00681112eb5a2)](https://circleci.com/gh/mapzen/ios) 3 | 4 |

5 | 6 |

7 | 8 | The Mapzen iOS SDK is a thin wrapper that packages up everything you need to use Mapzen services in your iOS applications. 9 | 10 | We welcome feedback from the community on its usage. Feature requests are also welcome; label it appropriately as an ["Enhancement"](https://github.com/mapzen/ios/issues?q=is%3Aopen+is%3Aissue+label%3Aenhancement). Feel free to also reach out to us using any of the other channels we have available on https://mapzen.com/. 11 | 12 | ## Usage 13 | Everything you need to get going using the Mapzen SDK 14 | 15 | ### Set up 16 | - [Installation](https://github.com/mapzen/ios/blob/master/docs/installation.md) 17 | - [Getting started](https://github.com/mapzen/ios/blob/master/docs/getting-started.md) 18 | 19 | ### Interacting with the map 20 | - [Position, rotation, zoom, and tilt](https://github.com/mapzen/ios/blob/master/docs/basic-functions.md) 21 | - [Markers, polylines, and polygons](https://github.com/mapzen/ios/blob/master/docs/features.md) 22 | - [Switching styles](https://github.com/mapzen/ios/blob/master/docs/styles.md) 23 | - [Gesture delegates](https://github.com/mapzen/ios/blob/master/docs/gesture-delegates.md) 24 | - [Current location](https://github.com/mapzen/ios/blob/master/docs/location-services.md) 25 | 26 | ### Search and routing 27 | - [Search](https://github.com/mapzen/ios/blob/master/docs/search.md) 28 | - [Routing](https://github.com/mapzen/ios/blob/master/docs/turn-by-turn.md) 29 | 30 | 31 | # What's Included 32 | 33 | Major features include: 34 | * High performance and highly customizable map rendering using OpenGL ES provided by [Tangram-es](https://github.com/tangrams/tangram-es). 35 | * Driving directions and customizable route lines provided by [Mapzen Turn-by-Turn](https://mapzen.com/products/turn-by-turn/). 36 | * Geocoding and Point-of-Interest search provided by [Mapzen Search](https://mapzen.com/products/search/). 37 | * Several [base map styles](https://mapzen.com/products/maps/) to suit most use cases. 38 | 39 | And many more features than we can list here in a timely fashion. 40 | 41 | # How Do I Get The SDK? 42 | 43 | Step 1: Get yourself a free [Mapzen API Key](https://mapzen.com/developers/sign_up). 44 | 45 | Step 2: Install the SDK through [Cocoapods](https://cocoapods.org/pods/Mapzen-ios-sdk). 46 | 47 | Step 3: Check out the sample app [source code](https://github.com/mapzen/ios/tree/master/SampleApp) or `pod try Mapzen-ios-sdk` to load it immediately. You'll need that API key from step 1 in either case. See where to set it [below](#configure-sample-app-api-key). 48 | 49 | Step 4: Let us know your thoughts! You can either open a [new issue on GitHub](https://github.com/mapzen/ios/issues) or send us email at ios-support@mapzen.com. 50 | 51 | ## Non-Cocoapods Usage 52 | 53 | Non-cocoapods usage at this point is not recommended, but can be accomplished. First, make sure to `git submodule update --init --recursive` to get all the style sheets after cloning this repository. Second. you will need to include the 3 other dependencies we require: [Tangram-es](https://github.com/tangrams/ios-framework), [OnTheRoad for iOS](https://github.com/mapzen/on-the-road_ios), and the [Pelias iOS SDK](https://github.com/pelias/pelias-ios-sdk). Note that your project will need to support Swift 3. 54 | 55 | ## Notes 56 | There's a couple of things you should probably know about up front: 57 | * We only will be supporting Swift 3.x moving forward. Older versions of the SDK were written in Swift 2, but it is not recommended to use that as the project has changed dramatically since then, and we're continuing to add features all the time. 58 | * If you wish to install the sample app to a device (recommended due to performance issues in the simulator), you will need to update the bundle identifier and the code signing in the Xcode project and go through the general code signing process necessary for installing to a device. This will require a free Apple Developer account. 59 | * If you decide to use Cocoapods and pull directly from master, you will need to add `:submodules => true` to your podfile line. Otherwise submodules will not load in. The full line would look like `pod "Mapzen-ios-sdk", :git => 'https://github.com/mapzen/ios.git', :branch => 'master', :submodules => true` 60 | 61 | ## Configure Sample App API Key 62 | There are two (optionally 3) ways to set your API key in the Sample App: 63 | 64 | 1. Update SampleApp/Info.plist 65 | 66 | Replace `$(MAPZEN_API_KEY)` with your key: 67 | 68 |

69 | 70 |

71 | 72 | -- OR -- 73 | 74 | 2. Create a new scheme and add an environment variable 75 | 76 | Duplicate the `ios-sdk` scheme and then add your environment variable here: 77 | 78 |

79 | 80 |

81 | 82 | -- OR -- 83 | 84 | 3. Manually change the code in the Sample App's AppDelegate.swift on line 31 to use your API key. Note that this *will* generate warnings about unused code. 85 | -------------------------------------------------------------------------------- /SampleApp-Objc/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // SampleApp-Objc 4 | // 5 | // Created by Matt Smollinger on 5/25/17. 6 | // Copyright © 2017 Mapzen. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /SampleApp-Objc/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // SampleApp-Objc 4 | // 5 | // Created by Matt Smollinger on 5/25/17. 6 | // Copyright © 2017 Mapzen. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | @import Mapzen_ios_sdk; 11 | 12 | @interface AppDelegate () 13 | 14 | @end 15 | 16 | @implementation AppDelegate 17 | 18 | 19 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 20 | // Override point for customization after application launch. 21 | MapzenManager.sharedManager.apiKey = [self getEnvironmentVariableForKey:@"MAPZEN_API_KEY"]; 22 | NSAssert([MapzenManager.sharedManager.apiKey containsString:@"mapzen-"], @"Set your Mapzen API key in the scheme by adding a MAPZEN_API_KEY environment variable."); 23 | return YES; 24 | } 25 | 26 | - (NSString *)getEnvironmentVariableForKey:(NSString *)key { 27 | if (NSBundle.mainBundle.infoDictionary[key] && [(NSString*)NSBundle.mainBundle.infoDictionary[key] length] > 0 ) { 28 | return NSBundle.mainBundle.infoDictionary[key]; 29 | } 30 | 31 | if ([NSProcessInfo processInfo].environment[key]) { 32 | return [NSProcessInfo processInfo].environment[key]; 33 | } 34 | 35 | return @""; 36 | } 37 | 38 | - (void)applicationWillResignActive:(UIApplication *)application { 39 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 40 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 41 | } 42 | 43 | 44 | - (void)applicationDidEnterBackground:(UIApplication *)application { 45 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 46 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 47 | } 48 | 49 | 50 | - (void)applicationWillEnterForeground:(UIApplication *)application { 51 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 52 | } 53 | 54 | 55 | - (void)applicationDidBecomeActive:(UIApplication *)application { 56 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 57 | } 58 | 59 | 60 | - (void)applicationWillTerminate:(UIApplication *)application { 61 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 62 | } 63 | 64 | 65 | @end 66 | -------------------------------------------------------------------------------- /SampleApp-Objc/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "ipad", 35 | "size" : "29x29", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "ipad", 40 | "size" : "29x29", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "40x40", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "40x40", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "76x76", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "76x76", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } -------------------------------------------------------------------------------- /SampleApp-Objc/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /SampleApp-Objc/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /SampleApp-Objc/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | NSLocationWhenInUseUsageDescription 45 | Need location to make things happen! 46 | MAPZEN_API_KEY 47 | $(MAPZEN_API_KEY) 48 | 49 | 50 | -------------------------------------------------------------------------------- /SampleApp-Objc/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // SampleApp-Objc 4 | // 5 | // Created by Matt Smollinger on 5/25/17. 6 | // Copyright © 2017 Mapzen. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /SampleApp-Objc/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // SampleApp-Objc 4 | // 5 | // Created by Matt Smollinger on 5/25/17. 6 | // Copyright © 2017 Mapzen. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | @import Mapzen_ios_sdk; 11 | 12 | @implementation ViewController 13 | 14 | - (void)viewDidLoad { 15 | [super viewDidLoad]; 16 | // Do any additional setup after loading the view, typically from a nib. 17 | MZMapViewController *mapView = [[MZMapViewController alloc] init]; 18 | [self.view addSubview:mapView.view]; 19 | NSLayoutConstraint *top = [mapView.view.topAnchor constraintEqualToAnchor:self.view.topAnchor]; 20 | NSLayoutConstraint *left = [mapView.view.leftAnchor constraintEqualToAnchor:self.view.leftAnchor]; 21 | NSLayoutConstraint *right = [mapView.view.rightAnchor constraintEqualToAnchor:self.view.rightAnchor]; 22 | NSLayoutConstraint *bottom = [mapView.view.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor]; 23 | [NSLayoutConstraint activateConstraints:@[top, left, right, bottom]]; 24 | [mapView loadStyleAsync:MapStyleWalkabout error:nil onStyleLoaded:^(enum MapStyle style) { 25 | (void)[mapView showCurrentLocation:YES]; 26 | (void)[mapView showFindMeButon:YES]; 27 | }]; 28 | } 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /SampleApp-Objc/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // SampleApp-Objc 4 | // 5 | // Created by Matt Smollinger on 5/25/17. 6 | // Copyright © 2017 Mapzen. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /SampleApp-ObjcTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /SampleApp-ObjcTests/SampleApp_ObjcTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // SampleApp_ObjcTests.m 3 | // SampleApp-ObjcTests 4 | // 5 | // Created by Matt Smollinger on 5/25/17. 6 | // Copyright © 2017 Mapzen. All rights reserved. 7 | // 8 | 9 | #import 10 | @import Mapzen_ios_sdk; 11 | 12 | @interface SampleApp_ObjcTests : XCTestCase 13 | 14 | @end 15 | 16 | @implementation SampleApp_ObjcTests 17 | 18 | - (void)testCreatingMZMapController { 19 | MZMapViewController *map = [[MZMapViewController alloc] init]; 20 | XCTAssertNotNil(map); 21 | } 22 | 23 | - (void)testMethodAvailabilityOnController { 24 | MZMapViewController *map = [[MZMapViewController alloc] init]; 25 | XCTAssert(map.showTransitOverlay == NO); 26 | } 27 | 28 | - (void)testStyleSheetProtocolAvailability { 29 | id test; 30 | test = [[BubbleWrapStyle alloc] init]; 31 | XCTAssertTrue([test.importString containsString:@"bubble-wrap"]); 32 | } 33 | @end 34 | -------------------------------------------------------------------------------- /SampleApp/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // ios-sdk 4 | // 5 | // Created by Matt Smollinger on 3/8/16. 6 | // Copyright © 2016 Mapzen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import HockeySDK 11 | import Mapzen_ios_sdk 12 | 13 | @UIApplicationMain 14 | class AppDelegate: UIResponder, UIApplicationDelegate { 15 | 16 | var window: UIWindow? 17 | 18 | static let MapUpdateNotification = NSNotification.Name(rawValue: "MapUpdateNotification") 19 | 20 | var selectedMapStyle: StyleSheet = BubbleWrapStyle() { 21 | didSet { 22 | NotificationCenter.default.post(name: AppDelegate.MapUpdateNotification, object: nil) 23 | } 24 | } 25 | 26 | 27 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 28 | startCrashReporter() 29 | let apiKey = getEnvironmentVariable(key: "MAPZEN_API_KEY") 30 | assert(apiKey.contains("mapzen-"), "Set your Mapzen API key in the scheme by adding a MAPZEN_API_KEY environment variable.") 31 | MapzenManager.sharedManager.apiKey = apiKey 32 | return true 33 | } 34 | 35 | private func startCrashReporter() { 36 | let manager = BITHockeyManager.shared() 37 | manager.configure(withIdentifier: getEnvironmentVariable(key: "HOCKEY_APP_ID")) 38 | manager.crashManager.crashManagerStatus = .autoSend 39 | manager.start() 40 | } 41 | 42 | private func getEnvironmentVariable(key: String) -> String { 43 | var envVar = "" 44 | // xcodebuild arg 45 | if let bundleVar = Bundle.main.infoDictionary?[key] as? String { 46 | if !bundleVar.isEmpty { 47 | envVar = bundleVar 48 | } 49 | } 50 | // scheme environment variables 51 | if envVar.isEmpty, let processVar = ProcessInfo.processInfo.environment[key] { 52 | envVar = processVar 53 | } 54 | return envVar 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /SampleApp/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "60x60", 35 | "idiom" : "iphone", 36 | "filename" : "mapzen_appicon_iphone2.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "60x60", 41 | "idiom" : "iphone", 42 | "filename" : "mapzen_appicon_iphone1.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "idiom" : "ipad", 47 | "size" : "20x20", 48 | "scale" : "1x" 49 | }, 50 | { 51 | "idiom" : "ipad", 52 | "size" : "20x20", 53 | "scale" : "2x" 54 | }, 55 | { 56 | "idiom" : "ipad", 57 | "size" : "29x29", 58 | "scale" : "1x" 59 | }, 60 | { 61 | "idiom" : "ipad", 62 | "size" : "29x29", 63 | "scale" : "2x" 64 | }, 65 | { 66 | "idiom" : "ipad", 67 | "size" : "40x40", 68 | "scale" : "1x" 69 | }, 70 | { 71 | "idiom" : "ipad", 72 | "size" : "40x40", 73 | "scale" : "2x" 74 | }, 75 | { 76 | "size" : "76x76", 77 | "idiom" : "ipad", 78 | "filename" : "mapzen_appicon_ipadonex.png", 79 | "scale" : "1x" 80 | }, 81 | { 82 | "size" : "76x76", 83 | "idiom" : "ipad", 84 | "filename" : "mapzen_appicon_ipad.png", 85 | "scale" : "2x" 86 | }, 87 | { 88 | "size" : "83.5x83.5", 89 | "idiom" : "ipad", 90 | "filename" : "mapzen_appicon_ipadpro.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "24x24", 95 | "idiom" : "watch", 96 | "scale" : "2x", 97 | "role" : "notificationCenter", 98 | "subtype" : "38mm" 99 | }, 100 | { 101 | "size" : "27.5x27.5", 102 | "idiom" : "watch", 103 | "scale" : "2x", 104 | "role" : "notificationCenter", 105 | "subtype" : "42mm" 106 | }, 107 | { 108 | "size" : "29x29", 109 | "idiom" : "watch", 110 | "role" : "companionSettings", 111 | "scale" : "2x" 112 | }, 113 | { 114 | "size" : "29x29", 115 | "idiom" : "watch", 116 | "role" : "companionSettings", 117 | "scale" : "3x" 118 | }, 119 | { 120 | "size" : "40x40", 121 | "idiom" : "watch", 122 | "scale" : "2x", 123 | "role" : "appLauncher", 124 | "subtype" : "38mm" 125 | }, 126 | { 127 | "size" : "86x86", 128 | "idiom" : "watch", 129 | "scale" : "2x", 130 | "role" : "quickLook", 131 | "subtype" : "38mm" 132 | }, 133 | { 134 | "size" : "98x98", 135 | "idiom" : "watch", 136 | "scale" : "2x", 137 | "role" : "quickLook", 138 | "subtype" : "42mm" 139 | } 140 | ], 141 | "info" : { 142 | "version" : 1, 143 | "author" : "xcode" 144 | } 145 | } -------------------------------------------------------------------------------- /SampleApp/Assets.xcassets/AppIcon.appiconset/mapzen_appicon_ipad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapzen/ios/b77c3eab3379c021ed81f0416d0fbc04a107ccdd/SampleApp/Assets.xcassets/AppIcon.appiconset/mapzen_appicon_ipad.png -------------------------------------------------------------------------------- /SampleApp/Assets.xcassets/AppIcon.appiconset/mapzen_appicon_ipadonex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapzen/ios/b77c3eab3379c021ed81f0416d0fbc04a107ccdd/SampleApp/Assets.xcassets/AppIcon.appiconset/mapzen_appicon_ipadonex.png -------------------------------------------------------------------------------- /SampleApp/Assets.xcassets/AppIcon.appiconset/mapzen_appicon_ipadpro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapzen/ios/b77c3eab3379c021ed81f0416d0fbc04a107ccdd/SampleApp/Assets.xcassets/AppIcon.appiconset/mapzen_appicon_ipadpro.png -------------------------------------------------------------------------------- /SampleApp/Assets.xcassets/AppIcon.appiconset/mapzen_appicon_iphone1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapzen/ios/b77c3eab3379c021ed81f0416d0fbc04a107ccdd/SampleApp/Assets.xcassets/AppIcon.appiconset/mapzen_appicon_iphone1.png -------------------------------------------------------------------------------- /SampleApp/Assets.xcassets/AppIcon.appiconset/mapzen_appicon_iphone2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapzen/ios/b77c3eab3379c021ed81f0416d0fbc04a107ccdd/SampleApp/Assets.xcassets/AppIcon.appiconset/mapzen_appicon_iphone2.png -------------------------------------------------------------------------------- /SampleApp/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /SampleApp/Assets.xcassets/mapzen_logo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "mapzen_logo.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "mapzen_logo@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "mapzen_logo@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /SampleApp/Assets.xcassets/mapzen_logo.imageset/mapzen_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapzen/ios/b77c3eab3379c021ed81f0416d0fbc04a107ccdd/SampleApp/Assets.xcassets/mapzen_logo.imageset/mapzen_logo.png -------------------------------------------------------------------------------- /SampleApp/Assets.xcassets/mapzen_logo.imageset/mapzen_logo@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapzen/ios/b77c3eab3379c021ed81f0416d0fbc04a107ccdd/SampleApp/Assets.xcassets/mapzen_logo.imageset/mapzen_logo@2x.png -------------------------------------------------------------------------------- /SampleApp/Assets.xcassets/mapzen_logo.imageset/mapzen_logo@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapzen/ios/b77c3eab3379c021ed81f0416d0fbc04a107ccdd/SampleApp/Assets.xcassets/mapzen_logo.imageset/mapzen_logo@3x.png -------------------------------------------------------------------------------- /SampleApp/Assets.xcassets/tabbar_map.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "maps_50.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "maps_75.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /SampleApp/Assets.xcassets/tabbar_map.imageset/maps_50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapzen/ios/b77c3eab3379c021ed81f0416d0fbc04a107ccdd/SampleApp/Assets.xcassets/tabbar_map.imageset/maps_50.png -------------------------------------------------------------------------------- /SampleApp/Assets.xcassets/tabbar_map.imageset/maps_75.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapzen/ios/b77c3eab3379c021ed81f0416d0fbc04a107ccdd/SampleApp/Assets.xcassets/tabbar_map.imageset/maps_75.png -------------------------------------------------------------------------------- /SampleApp/Assets.xcassets/tabbar_map_active.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "maps_active_50.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "maps_active_75.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /SampleApp/Assets.xcassets/tabbar_map_active.imageset/maps_active_50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapzen/ios/b77c3eab3379c021ed81f0416d0fbc04a107ccdd/SampleApp/Assets.xcassets/tabbar_map_active.imageset/maps_active_50.png -------------------------------------------------------------------------------- /SampleApp/Assets.xcassets/tabbar_map_active.imageset/maps_active_75.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapzen/ios/b77c3eab3379c021ed81f0416d0fbc04a107ccdd/SampleApp/Assets.xcassets/tabbar_map_active.imageset/maps_active_75.png -------------------------------------------------------------------------------- /SampleApp/Assets.xcassets/tabbar_nav.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "navigation_50.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "navigation_75.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /SampleApp/Assets.xcassets/tabbar_nav.imageset/navigation_50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapzen/ios/b77c3eab3379c021ed81f0416d0fbc04a107ccdd/SampleApp/Assets.xcassets/tabbar_nav.imageset/navigation_50.png -------------------------------------------------------------------------------- /SampleApp/Assets.xcassets/tabbar_nav.imageset/navigation_75.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapzen/ios/b77c3eab3379c021ed81f0416d0fbc04a107ccdd/SampleApp/Assets.xcassets/tabbar_nav.imageset/navigation_75.png -------------------------------------------------------------------------------- /SampleApp/Assets.xcassets/tabbar_nav_active.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "navigation_active_50.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "navigation_active_75.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /SampleApp/Assets.xcassets/tabbar_nav_active.imageset/navigation_active_50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapzen/ios/b77c3eab3379c021ed81f0416d0fbc04a107ccdd/SampleApp/Assets.xcassets/tabbar_nav_active.imageset/navigation_active_50.png -------------------------------------------------------------------------------- /SampleApp/Assets.xcassets/tabbar_nav_active.imageset/navigation_active_75.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapzen/ios/b77c3eab3379c021ed81f0416d0fbc04a107ccdd/SampleApp/Assets.xcassets/tabbar_nav_active.imageset/navigation_active_75.png -------------------------------------------------------------------------------- /SampleApp/Assets.xcassets/tabbar_search.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "search_50.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "search_75.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /SampleApp/Assets.xcassets/tabbar_search.imageset/search_50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapzen/ios/b77c3eab3379c021ed81f0416d0fbc04a107ccdd/SampleApp/Assets.xcassets/tabbar_search.imageset/search_50.png -------------------------------------------------------------------------------- /SampleApp/Assets.xcassets/tabbar_search.imageset/search_75.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapzen/ios/b77c3eab3379c021ed81f0416d0fbc04a107ccdd/SampleApp/Assets.xcassets/tabbar_search.imageset/search_75.png -------------------------------------------------------------------------------- /SampleApp/Assets.xcassets/tabbar_search_active.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "search_50.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "filename" : "search_75.png", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /SampleApp/Assets.xcassets/tabbar_search_active.imageset/search_50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapzen/ios/b77c3eab3379c021ed81f0416d0fbc04a107ccdd/SampleApp/Assets.xcassets/tabbar_search_active.imageset/search_50.png -------------------------------------------------------------------------------- /SampleApp/Assets.xcassets/tabbar_search_active.imageset/search_75.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapzen/ios/b77c3eab3379c021ed81f0416d0fbc04a107ccdd/SampleApp/Assets.xcassets/tabbar_search_active.imageset/search_75.png -------------------------------------------------------------------------------- /SampleApp/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /SampleApp/DemoMapViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DemoMapViewController.swift 3 | // ios-sdk 4 | // 5 | // Created by Matt Smollinger on 7/12/16. 6 | // Copyright © 2016 Mapzen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import TangramMap 11 | import Mapzen_ios_sdk 12 | import CoreLocation 13 | 14 | class DemoMapViewController: SampleMapViewController, MapMarkerSelectDelegate { 15 | 16 | private var styleLoaded = false 17 | 18 | lazy var activityIndicator : UIActivityIndicatorView = { 19 | let indicator = UIActivityIndicatorView.init(activityIndicatorStyle: .whiteLarge) 20 | indicator.color = .black 21 | indicator.translatesAutoresizingMaskIntoConstraints = false 22 | self.view.addSubview(indicator) 23 | 24 | let xConstraint = indicator.centerXAnchor.constraint(equalTo: self.view.centerXAnchor) 25 | let yConstraint = indicator.centerYAnchor.constraint(equalTo: self.view.centerYAnchor) 26 | NSLayoutConstraint.activate([xConstraint, yConstraint]) 27 | 28 | return indicator 29 | }() 30 | 31 | override func viewDidLoad() { 32 | super.viewDidLoad() 33 | setupSwitchStyleBtn() 34 | setupSwitchLocaleBtn() 35 | setupStyleNotification() 36 | markerSelectDelegate = self 37 | locationManager.requestAlwaysAuthorization() 38 | let appDelegate = UIApplication.shared.delegate as! AppDelegate 39 | try? loadStyleSheetAsync(appDelegate.selectedMapStyle) { (style) in 40 | self.styleLoaded = true 41 | let _ = self.showCurrentLocation(true) 42 | self.showFindMeButon(true) 43 | //Uncomment this to enable background location updates. However be aware that they are never cancelled so the sample app will just chew through your battery! 44 | // if self.locationManager.canEnableBackgroundLocationUpdates() { 45 | // _ = self.locationManager.enableBackgroundLocationUpdates(forType: .other, desiredAccuracy: kCLLocationAccuracyBest, pausesLocationAutomatically: false) 46 | // } 47 | } 48 | } 49 | 50 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 51 | if segue.identifier == "displayStyleSelector" { 52 | let destVC = segue.destination as! UINavigationController 53 | let styleVC = destVC.viewControllers[0] as! StylePickerVC 54 | styleVC.mapController = self 55 | } 56 | } 57 | 58 | override func locationDidUpdate(_ location: CLLocation) { 59 | super.locationDidUpdate(location) 60 | print("Location update received!") 61 | } 62 | 63 | //MARK : MapSelectDelegate 64 | func mapController(_ controller: MZMapViewController, didSelectMarker marker: GenericMarker, atScreenPosition position: CGPoint) { 65 | let alert = UIAlertController(title: "Marker Selected", message: nil, preferredStyle: .alert) 66 | alert.addAction(UIAlertAction(title: "Ok", style: .default, handler: nil)) 67 | present(alert, animated: true, completion: nil) 68 | } 69 | 70 | //MARK : Private 71 | private func setupSwitchStyleBtn() { 72 | let btn = UIBarButtonItem.init(title: "Map Style", style: .plain, target: self, action: #selector(openSettings)) 73 | self.navigationItem.rightBarButtonItem = btn 74 | } 75 | 76 | private func setupSwitchLocaleBtn() { 77 | let btn = UIBarButtonItem.init(title: "Map Language", style: .plain, target: self, action: #selector(changeMapLanguage)) 78 | self.navigationItem.leftBarButtonItem = btn 79 | } 80 | 81 | @objc private func openSettings() { 82 | performSegue(withIdentifier: "displayStyleSelector", sender: self) 83 | } 84 | 85 | 86 | @objc private func changeMapLanguage() { 87 | let languageIdByActionSheetTitle = [ 88 | "English": "en_US", 89 | "French": "fr_FR", 90 | "Japanese": "ja_JP", 91 | "Hindi": "hi_IN", 92 | "Spanish": "es_ES", 93 | "Korean": "ko_KR", 94 | "Italian": "it_IT", 95 | "OSM Default": "none", 96 | ] 97 | let actionSheet = UIAlertController.init(title: "Map Language", message: "Choose a language", preferredStyle: .actionSheet) 98 | for (actionTitle, languageIdentifier) in languageIdByActionSheetTitle { 99 | if (languageIdentifier == "none") { 100 | actionSheet.addAction(UIAlertAction.init(title: actionTitle, style: .default, handler: { [unowned self] (action) in 101 | let update = TGSceneUpdate(path: "global.ux_language", value: "") 102 | self.tgViewController.updateSceneAsync([update]) 103 | })) 104 | continue 105 | } 106 | actionSheet.addAction(UIAlertAction.init(title: actionTitle, style: .default, handler: { [unowned self] (action) in 107 | self.updateLocale(Locale.init(identifier: languageIdentifier)) 108 | })) 109 | } 110 | actionSheet.addAction(UIAlertAction.init(title: "Cancel", style: .cancel, handler: { [unowned self] (action) in 111 | self.dismiss(animated: true, completion: nil) 112 | })) 113 | let presentationController = actionSheet.popoverPresentationController 114 | presentationController?.barButtonItem = self.navigationItem.leftBarButtonItem 115 | self.navigationController?.present(actionSheet, animated: true, completion: nil) 116 | 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /SampleApp/DemoRouteDirectionCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RouteDirectionCell.swift 3 | // ios-sdk 4 | // 5 | // Created by Matt Smollinger on 10/4/16. 6 | // Copyright © 2016 Mapzen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class DemoRouteDirectionCell: UITableViewCell { 12 | 13 | @IBOutlet weak var titleLabel: UILabel! 14 | @IBOutlet weak var detailLabel: UILabel! 15 | } 16 | -------------------------------------------------------------------------------- /SampleApp/DemoRouteViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DemoRouteViewController.swift 3 | // ios-sdk 4 | // 5 | // Created by Matt Smollinger on 4/10/17. 6 | // Copyright © 2017 Mapzen. All rights reserved. 7 | // 8 | 9 | import OnTheRoad 10 | import Mapzen_ios_sdk 11 | 12 | class DemoRouteViewController: SampleMapViewController, MapSingleTapGestureDelegate { 13 | let routeListSegueId = "routeListSegueId" 14 | var currentRouteResult: OTRRoutingResult? 15 | var lastRoutingPoint: OTRGeoPoint? 16 | var costingModel: OTRRoutingCostingModel = .auto 17 | 18 | private var routingLocale = Locale.current { 19 | didSet { 20 | if let point = lastRoutingPoint { 21 | requestRoute(toPoint: point) 22 | } 23 | } 24 | } 25 | private var destination : OTRGeoPoint? 26 | 27 | //MARK:- ViewController life cycle 28 | override func viewDidLoad() { 29 | super.viewDidLoad() 30 | let appDelegate = UIApplication.shared.delegate as! AppDelegate 31 | try? loadStyleSheetAsync(appDelegate.selectedMapStyle) { [unowned self] (style) in 32 | self.singleTapGestureDelegate = self 33 | self.sceneDidLoad = true 34 | _ = self.showCurrentLocation(true) 35 | self.showFindMeButon(true) 36 | if self.firstTimeZoomToCurrentLocation { self.shouldZoomToCurrentLocation() } 37 | } 38 | setupSwitchLocaleBtn() 39 | setupStyleNotification() 40 | setupSwitchRoutingButton() 41 | let alert = UIAlertController(title: "Tap to Route", message: "Tap anywhere on the map to route!", preferredStyle: .alert) 42 | alert.addAction(UIAlertAction(title: "Ok!", style: UIAlertActionStyle.default, handler: nil)) 43 | present(alert, animated: true, completion: nil) 44 | } 45 | 46 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 47 | guard let identifier = segue.identifier else { 48 | return 49 | } 50 | switch identifier { 51 | case routeListSegueId: 52 | guard let routeResult = currentRouteResult else { return } 53 | if let vc = segue.destination as? DemoRoutingResultTableVC { 54 | vc.routingResult = routeResult 55 | } 56 | default: 57 | break 58 | } 59 | } 60 | 61 | //MARK:- Private 62 | 63 | private func setupSwitchRoutingButton() { 64 | let button = UIBarButtonItem.init(title: "Route Type", style: .plain, target: self, action: #selector(changeRouterCostingModel)) 65 | navigationItem.leftBarButtonItem = button 66 | } 67 | 68 | @objc private func changeRouterCostingModel() { 69 | let costingModels:[String : OTRRoutingCostingModel] = [ 70 | "Auto" : .auto, 71 | "Shorter Distance Auto" : .autoShorter, 72 | "Bicycle" : .bicycle, 73 | "Bus" : .bus, 74 | "Multimodal" : .multimodal, 75 | "Walking" : .pedestrian 76 | ] 77 | 78 | let actionSheet = UIAlertController.init(title: "Route Type", message: "Choose a route type", preferredStyle: .actionSheet) 79 | for (actionTitle, costModel) in costingModels { 80 | actionSheet.addAction(UIAlertAction.init(title: actionTitle, style: .default, handler: { [unowned self] (action) in 81 | self.costingModel = costModel 82 | if let point = self.lastRoutingPoint { 83 | self.requestRoute(toPoint: point) 84 | } 85 | })) 86 | } 87 | 88 | let presentationController = actionSheet.popoverPresentationController 89 | presentationController?.barButtonItem = navigationItem.leftBarButtonItem 90 | navigationController?.present(actionSheet, animated: true, completion: nil) 91 | } 92 | 93 | private func setupSwitchLocaleBtn() { 94 | let btn = UIBarButtonItem.init(title: "Router Language", style: .plain, target: self, action: #selector(changeRouterLanguage)) 95 | navigationItem.rightBarButtonItem = btn 96 | } 97 | 98 | @objc private func changeRouterLanguage() { 99 | let languageIdByActionSheetTitle = [ 100 | "English": "en-US", 101 | "French": "fr-FR", 102 | "Catalan": "ca-ES", 103 | "Hindi": "hi-IN", 104 | "Spanish": "es-ES", 105 | "Czech": "cs-CZ", 106 | "Italian": "it-IT", 107 | "German": "de-DE", 108 | "Slovenian": "sl-SI", 109 | "Pirate": "pirate", 110 | ] 111 | let actionSheet = UIAlertController.init(title: "Router Language", message: "Choose a language", preferredStyle: .actionSheet) 112 | for (actionTitle, languageIdentifier) in languageIdByActionSheetTitle { 113 | actionSheet.addAction(UIAlertAction.init(title: actionTitle, style: .default, handler: { [unowned self] (action) in 114 | self.routingLocale = Locale.init(identifier: languageIdentifier) 115 | })) 116 | } 117 | actionSheet.addAction(UIAlertAction.init(title: "Cancel", style: .cancel, handler: { [unowned self] (action) in 118 | self.dismiss(animated: true, completion: nil) 119 | })) 120 | let presentationController = actionSheet.popoverPresentationController 121 | presentationController?.barButtonItem = navigationItem.rightBarButtonItem 122 | navigationController?.present(actionSheet, animated: true, completion: nil) 123 | } 124 | 125 | private func requestRoute(toPoint: OTRGeoPoint) { 126 | guard let routingController = try? RoutingController.controller() else { return } 127 | routingController.updateLocale(routingLocale) 128 | 129 | guard let currentLocation = locationManager.currentLocation else { return } 130 | lastRoutingPoint = toPoint 131 | let startingPoint = OTRRoutingPoint(coordinate: OTRGeoPointMake(currentLocation.coordinate.latitude, currentLocation.coordinate.longitude), type: .break) 132 | let endingPoint = OTRRoutingPoint(coordinate: lastRoutingPoint!, type: .break) 133 | 134 | _ = routingController.requestRoute(withLocations: [startingPoint, endingPoint], 135 | costingModel: costingModel, 136 | costingOption: nil, 137 | directionsOptions: ["units" : "miles" as NSObject]) { (routingResult, token, error) in 138 | print("Error:\(String(describing: error))") 139 | self.currentRouteResult = routingResult 140 | if let result = routingResult { 141 | let _ = try? self.display(result) 142 | } 143 | 144 | 145 | } 146 | } 147 | 148 | //MARK:- Single Tap Gesture Recognizer Delegate 149 | func mapController(_ controller: MZMapViewController, recognizer: UIGestureRecognizer, shouldRecognizeSingleTapGesture location: CGPoint) -> Bool { 150 | return true 151 | } 152 | 153 | func mapController(_ controller: MZMapViewController, recognizer: UIGestureRecognizer, didRecognizeSingleTapGesture location: CGPoint) { 154 | let point = tgViewController.screenPosition(toLngLat: location) 155 | requestRoute(toPoint: OTRGeoPoint(coordinate: point)) 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /SampleApp/DemoRoutingResultTableVC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RoutingResultTableVC.swift 3 | // ios-sdk 4 | // 5 | // Created by Matt Smollinger on 10/4/16. 6 | // Copyright © 2016 Mapzen. All rights reserved. 7 | // 8 | 9 | import OnTheRoad 10 | 11 | class DemoRoutingResultTableVC: SampleTableViewController { 12 | 13 | var routingResult: OTRRoutingResult? 14 | 15 | let cellIdent = "basicCellIdent" 16 | 17 | // MARK: - Table view data source 18 | 19 | override func numberOfSections(in tableView: UITableView) -> Int { 20 | return 1 21 | } 22 | 23 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 24 | guard let result = routingResult else { 25 | return 0 26 | } 27 | 28 | return result.legs[0].maneuvers.count 29 | } 30 | 31 | func display(_ route : OTRRoutingResult){ 32 | routingResult = route 33 | tableView.reloadData() 34 | } 35 | 36 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 37 | let cell = tableView.dequeueReusableCell(withIdentifier: cellIdent, for: indexPath) as! DemoRouteDirectionCell 38 | 39 | guard let result = routingResult else { 40 | return cell 41 | } 42 | 43 | let resultLeg = result.legs[0] 44 | let manuever = resultLeg.maneuvers[indexPath.row] 45 | let instruction = manuever.instruction 46 | 47 | cell.titleLabel.text = instruction 48 | let length = String(format: "%.2f", manuever.length) 49 | cell.detailLabel.text = String("\(length) mi") 50 | return cell 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /SampleApp/DemoSearchListViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RoutingSearchVC.swift 3 | // ios-sdk 4 | // 5 | // Created by Matt Smollinger on 10/4/16. 6 | // Copyright © 2016 Mapzen. All rights reserved. 7 | // 8 | import UIKit 9 | 10 | class DemoSearchListViewController: SampleAutocompleteTableViewController { 11 | 12 | var delegate : AutocompleteSearchDelegate? 13 | 14 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 15 | super.tableView(tableView, didSelectRowAt: indexPath) 16 | if let delegate = delegate { 17 | delegate.selected((self.results?[indexPath.row])!) 18 | let _ = self.navigationController?.popViewController(animated: true) 19 | } 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /SampleApp/DemoSearchPinsViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SearchPinsViewController.swift 3 | // ios-sdk 4 | // 5 | // Created by Matt Smollinger on 1/6/17. 6 | // Copyright © 2017 Mapzen. All rights reserved. 7 | // 8 | 9 | import TangramMap 10 | import Mapzen_ios_sdk 11 | 12 | class DemoSearchPinsViewController: SampleMapViewController, UITextFieldDelegate, AutocompleteSearchDelegate { 13 | 14 | @IBOutlet weak var searchField: UITextField! 15 | 16 | @IBOutlet weak var displaySearch: UIButton! 17 | let searchListSegueId = "searchListSegueId" 18 | 19 | override func viewDidLoad() { 20 | super.viewDidLoad() 21 | let appDelegate = UIApplication.shared.delegate as! AppDelegate 22 | try? loadStyleSheetAsync(appDelegate.selectedMapStyle) { [unowned self] (style) in 23 | self.sceneDidLoad = true 24 | let _ = self.showCurrentLocation(true) 25 | self.showFindMeButon(true) 26 | if self.firstTimeZoomToCurrentLocation { self.shouldZoomToCurrentLocation() } 27 | } 28 | 29 | view.bringSubview(toFront: searchField) 30 | view.bringSubview(toFront: displaySearch) 31 | searchField.delegate = self 32 | setupStyleNotification() 33 | } 34 | 35 | func textFieldShouldReturn(_ textField: UITextField) -> Bool { 36 | textField.resignFirstResponder() 37 | return false 38 | } 39 | 40 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 41 | guard let identifier = segue.identifier else { 42 | return 43 | } 44 | switch identifier { 45 | case searchListSegueId: 46 | if let searchVC = segue.destination as? DemoSearchListViewController { 47 | searchVC.delegate = self 48 | } 49 | break 50 | 51 | default: 52 | break 53 | } 54 | } 55 | 56 | func annotationClicked(annotation: PeliasMapkitAnnotation) { 57 | let coordinates = "lat: \(annotation.coordinate.latitude), lon:\(annotation.coordinate.longitude)" 58 | let alert = UIAlertController(title: annotation.title, message: coordinates, preferredStyle: .alert) 59 | alert.addAction(UIAlertAction(title: "Ok", style: .default, handler: nil)) 60 | present(alert, animated: true, completion: nil) 61 | } 62 | 63 | func selected(_ location: PeliasMapkitAnnotation) { 64 | print("Selected \(String(describing: location.title))") 65 | searchField.text = location.title 66 | location.setTarget(target: self, action: #selector(self.annotationClicked(annotation:))) 67 | removeMapAnnotations() 68 | try? self.add([location]) 69 | 70 | animate(toZoomLevel: max(10, self.zoom), withDuration: 1.0) 71 | animate(toPosition: TGGeoPointMake(location.coordinate.longitude, location.coordinate.latitude), withDuration: 1.0) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /SampleApp/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | Mapzen SDK 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.1.1 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSRequiresIPhoneOS 26 | 27 | MAPZEN_API_KEY 28 | $(MAPZEN_API_KEY) 29 | NSLocationAlwaysAndWhenInUseUsageDescription 30 | Need location to make things happen! 31 | NSLocationWhenInUseUsageDescription 32 | Need location to make things happen! 33 | UIBackgroundModes 34 | 35 | location 36 | 37 | UILaunchStoryboardName 38 | LaunchScreen 39 | UIMainStoryboardFile 40 | Main 41 | UIRequiredDeviceCapabilities 42 | 43 | location-services 44 | armv7 45 | 46 | UISupportedInterfaceOrientations 47 | 48 | UIInterfaceOrientationPortrait 49 | UIInterfaceOrientationLandscapeLeft 50 | UIInterfaceOrientationLandscapeRight 51 | 52 | UISupportedInterfaceOrientations~ipad 53 | 54 | UIInterfaceOrientationPortrait 55 | UIInterfaceOrientationPortraitUpsideDown 56 | UIInterfaceOrientationLandscapeLeft 57 | UIInterfaceOrientationLandscapeRight 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /SampleApp/RouteDisplayViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RouteDisplayViewController.swift 3 | // ios-sdk 4 | // 5 | // Created by Matt Smollinger on 1/15/17. 6 | // Copyright © 2017 Mapzen. All rights reserved. 7 | // 8 | 9 | import OnTheRoad 10 | 11 | class RouteDisplayViewController: SampleMapViewController { 12 | 13 | var routingResult: OTRRoutingResult? 14 | 15 | override func viewDidLoad() { 16 | super.viewDidLoad() 17 | try? loadStyle(.bubbleWrap) 18 | } 19 | 20 | func show(_ route : OTRRoutingResult){ 21 | routingResult = route 22 | let _ = try? display(route) 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /SampleApp/RoutingSearchVC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RoutingSearchVC.swift 3 | // ios-sdk 4 | // 5 | // Created by Matt Smollinger on 10/4/16. 6 | // Copyright © 2016 Mapzen. All rights reserved. 7 | // 8 | import UIKit 9 | 10 | class RoutingSearchVC: AutocompleteTableVC { 11 | 12 | var delegate : AutocompleteSearchDelegate? 13 | 14 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 15 | super.tableView(tableView, didSelectRowAt: indexPath) 16 | if let delegate = delegate { 17 | delegate.selected((self.results?[indexPath.row])!) 18 | let _ = self.navigationController?.popViewController(animated: true) 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SampleApp/RoutingViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RoutingViewController.swift 3 | // ios-sdk 4 | // 5 | // Created by Matt Smollinger on 8/17/16. 6 | // Copyright © 2016 Mapzen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import OnTheRoad 11 | import Pelias 12 | 13 | class RoutingViewController: UIViewController, AutocompleteSearchDelegate { 14 | 15 | @IBOutlet weak var searchBar: UITextField! 16 | let routeSearchSegueID = "showRouteSearchSegue" 17 | let routeResultEmbedSegueID = "routeResultEmbedSegue" 18 | let routeListSegueId = "routeListSegue" 19 | var routeResultTable : RouteDisplayViewController? 20 | var currentRouteResult: OTRRoutingResult? 21 | private var routingLocale = Locale.current { 22 | didSet { 23 | requestRoute() 24 | } 25 | } 26 | private var destination : OTRGeoPoint? 27 | 28 | override func viewDidLoad() { 29 | super.viewDidLoad() 30 | setupSwitchLocaleBtn() 31 | } 32 | 33 | override func viewDidAppear(_ animated: Bool) { 34 | super.viewDidAppear(animated) 35 | if LocationManager.sharedManager.isAlwaysAuthorized() || LocationManager.sharedManager.isInUseAuthorized() { 36 | LocationManager.sharedManager.startUpdatingLocation() 37 | return 38 | } 39 | LocationManager.sharedManager.requestWhenInUseAuthorization() 40 | } 41 | 42 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 43 | guard let identifier = segue.identifier else { 44 | return 45 | } 46 | switch identifier { 47 | case routeSearchSegueID: 48 | if let searchVC = segue.destination as? RoutingSearchVC { 49 | searchVC.delegate = self 50 | } 51 | break 52 | case routeResultEmbedSegueID: 53 | if let resultVC = segue.destination as? RouteDisplayViewController { 54 | routeResultTable = resultVC 55 | } 56 | break 57 | case routeListSegueId: 58 | guard let routeResult = currentRouteResult else { return } 59 | if let vc = segue.destination as? RoutingResultTableVC { 60 | vc.routingResult = routeResult 61 | } 62 | default: 63 | break 64 | } 65 | } 66 | 67 | override var prefersStatusBarHidden: Bool { 68 | return false 69 | } 70 | 71 | // MARK : RoutingSearchDelegate 72 | func selected(_ location: PeliasMapkitAnnotation) { 73 | print("Selected \(location.title)") 74 | searchBar.text = location.title 75 | destination = OTRGeoPointMake(location.coordinate.latitude, location.coordinate.longitude) 76 | requestRoute() 77 | } 78 | 79 | // MARK : private 80 | private func setupSwitchLocaleBtn() { 81 | let btn = UIBarButtonItem.init(title: "Change Router Language", style: .plain, target: self, action: #selector(changeRouterLanguage)) 82 | self.navigationItem.rightBarButtonItem = btn 83 | } 84 | 85 | @objc private func changeRouterLanguage() { 86 | let languageIdByActionSheetTitle = [ 87 | "English": "en-US", 88 | "French": "fr-FR", 89 | "Catalan": "ca-ES", 90 | "Hindi": "hi-IN", 91 | "Spanish": "es-ES", 92 | "Czech": "cs-CZ", 93 | "Italian": "it-IT", 94 | "German": "de-DE", 95 | "Slovenian": "sl-SI", 96 | "Pirate": "pirate", 97 | ] 98 | let actionSheet = UIAlertController.init(title: "Router Language", message: "Choose a language", preferredStyle: .actionSheet) 99 | for (actionTitle, languageIdentifier) in languageIdByActionSheetTitle { 100 | actionSheet.addAction(UIAlertAction.init(title: actionTitle, style: .default, handler: { [unowned self] (action) in 101 | self.routingLocale = Locale.init(identifier: languageIdentifier) 102 | })) 103 | } 104 | actionSheet.addAction(UIAlertAction.init(title: "Cancel", style: .cancel, handler: { [unowned self] (action) in 105 | self.dismiss(animated: true, completion: nil) 106 | })) 107 | self.navigationController?.present(actionSheet, animated: true, completion: nil) 108 | } 109 | 110 | private func requestRoute() { 111 | guard let routingController = try? RoutingController.controller() else { return } 112 | routingController.updateLocale(routingLocale) 113 | 114 | guard let currentLocation = LocationManager.sharedManager.currentLocation, let destination = destination else { return } 115 | let startingPoint = OTRRoutingPoint(coordinate: OTRGeoPointMake(currentLocation.coordinate.latitude, currentLocation.coordinate.longitude), type: .break) 116 | let endingPoint = OTRRoutingPoint(coordinate: destination, type: .break) 117 | 118 | _ = routingController.requestRoute(withLocations: [startingPoint, endingPoint], 119 | costingModel: .auto, 120 | costingOption: nil, 121 | directionsOptions: ["units" : "miles" as NSObject]) { (routingResult, token, error) in 122 | print("Error:\(error)") 123 | self.currentRouteResult = routingResult 124 | self.routeResultTable?.show(routingResult!) 125 | 126 | } 127 | } 128 | 129 | } 130 | -------------------------------------------------------------------------------- /SampleApp/SampleAutocompleteTableViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AutocompleteTableVC.swift 3 | // ios-sdk 4 | // 5 | // Created by Matt Smollinger on 7/12/16. 6 | // Copyright © 2016 Mapzen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CoreLocation 11 | import Mapzen_ios_sdk 12 | 13 | protocol AutocompleteSearchDelegate { 14 | func selected(_ location: PeliasMapkitAnnotation) 15 | } 16 | 17 | class SampleAutocompleteTableViewController: SampleTableViewController, UISearchResultsUpdating, UISearchBarDelegate, LocationManagerDelegate { 18 | 19 | let searchController = UISearchController(searchResultsController: nil) 20 | var results: [PeliasMapkitAnnotation]? 21 | let manager = LocationManager() 22 | var currentLocation: CLLocation? 23 | 24 | override func viewDidLoad() { 25 | super.viewDidLoad() 26 | searchController.searchResultsUpdater = self 27 | searchController.dimsBackgroundDuringPresentation = false 28 | searchController.searchBar.delegate = self 29 | self.tableView.tableHeaderView = searchController.searchBar 30 | self.definesPresentationContext = true 31 | searchController.searchBar.sizeToFit() 32 | manager.delegate = self 33 | } 34 | 35 | override func viewDidAppear(_ animated: Bool) { 36 | super.viewDidAppear(animated) 37 | if manager.isInUseAuthorized() || manager.isAlwaysAuthorized() { 38 | manager.requestLocation() 39 | return 40 | } 41 | manager.requestWhenInUseAuthorization() 42 | } 43 | 44 | // MARK: - Table view data source 45 | 46 | override func numberOfSections(in tableView: UITableView) -> Int { 47 | return 1 48 | } 49 | 50 | override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 51 | if let unwrappedResults = results { 52 | return unwrappedResults.count 53 | } 54 | return 0 55 | } 56 | 57 | func updateSearchResults(for searchController: UISearchController) { 58 | if let searchText = searchController.searchBar.text, searchController.searchBar.text?.isEmpty == false { 59 | var geoPoint = GeoPoint(latitude: 40.7312973034393, longitude: -73.99896644276561) 60 | if let location = currentLocation { 61 | geoPoint = GeoPoint(latitude: location.coordinate.latitude, longitude: location.coordinate.longitude) 62 | } 63 | let config = AutocompleteConfig(searchText: searchText, focusPoint: geoPoint, completionHandler: { (autocompleteResponse) -> Void in 64 | print("Error:\(String(describing: autocompleteResponse.error))") 65 | if let parsedItems = autocompleteResponse.peliasResponse.parsedMapItems() { 66 | self.results = parsedItems 67 | self.tableView.reloadData() 68 | } 69 | }) 70 | _ = MapzenSearch.sharedInstance.autocompleteQuery(config) 71 | } 72 | } 73 | 74 | override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 75 | let cell = tableView.dequeueReusableCell(withIdentifier: "basicCellIdent", for: indexPath) 76 | cell.textLabel?.text = results?[indexPath.row].title 77 | return cell 78 | } 79 | 80 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 81 | searchController.searchBar.resignFirstResponder() 82 | tableView.deselectRow(at: indexPath, animated: true) 83 | } 84 | 85 | //MARK: - LocationManager Delegate 86 | 87 | func authorizationDidSucceed() { 88 | manager.startUpdatingLocation() 89 | } 90 | 91 | func locationDidUpdate(_ location: CLLocation) { 92 | currentLocation = location 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /SampleApp/SampleMapViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SampleMapViewController.swift 3 | // ios-sdk 4 | // 5 | // Created by Sarah Lensing on 3/13/17. 6 | // Copyright © 2017 Mapzen. All rights reserved. 7 | // 8 | 9 | import CoreLocation 10 | import UIKit 11 | import Mapzen_ios_sdk 12 | 13 | class SampleMapViewController : MZMapViewController { 14 | 15 | var firstTimeZoomToCurrentLocation = true 16 | var sceneDidLoad = false 17 | private var myContext = 0 18 | 19 | override var prefersStatusBarHidden: Bool { 20 | return false 21 | } 22 | 23 | func shouldZoomToCurrentLocation() { 24 | if !sceneDidLoad { return } 25 | if !receivedLocation() { return } 26 | _ = resetCameraOnCurrentLocation() 27 | firstTimeZoomToCurrentLocation = false 28 | } 29 | 30 | //MARK:- Location Delegate Overrides 31 | override func locationDidUpdate(_ location: CLLocation) { 32 | super.locationDidUpdate(location) 33 | if (firstTimeZoomToCurrentLocation) { 34 | shouldZoomToCurrentLocation() 35 | } 36 | } 37 | 38 | //Notification for MapStyle changes 39 | func setupStyleNotification() { 40 | NotificationCenter.default.addObserver(self, selector: #selector(reloadMap), name: AppDelegate.MapUpdateNotification, object: nil) 41 | } 42 | 43 | @objc func reloadMap() { 44 | let appDelegate = UIApplication.shared.delegate as! AppDelegate 45 | try? loadStyleSheet(appDelegate.selectedMapStyle) 46 | } 47 | 48 | deinit { 49 | NotificationCenter.default.removeObserver(self) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /SampleApp/SampleTableViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SampleTableViewController.swift 3 | // ios-sdk 4 | // 5 | // Created by Sarah Lensing on 3/13/17. 6 | // Copyright © 2017 Mapzen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class SampleTableViewController: UITableViewController { 12 | 13 | override var prefersStatusBarHidden: Bool { 14 | return false 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /SampleApp/StylePickerVC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StylePickerVC.swift 3 | // ios-sdk 4 | // 5 | // Created by Matt Smollinger on 10/3/17. 6 | // Copyright © 2017 Mapzen. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Mapzen_ios_sdk 11 | 12 | class StylePickerVC: UITableViewController, UIPickerViewDataSource, UIPickerViewDelegate { 13 | 14 | 15 | @IBOutlet var styleSheetPicker: UIPickerView! 16 | @IBOutlet var colorPicker: UIPickerView! 17 | 18 | @IBOutlet var levelOfDetailText: UITextField! 19 | @IBOutlet var labelDensityText: UITextField! 20 | 21 | @IBOutlet var transitOverlaySwitch: UISwitch! 22 | @IBOutlet var bikeOverlaySwitch: UISwitch! 23 | @IBOutlet var walkingOverlaySwitch: UISwitch! 24 | 25 | weak var mapController : SampleMapViewController? 26 | var currentSelectedStyle: StyleSheet = BubbleWrapStyle() 27 | var currentColor: String = "" 28 | var currentLabelLevel: Int = 0 29 | var currentDetailLevel: Int = 0 30 | 31 | var availableStyles : [ String : StyleSheet ] = ["Bubble Wrap" : BubbleWrapStyle(), 32 | "Cinnabar" : CinnabarStyle(), 33 | "Refill" : RefillStyle(), 34 | "Walkabout" : WalkaboutStyle(), 35 | "Zinc" : ZincStyle()] 36 | 37 | //MARK:- Internal Funcs 38 | 39 | 40 | func transitOverlaySwitchChanged(switchState: UISwitch) { 41 | mapController?.showTransitOverlay = switchState.isOn 42 | } 43 | func bikeOverlaySwitchChanged(switchState: UISwitch) { 44 | if switchState.isOn { 45 | walkingOverlaySwitch.setOn(false, animated: true) 46 | mapController?.showWalkingPathOverlay = false 47 | } 48 | mapController?.showBikeOverlay = switchState.isOn 49 | } 50 | func walkingOverlaySwitchChanged(switchState: UISwitch) { 51 | if switchState.isOn { 52 | bikeOverlaySwitch.setOn(false, animated: true) 53 | mapController?.showBikeOverlay = false 54 | } 55 | mapController?.showWalkingPathOverlay = switchState.isOn 56 | } 57 | 58 | func setUIStateForStyle(styleSheet: StyleSheet) { 59 | colorPicker.reloadAllComponents() 60 | if styleSheet.availableDetailLevels > 0 { 61 | levelOfDetailText.text = String(styleSheet.detailLevel) 62 | levelOfDetailText.isEnabled = true 63 | } else { 64 | levelOfDetailText.text = "N/A" 65 | levelOfDetailText.isEnabled = false 66 | } 67 | 68 | if styleSheet.availableLabelLevels > 0 { 69 | labelDensityText.text = String(styleSheet.labelLevel) 70 | labelDensityText.isEnabled = true 71 | } else { 72 | labelDensityText.text = "N/A" 73 | labelDensityText.isEnabled = false 74 | } 75 | } 76 | 77 | //MARK:- Controller Lifecycle 78 | 79 | override func viewDidLoad() { 80 | super.viewDidLoad() 81 | transitOverlaySwitch.setOn(mapController!.showTransitOverlay, animated: true) 82 | transitOverlaySwitch.addTarget(self, action: #selector(transitOverlaySwitchChanged(switchState:)), for: .valueChanged) 83 | 84 | bikeOverlaySwitch.setOn(mapController!.showBikeOverlay, animated: true) 85 | bikeOverlaySwitch.addTarget(self, action: #selector(bikeOverlaySwitchChanged(switchState:)), for: .valueChanged) 86 | 87 | walkingOverlaySwitch.setOn(mapController!.showWalkingPathOverlay, animated: true) 88 | walkingOverlaySwitch.addTarget(self, action: #selector(walkingOverlaySwitchChanged(switchState:)), for: .valueChanged) 89 | 90 | let appDelegate = UIApplication.shared.delegate as! AppDelegate 91 | currentSelectedStyle = appDelegate.selectedMapStyle 92 | currentColor = currentSelectedStyle.currentColor 93 | currentDetailLevel = currentSelectedStyle.detailLevel 94 | currentLabelLevel = currentSelectedStyle.labelLevel 95 | 96 | setUIStateForStyle(styleSheet: currentSelectedStyle) 97 | } 98 | 99 | 100 | override func viewWillAppear(_ animated: Bool) { 101 | super.viewWillAppear(animated) 102 | let values = Array(availableStyles.values) 103 | 104 | //Set the style picker to the correct current style 105 | let index = values.index { (style) -> Bool in 106 | style.relativePath == currentSelectedStyle.relativePath 107 | } 108 | if let unwrappedIndex = index { 109 | styleSheetPicker.selectRow(unwrappedIndex, inComponent: 0, animated: false) 110 | } 111 | 112 | //Set the color picker to the correct current color (assuming we have one) 113 | if currentSelectedStyle.availableColors.count > 0 && 114 | !currentSelectedStyle.currentColor.isEmpty { 115 | if let colorIndex = currentSelectedStyle.availableColors.index(of: currentSelectedStyle.currentColor) { 116 | colorPicker.selectRow(colorIndex, inComponent: 0, animated: false) 117 | } 118 | } 119 | } 120 | func saveTextField(_ textField: UITextField) { 121 | guard let text = textField.text, var level = Int(text) else { return } 122 | 123 | if level > currentSelectedStyle.availableLabelLevels { level = currentSelectedStyle.availableLabelLevels } 124 | if level < 0 { level = 0 } 125 | 126 | if textField == levelOfDetailText { 127 | currentSelectedStyle.detailLevel = level 128 | } 129 | 130 | if textField == labelDensityText { 131 | currentSelectedStyle.labelLevel = level 132 | } 133 | } 134 | 135 | //MARK:- Interface Builder 136 | @IBAction func savePressed(_ sender: Any) { 137 | let appDelegate = UIApplication.shared.delegate as! AppDelegate 138 | saveTextField(labelDensityText) 139 | saveTextField(levelOfDetailText) 140 | appDelegate.selectedMapStyle = currentSelectedStyle 141 | self.dismiss(animated: true, completion: nil) 142 | } 143 | @IBAction func cancelPressed(_ sender: Any) { 144 | self.dismiss(animated: true, completion: nil) 145 | } 146 | 147 | //MARK:- Picker View 148 | func numberOfComponents(in pickerView: UIPickerView) -> Int { 149 | return 1 150 | } 151 | 152 | func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int { 153 | if pickerView == styleSheetPicker { 154 | return availableStyles.count 155 | } 156 | if pickerView == colorPicker { 157 | let styleKeys = Array(availableStyles.keys) 158 | if currentSelectedStyle.availableColors.count > 0 && 159 | styleKeys[styleSheetPicker.selectedRow(inComponent: 0)] != "Zinc"{ 160 | // We want to return 1 in the event we have no colors to show "No Colors" so just need this 161 | return currentSelectedStyle.availableColors.count 162 | } 163 | } 164 | return 1 165 | } 166 | 167 | func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? { 168 | let styleKeys = Array(availableStyles.keys) 169 | if pickerView == styleSheetPicker { 170 | return styleKeys[row] 171 | } 172 | if pickerView == colorPicker { 173 | if currentSelectedStyle.availableColors.count == 0 || 174 | styleKeys[styleSheetPicker.selectedRow(inComponent: 0)] == "Zinc" { 175 | return "N/A" 176 | } 177 | return currentSelectedStyle.availableColors[row] 178 | } 179 | 180 | return "???" 181 | } 182 | 183 | 184 | func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) { 185 | if pickerView == styleSheetPicker { 186 | let keys = Array(availableStyles.keys) 187 | let style = availableStyles[keys[row]] 188 | guard let unwrappedStyle = style else { return } 189 | currentSelectedStyle = unwrappedStyle 190 | setUIStateForStyle(styleSheet: unwrappedStyle) 191 | } 192 | if pickerView == colorPicker { 193 | if currentSelectedStyle.availableColors.count == 0 { return } 194 | currentSelectedStyle.currentColor = currentSelectedStyle.availableColors[row] 195 | } 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /circle.yml: -------------------------------------------------------------------------------- 1 | machine: 2 | xcode: 3 | version: "8.3.3" 4 | environment: 5 | GYM_CODE_SIGNING_IDENTITY: "iPhone Distribution: Michael Cunningham (LTQC954SPQ)" 6 | 7 | checkout: 8 | post: 9 | - git submodule sync 10 | - git submodule update --init --recursive 11 | 12 | dependencies: 13 | override: 14 | - pod install: 15 | timeout: 1800 16 | cache_directories: 17 | - "~/.cocoapods" 18 | test: 19 | override: 20 | - if [[ "$CIRCLE_BRANCH" = "master" ]] ; then scripts/build_test_objc.sh ; fi 21 | - scripts/build_test_swift.sh 22 | deployment: 23 | alpha_distribution: 24 | branch: master 25 | commands: 26 | - fastlane master 27 | - scripts/deploy.sh 28 | - sudo pip install 'Circle-Beacon == 2.0.0' 29 | - alert-circle mapzen documentation master $CIRCLE_TOKEN 30 | -------------------------------------------------------------------------------- /docs/basic-functions.md: -------------------------------------------------------------------------------- 1 | # Position, rotation, zoom, and tilt 2 | 3 | When the map style finishes loading, your `OnStyleLoaded` closure will be executed and you can begin to manipulate the map. Set the position, rotation, zoom, and tilt as follows: 4 | 5 | ```swift 6 | import Foundation 7 | import Mapzen-ios-sdk 8 | 9 | class PositionExampleViewController: MZMapViewController { 10 | 11 | override func viewDidLoad() { 12 | super.viewDidLoad() 13 | _ = try? loadStyleAsync(.bubbleWrap) { [unowned self] (style) in 14 | self.position = TGGeoPointMake(-73.9903, 40.74433) 15 | self.rotation = 0 16 | self.zoom = 17 17 | self.tilt = 0 18 | } 19 | } 20 | 21 | } 22 | ``` 23 | -------------------------------------------------------------------------------- /docs/documentation.md: -------------------------------------------------------------------------------- 1 | # API Docs 2 | 3 | Browse the Mapzen iOS SDK API docs generated using [Jazzy](https://github.com/realm/jazzy). 4 | 5 | ### [Browse Documentation](http://mapzen.github.io/ios/) 6 | -------------------------------------------------------------------------------- /docs/features.md: -------------------------------------------------------------------------------- 1 | # Add features to a map 2 | 3 | The iOS SDK offers ways to add various feature overlays to a map as markers (points), polygons, and polylines. 4 | 5 | ## Markers 6 | There are three different marker classes: `PointMarker`, `SystemPointMarker`, and `SelectableSystemPointMarker`. Use `PointMarker` for markers with either a custom background color or image. Use `SystemPointMarker` for items such as a current location indicator, route location arrow, or dropped pin. If you would like selectable system markers, use `SelectableSystemPointMarker` for search and route start & end pins. Add the marker to the map by calling `MZMapViewController#addMarker(GenericMarker)`. 7 | 8 | ```swift 9 | import Foundation 10 | import Mapzen-ios-sdk 11 | 12 | class FeatureExampleViewController: MZMapViewController { 13 | 14 | override func viewDidLoad() { 15 | super.viewDidLoad() 16 | _ = try? loadStyleAsync(.bubbleWrap) { [unowned self] (style) in 17 | self.position = TGGeoPointMake(-73.9908, 40.73711) 18 | self.zoom = 14 19 | self.addMarkers() 20 | } 21 | } 22 | 23 | fileprivate func addMarkers() { 24 | let pointMarker = PointMarker(size: CGSize(width: 30, height: 30)) 25 | pointMarker.icon = UIImage.init(named: "logo") 26 | pointMarker.backgroundColor = UIColor.purple 27 | pointMarker.point = TGGeoPointMake(-73.9903, 40.74433) 28 | self.addMarker(pointMarker); 29 | 30 | let systemMarker = SystemPointMarker(markerType: .currentLocation) 31 | systemMarker.point = TGGeoPointMake(-73.984770, 40.734807) 32 | self.addMarker(systemMarker); 33 | 34 | let selectableSystemMarker = SelectableSystemPointMarker(markerType: .searchPin) 35 | selectableSystemMarker.point = TGGeoPointMake(-73.998674, 40.732172) 36 | self.addMarker(selectableSystemMarker); 37 | } 38 | } 39 | ``` 40 | 41 | ## Polygons 42 | To add a polygon to the map, create a `TGGeoPolygon` and add points to it. Then create a `PolygonMarker`, set its polygon to be the one you created above and call `MZMapViewController#addMarker(PolygonMarker)`. 43 | 44 | ```swift 45 | import Foundation 46 | import Mapzen-ios-sdk 47 | 48 | class FeatureExampleViewController: MZMapViewController { 49 | 50 | override func viewDidLoad() { 51 | super.viewDidLoad() 52 | _ = try? loadStyleAsync(.bubbleWrap) { [unowned self] (style) in 53 | self.position = TGGeoPointMake(-73.9908, 40.73711) 54 | self.zoom = 14 55 | self.addPolygon() 56 | } 57 | } 58 | 59 | fileprivate func addPolygon() { 60 | let polygon = TGGeoPolygon.init() 61 | polygon.startPath(TGGeoPointMake(-73.9903, 40.74433)) 62 | polygon.add(TGGeoPointMake(-73.984770, 40.734807)) 63 | polygon.add(TGGeoPointMake(-73.998674, 40.732172)) 64 | polygon.add(TGGeoPointMake(-73.996142, 40.741050)) 65 | let marker = PolygonMarker() 66 | marker.polygon = polygon 67 | marker.backgroundColor = UIColor.green 68 | self.addMarker(marker) 69 | } 70 | 71 | } 72 | ``` 73 | 74 | ## Polylines 75 | To add a polyline to the map, create a `TGGeoPolyline` and add points to it. Then create a `PolylineMarker`, set its polyline to be the one you created above and call `MZMapViewController#addMarker(PolylineMarker)`. 76 | 77 | ```swift 78 | import Foundation 79 | import Mapzen-ios-sdk 80 | 81 | class FeatureExampleViewController: MZMapViewController { 82 | 83 | override func viewDidLoad() { 84 | super.viewDidLoad() 85 | _ = try? loadStyleAsync(.bubbleWrap) { [unowned self] (style) in 86 | self.position = TGGeoPointMake(-73.9908, 40.73711) 87 | self.zoom = 14 88 | self.addPolyline() 89 | } 90 | } 91 | 92 | fileprivate func addPolyline() { 93 | let polyline = TGGeoPolyline.init() 94 | polyline.add(TGGeoPointMake(-73.9903, 40.74433)) 95 | polyline.add(TGGeoPointMake(-73.984770, 40.734807)) 96 | polyline.add(TGGeoPointMake(-73.998674, 40.732172)) 97 | polyline.add(TGGeoPointMake(-73.996142, 40.741050)) 98 | let marker = PolylineMarker() 99 | marker.polyline = polyline 100 | marker.backgroundColor = UIColor.blue 101 | self.addMarker(marker) 102 | } 103 | 104 | } 105 | ``` 106 | -------------------------------------------------------------------------------- /docs/gesture-delegates.md: -------------------------------------------------------------------------------- 1 | # Gesture Delegates 2 | 3 | The map supports taps, double taps, shoves, scales, rotations, pans, and long presses. To receive information about when these events occur, implement a delegate protocol. 4 | 5 | ```swift 6 | import UIKit 7 | import Mapzen-ios-sdk 8 | 9 | class GestureExampleViewController: MZMapViewController, MapSingleTapGestureDelegate, MapDoubleTapGestureDelegate, MapLongPressGestureDelegate, MapPanGestureDelegate, MapPinchGestureDelegate, MapRotateGestureDelegate, MapShoveGestureDelegate { 10 | 11 | override func viewDidLoad() { 12 | super.viewDidLoad() 13 | 14 | try? loadStyleAsync(.bubbleWrap) { [unowned self] (style) in 15 | self.setupDelegates() 16 | } 17 | } 18 | 19 | //MARK: Private 20 | private func setupDelegates() { 21 | self.singleTapGestureDelegate = self 22 | self.doubleTapGestureDelegate = self 23 | self.longPressGestureDelegate = self 24 | self.panDelegate = self 25 | self.pinchDelegate = self 26 | self.rotateDelegate = self 27 | self.shoveDelegate = self 28 | } 29 | 30 | private func logGesture(_ gesture: String) { 31 | print("Gesture: \(gesture)") 32 | } 33 | 34 | //MARK: MapSingleTapGestureDelegate 35 | func mapController(_ controller: MZMapViewController, recognizer: UIGestureRecognizer, shouldRecognizeSingleTapGesture location: CGPoint) -> Bool { 36 | return true 37 | } 38 | 39 | 40 | func mapController(_ controller: MZMapViewController, recognizer: UIGestureRecognizer, didRecognizeSingleTapGesture location: CGPoint) { 41 | logGesture("Single tap") 42 | } 43 | 44 | //MARK: MapDoubleTapGestureDelegate 45 | func mapController(_ controller: MZMapViewController, recognizer: UIGestureRecognizer, shouldRecognizeDoubleTapGesture location: CGPoint) -> Bool { 46 | return true 47 | } 48 | 49 | func mapController(_ controller: MZMapViewController, recognizer: UIGestureRecognizer, didRecognizeDoubleTapGesture location: CGPoint) { 50 | logGesture("Double tap") 51 | } 52 | 53 | //MARK: MapLongPressGestureDelegate 54 | func mapController(_ controller: MZMapViewController, recognizer: UIGestureRecognizer, shouldRecognizeLongPressGesture location: CGPoint) -> Bool { 55 | return true 56 | } 57 | 58 | func mapController(_ controller: MZMapViewController, recognizer: UIGestureRecognizer, didRecognizeLongPressGesture location: CGPoint) { 59 | logGesture("Long press") 60 | } 61 | 62 | //MARK: MapPanGestureDelegate 63 | func mapController(_ controller: MZMapViewController, didPanMap displacement: CGPoint) { 64 | logGesture("Pan") 65 | } 66 | 67 | //MARK: MapPinchGestureDelegate 68 | func mapController(_ controller: MZMapViewController, didPinchMap location: CGPoint) { 69 | logGesture("Pinch") 70 | } 71 | 72 | //MARK: MapRotateGestureDelegate 73 | func mapController(_ controller: MZMapViewController, didRotateMap location: CGPoint) { 74 | logGesture("Rotate") 75 | } 76 | 77 | //MARK: MapShoveGestureDelegate 78 | func mapController(_ controller: MZMapViewController, didShoveMap displacement: CGPoint) { 79 | logGesture("Shove") 80 | } 81 | } 82 | ``` 83 | -------------------------------------------------------------------------------- /docs/getting-started.md: -------------------------------------------------------------------------------- 1 | # Getting started 2 | 3 | ## 1. Sign up for an API key 4 | Sign up for an API key from the [Mapzen developer portal](https://mapzen.com/documentation/overview/). Then, use it in the App Delegate's `applicationDidFinishLaunching:`: 5 | 6 | ```swift 7 | import Mapzen-ios-sdk 8 | ---- Inside your applicationDidFinishLaunching() ---- 9 | MapzenManager.sharedManager.apiKey = "your-mapzen-api-key" 10 | ``` 11 | 12 | ## 2. Add a map to your storyboard 13 | Adding a Mapzen map to your storyboard is as easy as: 14 | 15 | 1. Drag a standard UIViewController onto your storyboard canvas. 16 | 2. Create a subclass file named, for example: `DemoMapViewController` and set its super class to be `MZMapViewController`. 17 | 3. Back, in the storyboard, change the UIViewController's subclass to be `DemoMapViewController`. 18 | 19 | ## 3. Initialize the map 20 | Override `viewDidLoad()` in `DemoMapViewController`'s implementation and instruct it to load a map style like so: 21 | ```swift 22 | _ = try? loadStyleAsync(.bubbleWrap) { [unowned self] (style) in 23 | // the map is now ready for interaction 24 | } 25 | ``` 26 | This will load the house style [Bubble Wrap](https://github.com/tangrams/bubble-wrap) that's packaged with the SDK. 27 | 28 | 29 | Your map is now ready to use. 30 | 31 | For advanced use (animations, custom styles, etc.) please refer to the [Tangram ES](https://github.com/tangrams/tangram-es) documentation. 32 | -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | # Mapzen iOS SDK 2 | 3 | The Mapzen iOS SDK is a thin wrapper that packages up everything you need to use Mapzen services in your iOS applications. It also simplifies setup, installation, API key management and generally makes your life better. 4 | 5 | ## Components 6 | 7 | Our SDK includes map rendering, search/geocoding, routing client, and location tracking. It's built using the following standalone libraries and services from Mapzen: 8 | 9 | * **[Tangram ES](https://github.com/tangrams/tangram-es)** 2D and 3D map renderer using OpenGL ES 10 | * **[Pelias iOS SDK](https://github.com/pelias/pelias-ios-sdk)** Mapzen Search client for iOS 11 | * **[On the Road iOS](https://github.com/mapzen/on-the-road_ios)** Mapzen Routing client and routing utilities 12 | 13 | ## Find out more 14 | 15 | Check out the rest of the documentation for how to get started or you can cruise around the [source code on GitHub](https://github.com/mapzen/ios). 16 | -------------------------------------------------------------------------------- /docs/installation.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | The Mapzen iOS SDK can be included in any iOS app via Cocoapods. It includes everything you need to get started, and includes a sample app you can also give a spin. 4 | 5 | ## Cocoapods 6 | 7 | We're going to assume you have the latest stable build of Cocoapods installed. If not, check out how to install it over at https://cocoapods.org/ 8 | 9 | You can try out the SDK using: 10 | `pod try Mapzen-ios-sdk` 11 | 12 | Adding to your app is as simple as adding the following to your app's podfile: 13 | `pod 'Mapzen-ios-sdk'` 14 | -------------------------------------------------------------------------------- /docs/location-services.md: -------------------------------------------------------------------------------- 1 | # Location Services 2 | It is extremely easy to show the user's current location on the map. Simply call `MZMapViewController#showCurrentLocation(true)` to display an icon on the map. The iOS SDK also supports visually tracking and centering on the user's current location. To display a button to enable this behavior, call `MZMapViewController#showFindMeButon(true)`. 3 | 4 | ```swift 5 | import Mapzen-ios-sdk 6 | 7 | class LocationExampleViewController: MZMapViewController { 8 | 9 | override func viewDidLoad() { 10 | super.viewDidLoad() 11 | 12 | try? loadStyleAsync(.bubbleWrap) { [unowned self] (style) in 13 | _ = self.showCurrentLocation(true) 14 | _ = self.showFindMeButon(true) 15 | } 16 | } 17 | } 18 | ``` 19 | -------------------------------------------------------------------------------- /docs/places.md: -------------------------------------------------------------------------------- 1 | # Mapzen Places 2 | 3 | We will be building this feature soon. Track its progress [here](https://github.com/mapzen/ios/issues). Feel free to create issues or submit pull requests, we welcome outside contributions! 4 | -------------------------------------------------------------------------------- /docs/search.md: -------------------------------------------------------------------------------- 1 | # Search 2 | 3 | ## Getting Started 4 | Be sure to sign up for an API key as described [here](https://mapzen.com/documentation/ios/getting-started/). After you have a key configured in your app, use the main entry point, `MapzenSearch`, for executing search-related queries. 5 | 6 | ## Search 7 | Find a place by searching for an address or name. 8 | 9 | ```swift 10 | private func search() { 11 | let config = SearchConfig.init(searchText: "pizza") { (response) in 12 | // display result 13 | } 14 | _ = MapzenSearch.sharedInstance.search(config) 15 | } 16 | ``` 17 | 18 | ## Autocomplete 19 | Get real-time result suggestions with autocomplete. 20 | 21 | ```swift 22 | private func autocomplete() { 23 | let point = GeoPoint.init(latitude: 40.74433, longitude: -73.9903) 24 | let config = AutocompleteConfig.init(searchText: "pizza", focusPoint: point) { (response) in 25 | // display result 26 | } 27 | _ = MapzenSearch.sharedInstance.autocompleteQuery(config) 28 | } 29 | ``` 30 | 31 | ## Reverse 32 | Find what is located at a certain coordinate location. 33 | 34 | ```swift 35 | private func reverseGeo() { 36 | let point = GeoPoint.init(latitude: 40.74433, longitude: -73.9903) 37 | let config = ReverseConfig.init(point: point) { (response) in 38 | // display result 39 | } 40 | _ = MapzenSearch.sharedInstance.reverseGeocode(config) 41 | } 42 | ``` 43 | 44 | ## Place 45 | Get rich details about a place. 46 | 47 | ```swift 48 | private func places() { 49 | let config = PlaceConfig.init(gids: ["gid", "anotherGid"]) { (response) in 50 | // display result 51 | } 52 | _ = MapzenSearch.sharedInstance.placeQuery(config) 53 | } 54 | ``` 55 | -------------------------------------------------------------------------------- /docs/styles.md: -------------------------------------------------------------------------------- 1 | # Switching Styles 2 | The map’s style can be configured with `MZMapViewController#loadStyleAsync`. We recommend using the asynchronous method which takes an `OnStyleLoaded` closure however, the style can also be updated synchronously. 3 | 4 | ```swift 5 | import UIKit 6 | import Mapzen-ios-sdk 7 | class StyleExampleViewController: MZMapViewController { 8 | 9 | private var styleLoaded = false 10 | 11 | lazy var activityIndicator : UIActivityIndicatorView = { 12 | let indicator = UIActivityIndicatorView.init(activityIndicatorStyle: .whiteLarge) 13 | indicator.color = .black 14 | indicator.translatesAutoresizingMaskIntoConstraints = false 15 | self.view.addSubview(indicator) 16 | 17 | let xConstraint = indicator.centerXAnchor.constraint(equalTo: self.view.centerXAnchor) 18 | let yConstraint = indicator.centerYAnchor.constraint(equalTo: self.view.centerYAnchor) 19 | NSLayoutConstraint.activate([xConstraint, yConstraint]) 20 | 21 | return indicator 22 | }() 23 | 24 | override func viewDidLoad() { 25 | super.viewDidLoad() 26 | 27 | setupSwitchStyleBtn() 28 | try? loadStyleAsync(.bubbleWrap) { [unowned self] (style) in 29 | self.styleLoaded = true 30 | } 31 | } 32 | 33 | //MARK: Private 34 | private func setupSwitchStyleBtn() { 35 | let btn = UIBarButtonItem.init(title: "Map Style", style: .plain, target: self, action: #selector(showStyleActionSheet)) 36 | self.navigationItem.rightBarButtonItem = btn 37 | } 38 | 39 | @objc private func showStyleActionSheet() { 40 | let actionSheet = UIAlertController.init(title: "Map Style", message: "Choose a map style", preferredStyle: .actionSheet) 41 | actionSheet.addAction(UIAlertAction.init(title: "Bubble Wrap", style: .default, handler: { [unowned self] (action) in 42 | self.indicateLoadStyle(style: .bubbleWrap) 43 | })) 44 | actionSheet.addAction(UIAlertAction.init(title: "Cinnabar", style: .default, handler: { [unowned self] (action) in 45 | self.indicateLoadStyle(style: .cinnabar) 46 | })) 47 | actionSheet.addAction(UIAlertAction.init(title: "Refill", style: .default, handler: { [unowned self] (action) in 48 | self.indicateLoadStyle(style: .refill) 49 | })) 50 | actionSheet.addAction(UIAlertAction.init(title: "Walkabout", style: .default, handler: { [unowned self] (action) in 51 | self.indicateLoadStyle(style: .walkabout) 52 | })) 53 | actionSheet.addAction(UIAlertAction.init(title: "Zinc", style: .default, handler: { [unowned self] (action) in 54 | self.indicateLoadStyle(style: .zinc) 55 | })) 56 | actionSheet.addAction(UIAlertAction.init(title: "Cancel", style: .cancel, handler: { [unowned self] (action) in 57 | self.dismiss(animated: true, completion: nil) 58 | })) 59 | self.navigationController?.present(actionSheet, animated: true, completion: nil) 60 | } 61 | 62 | private func indicateLoadStyle(style: MapStyle) { 63 | activityIndicator.startAnimating() 64 | try? loadStyleAsync(style, onStyleLoaded: { [unowned self] (style) in 65 | self.activityIndicator.stopAnimating() 66 | }) 67 | } 68 | } 69 | ``` 70 | -------------------------------------------------------------------------------- /docs/turn-by-turn.md: -------------------------------------------------------------------------------- 1 | # Turn-by-Turn 2 | 3 | ## Getting Started 4 | Be sure to sign up for an API key as described [here](https://mapzen.com/documentation/ios/getting-started/). After you have a key configured in your app, use the main entry point, `RoutingController`, for executing turn-by-turn queries. 5 | 6 | ## Fetch Route 7 | To fetch a route you need to give the router a list of locations and a costing model at minimum. The first and last locations must be "break" points. To configure costing options see the turn-by-turn [costing options documentation](https://mapzen.com/documentation/mobility/turn-by-turn/api-reference/#costing-options) for available parameters. And, to configure directions options such as the units, see the [directions options documentation](https://mapzen.com/documentation/mobility/turn-by-turn/api-reference/#directions-options). 8 | 9 | ```swift 10 | let router = try? RoutingController.controller() 11 | let locations = [ 12 | OTRRoutingPoint.init(coordinate: OTRGeoPoint.init(latitude: 40.74433, longitude: -73.9903), type: .break), 13 | OTRRoutingPoint.init(coordinate: OTRGeoPoint.init(latitude: 40.734807, longitude: -73.984770), type: .through), 14 | OTRRoutingPoint.init(coordinate: OTRGeoPoint.init(latitude: 40.732172, longitude: -73.998674), type: .through), 15 | OTRRoutingPoint.init(coordinate: OTRGeoPoint.init(latitude: 40.741050, longitude: -73.996142), type: .break) 16 | ] 17 | _ = router?.requestRoute(withLocations: locations, costingModel: .auto, costingOption: nil, directionsOptions: nil) { (result, asd, error) in 18 | // 19 | } 20 | ``` 21 | -------------------------------------------------------------------------------- /fastlane/Appfile: -------------------------------------------------------------------------------- 1 | app_identifier "com.mapzen.ios-sdk" # The bundle identifier of your app 2 | 3 | team_id "LTQC954SPQ" # Developer Portal Team ID 4 | 5 | # you can even provide different app identifiers, Apple IDs and team names per lane: 6 | # More information: https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Appfile.md 7 | -------------------------------------------------------------------------------- /fastlane/Fastfile: -------------------------------------------------------------------------------- 1 | # Customise this file, documentation can be found here: 2 | # https://github.com/fastlane/fastlane/tree/master/fastlane/docs 3 | # All available actions: https://docs.fastlane.tools/actions 4 | # can also be listed using the `fastlane actions` command 5 | 6 | # Change the syntax highlighting to Ruby 7 | # All lines starting with a # are ignored when running `fastlane` 8 | 9 | # If you want to automatically update fastlane if a new version is available: 10 | # update_fastlane 11 | 12 | # This is the minimum version number required. 13 | # Update this, if you use features of a newer version 14 | fastlane_version "2.27.0" 15 | 16 | default_platform :ios 17 | 18 | circle_artifacts_path = ENV['CIRCLE_ARTIFACTS'] 19 | 20 | ipa_path = "#{circle_artifacts_path}/ios-sdk.ipa" 21 | dsym_path = "#{circle_artifacts_path}/ios-sdk.app.dSYM.zip" 22 | 23 | platform :ios do 24 | before_all do 25 | cocoapods 26 | end 27 | 28 | desc "Build master" 29 | lane :master do 30 | gym( 31 | scheme: "ios-sdk", 32 | export_method: "ad-hoc", 33 | output_directory: ENV['CIRCLE_ARTIFACTS'] 34 | ) # Build your app - more options available 35 | end 36 | 37 | desc "Submit a new nighly build to Hockeyapp" 38 | lane :nightly do 39 | # Push to Hockey 40 | textile_type = "0" 41 | markdown_type = "1" 42 | hockey( 43 | api_token: ENV['HOCKEYAPP_TOKEN'], 44 | teams: ENV['NIGHTLY_SAMPLE_TEAM_ID'], 45 | notes_type: textile_type, 46 | ipa: ipa_path, 47 | dsym: dsym_path, 48 | commit_sha: ENV['CIRCLE_SHA1'], 49 | build_server_url: ENV['CIRCLE_BUILD_URL'], 50 | repository_url: ENV['CIRCLE_REPOSITORY_URL'], 51 | notes: default_changelog 52 | ) 53 | end 54 | 55 | desc "Returns a default changelog." 56 | lane :default_changelog do 57 | changelog = sh('git log --date=local --since="1 day ago" --pretty=oneline --abbrev-commit') 58 | # HAX: strip emoji from changelog 59 | changelog = changelog.sub(/[\u{1F300}-\u{1F6FF}]/, '') 60 | Actions.lane_context[SharedValues::FL_CHANGELOG] = changelog 61 | puts changelog 62 | changelog 63 | end 64 | 65 | #TBD: Might be useful to set this up to send us slack messages when builds break but only on master 66 | error do |lane, exception| 67 | # slack( 68 | # message: exception.message, 69 | # success: false 70 | # ) 71 | end 72 | end 73 | 74 | 75 | # More information about multiple platforms in fastlane: https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Platforms.md 76 | # All available actions: https://docs.fastlane.tools/actions 77 | 78 | # fastlane reports which actions are used 79 | # No personal data is recorded. Learn more at https://github.com/fastlane/enhancer 80 | -------------------------------------------------------------------------------- /fastlane/README.md: -------------------------------------------------------------------------------- 1 | fastlane documentation 2 | ================ 3 | # Installation 4 | 5 | Make sure you have the latest version of the Xcode command line tools installed: 6 | 7 | ``` 8 | xcode-select --install 9 | ``` 10 | 11 | ## Choose your installation method: 12 | 13 | 14 | 15 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 |
Homebrew 16 | Installer Script 17 | Rubygems 18 |
macOSmacOSmacOS or Linux with Ruby 2.0.0 or above
brew cask install fastlaneDownload the zip file. Then double click on the install script (or run it in a terminal window).sudo gem install fastlane -NV
30 | 31 | # Available Actions 32 | ## iOS 33 | ### ios master 34 | ``` 35 | fastlane ios master 36 | ``` 37 | Build master 38 | ### ios nightly 39 | ``` 40 | fastlane ios nightly 41 | ``` 42 | Submit a new nighly build to Hockeyapp 43 | ### ios default_changelog 44 | ``` 45 | fastlane ios default_changelog 46 | ``` 47 | Returns a default changelog. 48 | 49 | ---- 50 | 51 | This README.md is auto-generated and will be re-generated every time [fastlane](https://fastlane.tools) is run. 52 | More information about fastlane can be found on [fastlane.tools](https://fastlane.tools). 53 | The documentation of fastlane can be found on [docs.fastlane.tools](https://docs.fastlane.tools). 54 | -------------------------------------------------------------------------------- /images/ic_find_me_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapzen/ios/b77c3eab3379c021ed81f0416d0fbc04a107ccdd/images/ic_find_me_normal.png -------------------------------------------------------------------------------- /images/ic_find_me_normal@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapzen/ios/b77c3eab3379c021ed81f0416d0fbc04a107ccdd/images/ic_find_me_normal@2x.png -------------------------------------------------------------------------------- /images/ic_find_me_normal@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapzen/ios/b77c3eab3379c021ed81f0416d0fbc04a107ccdd/images/ic_find_me_normal@3x.png -------------------------------------------------------------------------------- /images/ic_find_me_pressed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapzen/ios/b77c3eab3379c021ed81f0416d0fbc04a107ccdd/images/ic_find_me_pressed.png -------------------------------------------------------------------------------- /images/ic_find_me_pressed@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapzen/ios/b77c3eab3379c021ed81f0416d0fbc04a107ccdd/images/ic_find_me_pressed@2x.png -------------------------------------------------------------------------------- /images/ic_find_me_pressed@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mapzen/ios/b77c3eab3379c021ed81f0416d0fbc04a107ccdd/images/ic_find_me_pressed@3x.png -------------------------------------------------------------------------------- /ios-sdk.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios-sdk.xcodeproj/xcshareddata/xcschemes/MapzenSDK.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 35 | 36 | 47 | 48 | 54 | 55 | 56 | 57 | 58 | 59 | 65 | 66 | 72 | 73 | 74 | 75 | 77 | 78 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /ios-sdk.xcodeproj/xcshareddata/xcschemes/SampleApp-Objc.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 34 | 40 | 41 | 42 | 43 | 44 | 50 | 51 | 52 | 53 | 54 | 55 | 66 | 68 | 74 | 75 | 76 | 77 | 78 | 81 | 82 | 83 | 89 | 91 | 97 | 98 | 99 | 100 | 102 | 103 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /ios-sdk.xcodeproj/xcshareddata/xcschemes/ios-sdk.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 34 | 40 | 41 | 42 | 44 | 50 | 51 | 52 | 53 | 54 | 60 | 61 | 62 | 63 | 64 | 65 | 76 | 78 | 84 | 85 | 86 | 87 | 88 | 91 | 92 | 93 | 99 | 101 | 107 | 108 | 109 | 110 | 112 | 113 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /ios-sdk.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios-sdk.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /release_checklist.md: -------------------------------------------------------------------------------- 1 | # Mapzen iOS SDK Release Checklist 2 | 3 | ## Requirements 4 | - Have Xcode installed 5 | - Have cocoapods installed 6 | - Have ownership privileges to update the cocoapods trunk spec 7 | 8 | ## Steps 9 | 0. Check that the Mapzen-ios-sdk.podspec and Podfile pod dependencies versions are up to date 10 | 1. Tag current master with the version you want to release, and push to github. 11 | 2. Update the Mapzen-ios-sdk.podspec version number property with that same tag 12 | 3. Update the .swift file with the current version of swift the sdk is compiled against 13 | 4. Run `pod spec lint` to make sure everything is happy. Fix issues if not happy (eventually we should document known possible issues). Submit to PR once lint is clean. 14 | 5. Once #2 PR merges, push the updated pod spec to trunk: `pod trunk push Mapzen-ios-sdk.podspec` 15 | 6. Write up release notes and release the SDK on github. 16 | 7. Re-generate API docs using Jazzy and push changes to `gh-pages` branch to update http://mapzen.github.io/ios/. 17 | -------------------------------------------------------------------------------- /scripts/build_test_objc.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | set -o pipefail && xcodebuild -sdk iphonesimulator -workspace ios-sdk.xcworkspace -scheme "SampleApp-Objc" -destination 'platform=iOS Simulator,name=iPhone 6,OS=latest' MAPZEN_API_KEY=$MAPZEN_API_KEY HOCKEY_APP_ID=$HOCKEY_APP_ID clean build test | tee $CIRCLE_ARTIFACTS/xcode_raw.log | xcpretty --color --report junit --output $CIRCLE_TEST_REPORTS/xcode/results.xml -------------------------------------------------------------------------------- /scripts/build_test_swift.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | set -o pipefail && xcodebuild -sdk iphonesimulator -workspace ios-sdk.xcworkspace -scheme "ios-sdk" -destination 'platform=iOS Simulator,name=iPhone 6,OS=latest' MAPZEN_API_KEY=$MAPZEN_API_KEY HOCKEY_APP_ID=$HOCKEY_APP_ID clean build test | tee $CIRCLE_ARTIFACTS/xcode_raw.log | xcpretty --color --report junit --output $CIRCLE_TEST_REPORTS/xcode/results.xml -------------------------------------------------------------------------------- /scripts/deploy.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # 3 | if [[ ${PERFORM_NIGHTLY} ]] 4 | then 5 | fastlane nightly 6 | fi 7 | -------------------------------------------------------------------------------- /scripts/perform_nightly.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if [ -z ${CIRCLE_TOKEN} ] 4 | then 5 | echo "[ERROR] CIRCLE_TOKEN environment variable is not set." 6 | exit 1 7 | fi 8 | 9 | trigger_build_url=https://circleci.com/api/v1/project/mapzen/ios/tree/master?circle-token=${CIRCLE_TOKEN} 10 | 11 | post_data=$(cat <