diff --git a/Sources/MapboxCoreNavigation/LegacyRouteController.swift b/Sources/MapboxCoreNavigation/LegacyRouteController.swift index 4b7882a8a7c..81a71b92585 100644 --- a/Sources/MapboxCoreNavigation/LegacyRouteController.swift +++ b/Sources/MapboxCoreNavigation/LegacyRouteController.swift @@ -110,9 +110,27 @@ open class LegacyRouteController: NSObject, Router, InternalRouter, CLLocationMa var lastRerouteLocation: CLLocation? public var initialManeuverAvoidanceRadius: TimeInterval = RerouteController.DefaultManeuverAvoidanceRadius - - public var refreshesRoute: Bool = true - + + /** + Controls whether the route controller automatically updates ETA and traffic congestion data. + + When enabled, the route controller periodically refreshes route information at intervals defined by `RouteControllerProactiveReroutingInterval`. + By default, route refreshing is enabled only for routes using `.automobileAvoidingTraffic` or another `driving-traffic` profile. + If `reroutesProactively` is also enabled, the route controller runs the rerouting check after the route refresh completes. + + - Important: Route refresh is currently supported only for `driving-traffic` profiles. Enabling this property for other profiles (such as walking, cycling, or standard driving) may result in server errors or undefined behavior. + */ + public var refreshesRoute: Bool { + didSet { + if refreshesRoute { + let profile = indexedRouteResponse.validatedRouteOptions.profileIdentifier + if !profile.isAutomobileAvoidingTraffic { + Log.error("An incorrect route refresh was enabled for :\(profile.rawValue) navigation profile.", category: .navigation) + } + } + } + } + var lastRouteRefresh: Date? var isRefreshing = false @@ -272,7 +290,7 @@ open class LegacyRouteController: NSObject, Router, InternalRouter, CLLocationMa let options = indexedRouteResponse.validatedRouteOptions self.routeProgress = RouteProgress(route: indexedRouteResponse.currentRoute!, options: options) self.dataSource = source - self.refreshesRoute = options.profileIdentifier.isAutomobileAvoidingTraffic && options.refreshingEnabled + self.refreshesRoute = indexedRouteResponse.supportsRefreshes UIDevice.current.isBatteryMonitoringEnabled = true super.init() diff --git a/Sources/MapboxCoreNavigation/RouteController.swift b/Sources/MapboxCoreNavigation/RouteController.swift index 4558a1c16cb..c60b5603c8d 100644 --- a/Sources/MapboxCoreNavigation/RouteController.swift +++ b/Sources/MapboxCoreNavigation/RouteController.swift @@ -212,9 +212,27 @@ open class RouteController: NSObject { rerouteController.initialManeuverAvoidanceRadius = newValue } } - - public var refreshesRoute: Bool = true - + + /** + Controls whether the route controller automatically updates ETA and traffic congestion data. + + When enabled, the route controller periodically refreshes route information at intervals defined by `RouteControllerProactiveReroutingInterval`. + By default, route refreshing is enabled only for routes using `.automobileAvoidingTraffic` or another `driving-traffic` profile. + If `reroutesProactively` is also enabled, the route controller runs the rerouting check after the route refresh completes. + + - Important: Route refresh is currently supported only for `driving-traffic` profiles. Enabling this property for other profiles (such as walking, cycling, or standard driving) may result in server errors or undefined behavior. + */ + public var refreshesRoute: Bool { + didSet { + if refreshesRoute { + let profile = indexedRouteResponse.validatedRouteOptions.profileIdentifier + if !profile.isAutomobileAvoidingTraffic { + Log.error("An incorrect route refresh was enabled for :\(profile.rawValue) navigation profile.", category: .navigation) + } + } + } + } + var isRefreshing = false var lastRouteRefresh: Date? @@ -643,7 +661,7 @@ open class RouteController: NSObject { guard !hasFinishedRouting else { return } navigationSessionManager.reportStopNavigation() } - + private static func checkUniqueInstance() { Self.instanceLock.lock() let twoInstances = Self.instance != nil @@ -671,14 +689,10 @@ open class RouteController: NSObject { self.indexedRouteResponse = indexedRouteResponse self.dataSource = source - var isRouteOptions = false - if case .route = indexedRouteResponse.routeResponse.options { - isRouteOptions = true - } let options = indexedRouteResponse.validatedRouteOptions self.routeProgress = RouteProgress(route: indexedRouteResponse.currentRoute!, options: options) - self.refreshesRoute = isRouteOptions && options.profileIdentifier.isAutomobileAvoidingTraffic && options.refreshingEnabled + self.refreshesRoute = indexedRouteResponse.supportsRefreshes super.init() @@ -702,6 +716,7 @@ open class RouteController: NSObject { let options = indexedRouteResponse.validatedRouteOptions self.routeProgress = RouteProgress(route: indexedRouteResponse.currentRoute!, options: options) self.navigationSessionManager = navigationSessionManager + self.refreshesRoute = indexedRouteResponse.supportsRefreshes super.init() @@ -1209,3 +1224,13 @@ public enum AlternativeRouteError: Swift.Error { /// The navigation engine has failed to provide alternatives. case failedToUpdateAlternativeRoutes(reason: String) } + +extension IndexedRouteResponse { + var supportsRefreshes: Bool { + guard case .route = routeResponse.options else { + return false + } + return validatedRouteOptions.profileIdentifier.isAutomobileAvoidingTraffic && validatedRouteOptions.refreshingEnabled + + } +} diff --git a/Sources/MapboxCoreNavigation/Router.swift b/Sources/MapboxCoreNavigation/Router.swift index 80eb6c2c67c..1c8769e4cea 100644 --- a/Sources/MapboxCoreNavigation/Router.swift +++ b/Sources/MapboxCoreNavigation/Router.swift @@ -251,9 +251,13 @@ public protocol Router: CLLocationManagerDelegate { var reroutesProactively: Bool { get set } /** - If true, the `RouteController` attempts to update ETA and route congestion on an interval defined by `RouteControllerProactiveReroutingInterval`. - - Refreshing will be used only if route's mode of transportation profile is set to `.automobileAvoidingTraffic`. If `reroutesProactively` is enabled too, rerouting will be checked after route is refreshed. + Controls whether the route controller automatically updates ETA and traffic congestion data. + + When enabled, the route controller periodically refreshes route information at intervals defined by `RouteControllerProactiveReroutingInterval`. + By default, route refreshing is enabled only for routes using `.automobileAvoidingTraffic` or another `driving-traffic` profile. + If `reroutesProactively` is also enabled, the route controller runs the rerouting check after the route refresh completes. + + - Important: Route refresh is currently supported only for `driving-traffic` profiles. Enabling this property for other profiles (such as walking, cycling, or standard driving) may result in server errors or undefined behavior. */ var refreshesRoute: Bool { get set } diff --git a/Tests/MapboxCoreNavigationTests/RouteControllerTests.swift b/Tests/MapboxCoreNavigationTests/RouteControllerTests.swift index 50a60b5707c..6b726b4ee70 100644 --- a/Tests/MapboxCoreNavigationTests/RouteControllerTests.swift +++ b/Tests/MapboxCoreNavigationTests/RouteControllerTests.swift @@ -122,6 +122,30 @@ class RouteControllerTests: TestCase { customRoutingProvider: routingProvider, dataSource: dataSource) XCTAssertFalse(controller2.refreshesRoute, "Should not refresh for automobile") + + indexedRouteResponse.validatedRouteOptions.profileIdentifier = .init(rawValue: "custom/driving-traffic") + let controller3 = RouteController(indexedRouteResponse: indexedRouteResponse, + customRoutingProvider: routingProvider, + dataSource: dataSource) + XCTAssertTrue(controller3.refreshesRoute, "Should refresh for custom driving traffic") + + let mapMatchinOptions = NavigationMatchOptions( + coordinates: [ + .init(latitude: 59.3379254707993, longitude: 18.0768391763866), + .init(latitude: 59.3376613543215, longitude: 18.0758977499228), + .init(latitude: 59.3371292341531, longitude: 18.0754779388695), + ], + profileIdentifier: .automobileAvoidingTraffic) + let response = Fixture.mapMatchingResponse(from: "route-doubling-back", options: mapMatchinOptions) + let routeResponse = try! RouteResponse( + matching: response, + options: mapMatchinOptions, + credentials: .mocked) + let indexedResponse = IndexedRouteResponse(routeResponse: routeResponse, routeIndex: 0) + let controller4 = RouteController(indexedRouteResponse: indexedResponse, + customRoutingProvider: routingProvider, + dataSource: dataSource) + XCTAssertFalse(controller4.refreshesRoute, "Should not refresh for map matching response") } func testReturnIsFirstLocation() {