This is an example for integrating baato in iOS app for iOS version 9.0 and above.
This example is tested with the following mapbox pod configuration. Please be sure the pod for mapbox are compatible with iOS version 9.0 and above.
# Uncomment the next line to define a global platform for your project platform :ios, '9.0' source 'https://github.com/baato/BaatoPodSpec.git' source 'https://github.com/CocoaPods/Specs.git' target 'Baato iOS Example' do # Comment the next line if you don't want to use dynamic frameworks use_frameworks! # Pods for Baato iOS Example pod 'MaterialComponents/Snackbar' pod 'BaatoSwift', '~> 0.2.1' # Pods for baato-navigation pod 'Mapbox-iOS-SDK', '~> 5.2' pod 'MapboxNavigation', '~> 0.37.0'
target 'Baato iOS ExampleTests' do inherit! :search_paths # Pods for testing end
target 'Baato iOS ExampleUITests' do # Pods for testing end end
-
Clone the Baato-iOS-Example repo.
-
Open the example folder and install pod. (i.e, pod install)
-
Make a class to store Baato Access token so, that it can be accessed from anywhere for eg.
import Foundation class Constant { static let key = "Baato Public Key" //Which can be accessed with Constant.key inside the project }
-
Add the following setting in info.plist of main target for locating user and to use navigation service
- Key: Privacy - Location When In Use Usage Description Type: String Value: "Shows your location on the map: Reason why you need this feature."
- Go to project target -> Signing & Capabilities, add the following Background Modes from Capability
- Audio, Airplay, and Picture in Picture
- Location updates
- If you wish to remove mapbox info from map than use: Key: MGLMapboxMetricsEnabledSettingShownInApp Type: Boolean Value: 1
-
The example app should run, but will be crashed while starting navigation so unlock and override assertion: The xcode error pointer will auto point to assertion which is inside: Pods/MapboxDirections.swift/Sources/MapboxDirections/MBDirections.swift
@objc public init(accessToken: String?, host: String?) { //Only add this self.accessToken = "pk.xxx" apiEndpoint = URL(string: "https://api.baato.io")! }
Also remove assertion from Pods/MapboxCoreNavigation/MapboxCoreNavigation/NavigationEventsManager.swift as
lazy var accessToken: String = { guard let path = Bundle.main.path(forResource: "Info", ofType: "plist"), let dict = NSDictionary(contentsOfFile: path) as? [String: AnyObject], let token = dict["MGLMapboxAccessToken"] as? String else { //comment or remove assertion failure return "" } return token }()
All done, test the example app.
Note: Please use ResType.swift i.e, https://github.com/Bhawak/Baato-iOS-Example/blob/master/Baato%20iOS%20Example/Model/ResType.swift for decoding mapbox type direction response.
Use MGLMapViewDelegate extension and add baato url to mapview in viewDidLoad as
override func viewDidLoad(){
super.viewDidLoad()
var mView: MGLMapView!
let url = https://api.baato.io/api/v1/styles/breeze?key= + "your baato token"
mView = MGLMapView(frame: view.bounds, styleURL: url)
mView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
//setting center to Kathmandu
mView.setCenter(CLLocationCoordinate2D(latitude: 27.7172, longitude: 85.3240), zoomLevel: 12, animated: false)
mView.delegate = self
mView.logoView.image = UIImage(named: "BaatoLogo")
mView.logoView.tintColor = UIColor.init(red: 8/255, green: 127/255, blue: 82/255, alpha: 1.0)
mView.logoViewMargins = CGPoint.init(x: 5, y: 10)
//You must add MGLMapboxMetricsEnabledSettingShownInApp to yes in info.plist to remove mapbox attribution
mView.attributionButton.isHidden = true
mView.logoViewPosition = .bottomLeft
view.addSubview(mView)
}
Use NavigationMapViewDelegate and NavigationViewControllerDelegate extension to the viewcontroller
class ViewController: UIViewController, MGLMapViewDelegate, NavigationMapViewDelegate {
// Initializing parameter for navigation
let mytStyle = DayStyle()
var navigationViewController: NavigationViewController?
var navigationService: NavigationService!
var route: Route!
var router: LegacyRouteController!
var startPoint = MGLPointAnnotation()
var endPoint = MGLPointAnnotation()
override func viewDidLoad(){
super.viewDidLoad()
let url = URL(string: defStyle + "Baato access token")
//set mapstyle for navigation mapview
mytStyle.mapStyleURL = url!
}
}
extension ViewController: NavigationViewControllerDelegate {
// Reroute from custom server and map progress tacker
}
let apis = BaatoSwift.API.init(baseURL: "https://api.baato.io/api/v1", token: "Baato Key")
apis.startLat = startPoint.coordinate.latitude
apis.startLon = startPoint.coordinate.longitude
apis.destLat = point.coordinate.latitude
apis.destLon = point.coordinate.longitude
apis.navMode = BaatoSwift.NavigationMode.car
apis.navInstructions = true
//Requesting route and showing from direction API if turn by turn is not necessary
apis.getDirections { (result) in
switch result {
case .success(let navData):
guard let navData = navData else {
return
}
let routeString = navData[0].encodedPolyline
guard let style = self.mView.style else { return }
if(style.source(withIdentifier: "polyline") != nil) {
// First remove layer than source for updating
style.removeLayer(style.layer(withIdentifier: "polyline")!)
style.removeSource(style.source(withIdentifier: "polyline")!)
}
//BaatoSwift DecoderPoly class decodes route string to coordinate
let coordinates = BaatoSwift.DecoderPoly.decodePoints(routeString)
let shape = MGLPolyline(coordinates: coordinates, count: UInt(coordinates.count))
let source = MGLShapeSource(identifier: "polyline", shape: shape, options: nil)
style.addSource(source)
// Create new layer for the line.
let layer = MGLLineStyleLayer(identifier: "polyline", source: source)
layer.lineColor = NSExpression(forConstantValue: UIColor(red: 255/255, green: 0/255, blue: 0/255, alpha: 1))
layer.lineWidth = NSExpression(format: "mgl_interpolate:withCurveType:parameters:stops:($zoomLevel, 'linear', nil, %@)",
[14: 3, 18: 3])
style.addLayer(layer)
case .failure (let error) :
print(error.localizedDescription)
self.mapError(error: error)
}
}
//Requesting route and showing from Mapboxdirections API for turn by turn.
apis.getMapboxDirections{(result) in
switch result {
case .success (let data):
guard let data = data else {
return
}
let origin = Waypoint(coordinate: self.startPoint.coordinate, name: "Origin")
let destination = Waypoint(coordinate: self.point.coordinate, name: "Destination")
self.routeOptions = NavigationRouteOptions(waypoints: [origin, destination])
// Set locale so that it takes kilometer/meter(metric), default or "en" takes imperial
self.routeOptions!.locale = Locale.init(identifier: "ne")
self.routeOptions!.distanceMeasurementSystem = .metric
do {
let decoder = JSONDecoder()
//You need to use ResType class and is inside Model folder of this repo
guard let value = try? decoder.decode(ResType.self, from: data) else {
print("Error parsing JSON")
return
}
let mValue = value.data?.routes?.first
let mData = try mValue?.jsonString()
let mEro = Route.init(json: self.convertToDictionary(text: mData!)!, waypoints:[origin, destination], options: self.routeOptions!)
self.route = mEro
//Route computed prepare navigation service and start
self.startNavigation(startPoint: self.startPoint, endPoint: self.point)
} catch {
print("JSONSerialization error:", error)
}
case .failure (let error) :
print(error.localizedDescription)
self.mapError(error: error)
}
}
private func startNavigation(startPoint: MGLPointAnnotation, endPoint: MGLPointAnnotation){
navigationService = MapboxNavigationService(route: self.route, directions: nil, locationSource: nil, eventsManagerType: nil, simulating: .onPoorGPS, routerType: LegacyRouteController.self)
router = LegacyRouteController(along: self.route, dataSource: navigationService)
let navigationOptions = NavigationOptions(styles: [mytStyle], navigationService: navigationService)
self.navigationViewController = NavigationViewController(for: route, options: navigationOptions)
self.navigationViewController?.modalPresentationStyle = .fullScreen
self.navigationViewController?.delegate = self
self.navigationViewController?.showsReportFeedback = false
self.navigationViewController?.mapView?.logoView.image = UIImage(named: "BaatoLogo")
self.navigationViewController?.mapView?.attributionButton.isHidden = true
self.present(self.navigationViewController!, animated: false, completion: nil)
}
extension ViewController: NavigationViewControllerDelegate {
// Never reroute internally. Instead,
// 1. Fetch a route from your server
// 2. Map Match the coordinates from your server
// 3. Set the route on your server
func navigationViewController(_ navigationViewController: NavigationViewController, shouldRerouteFrom location: CLLocation) -> Bool {
// Here, we are simulating a custom server.
let routeOptions = NavigationRouteOptions(waypoints: [Waypoint(location: location), self.routeOptions!.waypoints.last!])
let startPoint = MGLPointAnnotation()
guard let userLocation = self.mView.userLocation else {
return false
}
startPoint.coordinate = userLocation.coordinate
startPoint.title = "Origin"
apis.startLat = startPoint.coordinate.latitude
apis.startLon = startPoint.coordinate.longitude
apis.destLat = point.coordinate.latitude
apis.destLon = point.coordinate.longitude
apis.getMapboxDirections{(result) in
self.manager.dismissAndCallCompletionBlocks(withCategory: .none)
switch result {
case .success (let data):
guard let data = data else {
return
}
let origin = Waypoint(coordinate: self.startPoint.coordinate, name: "Origin")
let destination = Waypoint(coordinate: self.point.coordinate, name: "Destination")
let routeOptions = NavigationRouteOptions(waypoints: [origin, destination])
routeOptions.distanceMeasurementSystem = .metric
do {
let decoder = JSONDecoder()
guard let value = try? decoder.decode(ResType.self, from: data) else {
print("Error parsing JSON")
return
}
let mValue = value.data?.routes?.first
let mData = try mValue?.jsonString()
let mRoute = Route.init(json: self.convertToDictionary(text: mData!)!, waypoints:[origin, destination], options: routeOptions)
let router = self.navigationService.router! as! LegacyRouteController
self.router = router
let routeProgress = RouteProgress(route: mRoute)
self.router.routeProgress = routeProgress
} catch {
print("JSONSerialization error:", error)
}
case .failure (let error) :
print(error.localizedDescription)
}
}
return false
}
func navigationViewController(_ navigationViewController: NavigationViewController, didUpdate progress: RouteProgress, with location: CLLocation, rawLocation: CLLocation) {
self.navigationViewController?.mapView?.updateCourseTracking(location: location)
}
func navigationViewControllerDidDismiss(_ navigationViewController: NavigationViewController, byCanceling canceled: Bool) {
navigationService.stop()
dismiss(animated: true, completion: nil)
}
}