Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Native Route Objects #3824

Merged
merged 7 commits into from Apr 26, 2022
Merged
8 changes: 8 additions & 0 deletions MapboxNavigation.xcodeproj/project.pbxproj
Expand Up @@ -44,6 +44,8 @@
2B42586D2657BF9100B487C3 /* TilesetDescriptorFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B42586B2657BF9000B487C3 /* TilesetDescriptorFactory.swift */; };
2B42586E2657BF9100B487C3 /* TileStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B42586C2657BF9100B487C3 /* TileStore.swift */; };
2B5407EB24470B0A006C820B /* AVAudioSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B5407EA24470B0A006C820B /* AVAudioSession.swift */; };
2B5A4AC12807124900170A2B /* RerouteController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B5A4ABF2807124800170A2B /* RerouteController.swift */; };
2B5A4AC22807124900170A2B /* RerouteControllerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B5A4AC02807124800170A2B /* RerouteControllerDelegate.swift */; };
2B72EC5E241276D10003B370 /* RouteVoiceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B72EC5D241276D10003B370 /* RouteVoiceController.swift */; };
2B72EC602412AA800003B370 /* SystemSpeechSynthesizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B72EC5F2412AA800003B370 /* SystemSpeechSynthesizer.swift */; };
2B7ACA9B25E3F84700B0ACFD /* PredictiveCacheManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B7ACA9925E3F84600B0ACFD /* PredictiveCacheManager.swift */; };
Expand Down Expand Up @@ -595,6 +597,8 @@
2B42586B2657BF9000B487C3 /* TilesetDescriptorFactory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TilesetDescriptorFactory.swift; sourceTree = "<group>"; };
2B42586C2657BF9100B487C3 /* TileStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TileStore.swift; sourceTree = "<group>"; };
2B5407EA24470B0A006C820B /* AVAudioSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AVAudioSession.swift; sourceTree = "<group>"; };
2B5A4ABF2807124800170A2B /* RerouteController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RerouteController.swift; sourceTree = "<group>"; };
2B5A4AC02807124800170A2B /* RerouteControllerDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RerouteControllerDelegate.swift; sourceTree = "<group>"; };
2B72EC5D241276D10003B370 /* RouteVoiceController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RouteVoiceController.swift; sourceTree = "<group>"; };
2B72EC5F2412AA800003B370 /* SystemSpeechSynthesizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SystemSpeechSynthesizer.swift; sourceTree = "<group>"; };
2B7ACA9925E3F84600B0ACFD /* PredictiveCacheManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PredictiveCacheManager.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1994,6 +1998,8 @@
3582A24F20EEC46B0029C5DE /* Router.swift */,
353E69031EF0C4E5007B2AE5 /* SimulatedLocationManager.swift */,
35C98730212E02B500808B82 /* RouteController.swift */,
2B5A4ABF2807124800170A2B /* RerouteController.swift */,
2B5A4AC02807124800170A2B /* RerouteControllerDelegate.swift */,
5A39B9272498F9890026DFD1 /* PassiveLocationManager.swift */,
8A3A218F25EEC00200EDA999 /* CoreNavigationNavigator.swift */,
E2805A5726CB994500165DB9 /* NSLock+MapboxInternal.swift */,
Expand Down Expand Up @@ -2916,6 +2922,7 @@
3A163AE3249901D000D66A0D /* FixLocation.swift in Sources */,
2E50E0D2264E468B009D3848 /* RoadObjectMatcherError.swift in Sources */,
8D2AA745211CDD4000EB7F72 /* NavigationService.swift in Sources */,
2B5A4AC12807124900170A2B /* RerouteController.swift in Sources */,
2B01E4B8274671550002A5F7 /* Directions+RoutingProvider.swift in Sources */,
2B7ACA9C25E3F84700B0ACFD /* PredictiveCacheOptions.swift in Sources */,
35A5413B1EFC052700E49846 /* RouteOptions.swift in Sources */,
Expand All @@ -2937,6 +2944,7 @@
2BBED93B267A3AB900F90032 /* BillingHandler.swift in Sources */,
2BE701352535948100F46E4E /* RestStop.swift in Sources */,
2B01E4B6274671550002A5F7 /* MapboxRoutingProvider.swift in Sources */,
2B5A4AC22807124900170A2B /* RerouteControllerDelegate.swift in Sources */,
8D4CF9C621349FFB009C3FEE /* NavigationServiceDelegate.swift in Sources */,
2BF398C3274FE99A000C9A72 /* HandlerFactory.swift in Sources */,
118D883626F8CA0700B2ED7B /* ActiveNavigationFeedbackType.swift in Sources */,
Expand Down
40 changes: 36 additions & 4 deletions Sources/MapboxCoreNavigation/CoreNavigationNavigator.swift
Expand Up @@ -33,7 +33,7 @@ class Navigator {
}

private lazy var routeCoordinator: RoutesCoordinator = {
.init(setRoutesHandler: { [weak self] route, legIndex, completion in
.init(mainRouteSetupHandler: { [weak self] route, legIndex, completion in
self?.navigator.setPrimaryRouteForRoute(route, legIndex: legIndex) { [weak self] result in
if result.isValue() {
let routeInfo = result.value!
Expand All @@ -55,9 +55,33 @@ class Navigator {
completion(.failure(NavigatorError.failedToUpdateRoutes(reason: "Unexpected internal response")))
}
}
}, alternativeRoutesSetupHandler: { [weak self] routes, completion in
self?.navigator.setAlternativeRoutesForRoutes(routes) { [weak self] result in
if result.isValue() {
let alternativeRoutes = result.value as? [RouteAlternative] ?? []
os_log("Navigator Alternative Routes have been updated",
log: Navigator.log,
type: .debug)
completion(.success(alternativeRoutes))
}
else if result.isError() {
let reason = (result.error as String?) ?? ""
os_log("Failed to update navigator Alternative Routes with reason: %{public}@",
log: Navigator.log,
type: .error,
reason)
completion(.failure(NavigatorError.failedToUpdateRoutes(reason: reason)))
}
else {
assertionFailure("Invalid Expected value: \(result)")
completion(.failure(NavigatorError.failedToUpdateRoutes(reason: "Unexpected internal response")))
}
}
})
}()

private(set) var rerouteController: RerouteController

/**
Provides a new or an existing `MapboxCoreNavigation.Navigator` instance. Upon first initialization will trigger creation of `MapboxNavigationNative.Navigator` and `HistoryRecorderHandle` instances,
satisfying provided configuration (`tilesVersion` and `NavigationSettings`).
Expand Down Expand Up @@ -97,14 +121,16 @@ class Navigator {
credentials: NavigationSettings.shared.directions.credentials,
tilesVersion: Self.tilesVersion,
historyDirectoryURL: Self.historyDirectoryURL,
datasetProfileIdentifier: Self.datasetProfileIdentifier)
datasetProfileIdentifier: Self.datasetProfileIdentifier,
navigatorRouterType: NavigationSettings.shared.routingProviderSource.nativeSource)
tileStore = factory.tileStore
historyRecorder = factory.historyRecorder
cacheHandle = factory.cacheHandle
roadGraph = factory.roadGraph
navigator = factory.navigator
roadObjectStore = RoadObjectStore(navigator.roadObjectStore())
roadObjectMatcher = RoadObjectMatcher(MapboxNavigationNative.RoadObjectMatcher(cache: cacheHandle))
rerouteController = RerouteController(navigator, config: factory.navigatorConfig)

subscribeNavigator()
}
Expand All @@ -124,7 +150,8 @@ class Navigator {
tilesVersion: version ?? Self.tilesVersion,
historyDirectoryURL: Self.historyDirectoryURL,
targetVersion: version.map { _ in Self.tilesVersion },
datasetProfileIdentifier: Self.datasetProfileIdentifier)
datasetProfileIdentifier: Self.datasetProfileIdentifier,
navigatorRouterType: NavigationSettings.shared.routingProviderSource.nativeSource)
tileStore = factory.tileStore
historyRecorder = factory.historyRecorder
cacheHandle = factory.cacheHandle
Expand All @@ -133,6 +160,7 @@ class Navigator {

roadObjectStore.native = navigator.roadObjectStore()
roadObjectMatcher.native = MapboxNavigationNative.RoadObjectMatcher(cache: cacheHandle)
rerouteController = RerouteController(navigator, config: factory.navigatorConfig)

subscribeNavigator()
}
Expand Down Expand Up @@ -209,10 +237,14 @@ class Navigator {

// MARK: - Navigator Updates

func setRoutes(_ route: RouteInterface, uuid: UUID, legIndex: UInt32, completion: @escaping (Result<RouteInfo, Error>) -> Void) {
func setMainRoute(_ route: RouteInterface, uuid: UUID, legIndex: UInt32, completion: @escaping (Result<RouteInfo, Error>) -> Void) {
routeCoordinator.beginActiveNavigation(with: route, uuid: uuid, legIndex: legIndex, completion: completion)
}

func setAlternativeRoutes(_ routes: [RouteInterface], completion: @escaping (Result<[RouteAlternative], Error>) -> Void) {
routeCoordinator.alternativeRoutesSetupHandler(routes, completion)
}

func unsetRoutes(uuid: UUID, completion: @escaping (Result<RouteInfo, Error>) -> Void) {
routeCoordinator.endActiveNavigation(with: uuid, completion: completion)
}
Expand Down
3 changes: 2 additions & 1 deletion Sources/MapboxCoreNavigation/MapboxRoutingProvider.swift
Expand Up @@ -123,7 +123,8 @@ public class MapboxRoutingProvider: RoutingProvider {
credentials: settings.directions.credentials,
tilesVersion: Navigator.tilesVersion,
historyDirectoryURL: Navigator.historyDirectoryURL,
datasetProfileIdentifier: datasetProfileIdentifier ?? Navigator.datasetProfileIdentifier)
datasetProfileIdentifier: datasetProfileIdentifier ?? Navigator.datasetProfileIdentifier,
navigatorRouterType: source.nativeSource)
return RouterFactory.build(for: source.nativeSource,
cache: factory.cacheHandle,
config: factory.configHandle,
Expand Down
41 changes: 30 additions & 11 deletions Sources/MapboxCoreNavigation/NativeHandlersFactory.swift
Expand Up @@ -21,21 +21,24 @@ class NativeHandlersFactory {
let targetVersion: String?
let configFactoryType: ConfigFactory.Type
let datasetProfileIdentifier: ProfileIdentifier
let navigatorRouterType: MapboxNavigationNative.RouterType?

init(tileStorePath: String,
credentials: Credentials,
tilesVersion: String = "",
historyDirectoryURL: URL? = nil,
targetVersion: String? = nil,
configFactoryType: ConfigFactory.Type = ConfigFactory.self,
datasetProfileIdentifier: ProfileIdentifier = ProfileIdentifier.automobile) {
datasetProfileIdentifier: ProfileIdentifier = ProfileIdentifier.automobile,
navigatorRouterType: MapboxNavigationNative.RouterType? = nil) {
self.tileStorePath = tileStorePath
self.credentials = credentials
self.tilesVersion = tilesVersion
self.historyDirectoryURL = historyDirectoryURL
self.targetVersion = targetVersion
self.configFactoryType = configFactoryType
self.datasetProfileIdentifier = datasetProfileIdentifier
self.navigatorRouterType = navigatorRouterType
}

// MARK: - Native Handlers
Expand All @@ -52,10 +55,16 @@ class NativeHandlersFactory {
onMainQueueSync { // Make sure that Navigator pick ups Main Thread RunLoop.
LogConfiguration.getInstance().setFilterLevelFor(LoggingLevel.info)

let router = navigatorRouterType.map {
MapboxNavigationNative.RouterFactory.build(for: $0,
cache: cacheHandle,
config: configHandle,
historyRecorder: historyRecorder)
}
return MapboxNavigationNative.Navigator(config: configHandle,
cache: cacheHandle,
historyRecorder: historyRecorder,
router: nil)
router: router)
}
}()

Expand Down Expand Up @@ -99,8 +108,26 @@ class NativeHandlersFactory {
endpointConfig: endpointConfig)
}()

lazy var navigatorConfig: NavigatorConfig = {
return NavigatorConfig(voiceInstructionThreshold: nil,
electronicHorizonOptions: nil,
polling: nil,
incidentsOptions: nil,
noSignalSimulationEnabled: nil,
avoidManeuverSeconds: NSNumber(value: RerouteController.DefaultManeuverAvoidanceRadius),
useSensors: false)
}()

lazy var configHandle: ConfigHandle = {
let customConfig = UserDefaults.standard.dictionary(forKey: customConfigKey) ?? [:]
let defaultConfig = [
customConfigFeaturesKey: [
"useInternalReroute": true
]
]

var customConfig = UserDefaults.standard.dictionary(forKey: customConfigKey) ?? [:]
customConfig.deepMerge(with: defaultConfig, uniquingKeysWith: { first, _ in first })

let customConfigJSON: String
if let jsonDataConfig = try? JSONSerialization.data(withJSONObject: customConfig, options: []),
let encodedConfig = String(data: jsonDataConfig, encoding: .utf8) {
Expand All @@ -110,14 +137,6 @@ class NativeHandlersFactory {
customConfigJSON = ""
}

let navigatorConfig = NavigatorConfig(voiceInstructionThreshold: nil,
electronicHorizonOptions: nil,
polling: nil,
incidentsOptions: nil,
noSignalSimulationEnabled: nil,
avoidManeuverSeconds: nil,
useSensors: nil)

return configFactoryType.build(for: settingsProfile,
config: navigatorConfig,
customConfig: customConfigJSON)
Expand Down
31 changes: 24 additions & 7 deletions Sources/MapboxCoreNavigation/NavigationSettings.swift
@@ -1,6 +1,8 @@
import Foundation
import MapboxDirections

public typealias RoutingProviderSource = MapboxRoutingProvider.Source

/**
Global settings that are used across the SDK for altering navigation behavior.

Expand All @@ -11,7 +13,7 @@ import MapboxDirections
To customize the user experience during a particular turn-by-turn navigation session, use the `NavigationOptions` class
when initializing a `NavigationViewController`.

To customize some global defaults use `NavigationSettings.initialize(directions:tileStoreConfiguration:)` method.
To customize some global defaults use `NavigationSettings.initialize(directions:tileStoreConfiguration:routingProviderSource:)` method.
*/
public class NavigationSettings {

Expand All @@ -32,11 +34,14 @@ public class NavigationSettings {

private struct State {
static var `default`: State {
.init(directions: .shared, tileStoreConfiguration: .default)
.init(directions: .shared,
tileStoreConfiguration: .default,
routingProviderSource: .hybrid)
}

var directions: Directions
var tileStoreConfiguration: TileStoreConfiguration
var routingProviderSource: RoutingProviderSource
}

/// Protects access to `_state`.
Expand All @@ -60,7 +65,7 @@ public class NavigationSettings {
/**
Default `Directions` instance. By default, `Directions.shared` is used.

You can override this property by using `NavigationSettings.initialize(directions:tileStoreConfiguration:)` method.
You can override this property by using `NavigationSettings.initialize(directions:tileStoreConfiguration:routingProviderSource:)` method.
*/
public var directions: Directions {
state.directions
Expand All @@ -69,12 +74,20 @@ public class NavigationSettings {
/**
Global `TileStoreConfiguration` instance.

You can override this property by using `NavigationSettings.initialize(directions:tileStoreConfiguration:)` method.
You can override this property by using `NavigationSettings.initialize(directions:tileStoreConfiguration:routingProviderSource:)` method.
*/
public var tileStoreConfiguration: TileStoreConfiguration {
state.tileStoreConfiguration
}

/**
Default `routingProviderSource` used for rerouting during navigation. By default, `.hybrid` is used.
You can override this property by using `NavigationSettings.initialize(directions:tileStoreConfiguration:routingProviderSource:)` method.
*/
public var routingProviderSource: RoutingProviderSource {
state.routingProviderSource
}

/**
Initializes the settings with custom instances of globally used types.

Expand All @@ -88,16 +101,20 @@ public class NavigationSettings {
fall back to the `NavigationSettings.directions` by default.
- tileStoreConfiguration: Options for configuring how map and navigation tiles are stored on the device. See
`TileStoreConfiguration` for more details.
- routingProviderSource: Configures the type of routing to be used by various SDK objects when providing route calculations.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(This change is mentioned in the changelog blurb being added in #3754.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, but without #3754 this change is unused. I've added it in this PR though to avoid conflicts between RerouteController and Alternative routes integrations.

*/
public func initialize(directions: Directions,
tileStoreConfiguration: TileStoreConfiguration) {
tileStoreConfiguration: TileStoreConfiguration,
routingProviderSource: RoutingProviderSource = .hybrid) {
lock.lock(); defer {
lock.unlock()
}
if _state != nil {
print("Warning: Using NavigationSettings.initialize(directions:tileStoreConfiguration:) after corresponding variables was initialized. Possible reasons: Initialize called more than once, or the following properties was accessed before initialization: `tileStoreConfiguration`, `directions`. This might result in an undefined behaviour. ")
print("Warning: Using NavigationSettings.initialize(directions:tileStoreConfiguration:routingProviderSource:) after corresponding variables was initialized. Possible reasons: Initialize called more than once, or the following properties was accessed before initialization: `tileStoreConfiguration`, `directions`, `routingProviderSource`. This might result in an undefined behaviour. ")
}
_state = .init(directions: directions, tileStoreConfiguration: tileStoreConfiguration)
_state = .init(directions: directions,
tileStoreConfiguration: tileStoreConfiguration,
routingProviderSource: routingProviderSource)
}

/**
Expand Down