diff --git a/CHANGELOG.md b/CHANGELOG.md index ccfed968461..2246669c5d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,30 @@ ## v2.4.0 +### Packaging + +* MapboxNavigation now requires [MapboxMaps v10.4.0-rc.1](https://github.com/mapbox/mapbox-maps-ios/releases/tag/v10.4.0-rc.1). ([#3765](https://github.com/mapbox/mapbox-navigation-ios/pull/3765)) +* MapboxCoreNavigation now requires [MapboxNavigationNative v92._x_](https://github.com/mapbox/mapbox-navigation-native-ios/releases/tag/92.0.0). ([#3765](https://github.com/mapbox/mapbox-navigation-ios/pull/3765)) +* MapboxCoreNavigation now requires [MapboxDirections v2.4.0-beta.1](https://github.com/mapbox/mapbox-directions-swift/releases/tag/v2.4.0-beta.1). ([#3765](https://github.com/mapbox/mapbox-navigation-ios/pull/3765)) + +### Location tracking + +* Added the `RoadObject.Kind.railroadCrossing` enumeration case to represent a railroad crossing along the route. ([#3765](https://github.com/mapbox/mapbox-navigation-ios/pull/3765)) +* Throttled requests to the Mapbox Directions API while the user moves around in a parking lot that has not been mapped in detail. ([#3765](https://github.com/mapbox/mapbox-navigation-ios/pull/3765)) +* If the user backtracks from the beginning of one leg of the route to the end of the previous leg, the user is no longer considered to be off-route; instead, `RouteProgress.currentLegProgress` decrements and other properties follow suit. ([#3765](https://github.com/mapbox/mapbox-navigation-ios/pull/3765)) +* Fixed a crash starting turn-by-turn navigation on some routes that included bridges and restricted-access roads. ([#3765](https://github.com/mapbox/mapbox-navigation-ios/pull/3765)) + +### Offline routing + +* In [bilingual and multilingual areas](https://wiki.openstreetmap.org/wiki/Multilingual_names), spoken and visual instructions include street names and destinations in the user’s preferred language when that language is signposted. ([#3765](https://github.com/mapbox/mapbox-navigation-ios/pull/3765)) +* Fixed `Intersection.outletIndex` values that were off by one. ([#3765](https://github.com/mapbox/mapbox-navigation-ios/pull/3765)) +* High-occupancy vehicle roads in OpenStreetMap that lack a [`hov:minimum`](https://wiki.openstreetmap.org/wiki/Key:hov:minimum) tag are now assumed to be HOV 2+ roads, requiring one or more passengers. ([#3765](https://github.com/mapbox/mapbox-navigation-ios/pull/3765)) +* Decreased memory usage when starting turn-by-turn navigation along a long-distance route. ([#3765](https://github.com/mapbox/mapbox-navigation-ios/pull/3765)) +* Fixed an issue where street names in spoken instructions could be go unpronounced if tagged with a [pronunciation](https://wiki.openstreetmap.org/wiki/Key:name:pronunciation). ([#3765](https://github.com/mapbox/mapbox-navigation-ios/pull/3765)) +* Added support for [the workaround](https://github.com/mapbox/mapbox-directions-swift/issues/662) for requesting a route that avoids an arbitrary set of coordinates. ([#3765](https://github.com/mapbox/mapbox-navigation-ios/pull/3765)) + +* MapboxNavigationNative now sends messages to the Unified Logging subsystem `com.mapbox` for easier filtering. ([#3765](https://github.com/mapbox/mapbox-navigation-ios/pull/3765)) + ### User interface * `FeedbackViewController` now supports changing styles based on `StyleManager`. ([#3764](https://github.com/mapbox/mapbox-navigation-ios/pull/3764)) diff --git a/Cartfile b/Cartfile index fdc85e85e6e..a00a0908203 100644 --- a/Cartfile +++ b/Cartfile @@ -1,4 +1,4 @@ -binary "https://api.mapbox.com/downloads/v2/carthage/mapbox-common/MapboxCommon.json" ~> 21.1.0 -binary "https://api.mapbox.com/downloads/v2/carthage/mobile-navigation-native/MapboxNavigationNative.xcframework.json" ~> 88.0 -github "mapbox/mapbox-directions-swift" ~> 2.3 +binary "https://api.mapbox.com/downloads/v2/carthage/mapbox-common/MapboxCommon.json" == 21.2.0-rc.1 +binary "https://api.mapbox.com/downloads/v2/carthage/mobile-navigation-native/MapboxNavigationNative.xcframework.json" ~> 92.0 +github "mapbox/mapbox-directions-swift" == 2.4.0-beta.1 github "mapbox/mapbox-events-ios" ~> 1.0.0 diff --git a/Cartfile.resolved b/Cartfile.resolved index 2e78b7a72e8..4f56917da26 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -1,9 +1,9 @@ -binary "https://api.mapbox.com/downloads/v2/carthage/mapbox-common/MapboxCommon.json" "21.1.1" -binary "https://api.mapbox.com/downloads/v2/carthage/mobile-navigation-native/MapboxNavigationNative.xcframework.json" "88.0.0" +binary "https://api.mapbox.com/downloads/v2/carthage/mapbox-common/MapboxCommon.json" "21.2.0-rc.1" +binary "https://api.mapbox.com/downloads/v2/carthage/mobile-navigation-native/MapboxNavigationNative.xcframework.json" "92.0.0" github "Quick/Nimble" "v9.2.1" github "Quick/Quick" "v3.1.2" -github "mapbox/mapbox-directions-swift" "v2.3.0" +github "mapbox/mapbox-directions-swift" "v2.4.0-beta.1" github "mapbox/mapbox-events-ios" "v1.0.7" github "mapbox/turf-swift" "v2.3.0" github "pointfreeco/swift-snapshot-testing" "1.9.0" -github "raphaelmor/Polyline" "v5.0.2" +github "raphaelmor/Polyline" "v5.0.3" diff --git a/MapboxCoreNavigation.podspec b/MapboxCoreNavigation.podspec index db5960649dc..cf5bd030663 100644 --- a/MapboxCoreNavigation.podspec +++ b/MapboxCoreNavigation.podspec @@ -40,8 +40,8 @@ Pod::Spec.new do |s| s.requires_arc = true s.module_name = "MapboxCoreNavigation" - s.dependency "MapboxNavigationNative", "~> 88.0" - s.dependency "MapboxDirections", "~> 2.3" + s.dependency "MapboxNavigationNative", "~> 92.0" + s.dependency "MapboxDirections-pre", "2.4.0-beta.1" s.dependency "MapboxMobileEvents", "~> 1.0" s.swift_version = "5.0" diff --git a/MapboxNavigation-SPM.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/MapboxNavigation-SPM.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 445cbc2f536..a7ae8962b8a 100644 --- a/MapboxNavigation-SPM.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/MapboxNavigation-SPM.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -24,8 +24,8 @@ "repositoryURL": "https://github.com/mapbox/mapbox-common-ios.git", "state": { "branch": null, - "revision": "d0adb996dfad19ed1edc611ee640e6c7506951a0", - "version": "21.1.0" + "revision": "2a1c1a6bd3e1d0c66d384c4dfbd0a03d60f3f877", + "version": "21.2.0-rc.1" } }, { @@ -33,8 +33,8 @@ "repositoryURL": "https://github.com/mapbox/mapbox-core-maps-ios.git", "state": { "branch": null, - "revision": "f346a9880cf92ea5e8bc15a4034bb18d3d4c079c", - "version": "10.3.2" + "revision": "c6aac72620f97c28a4475b62b050c69c3a589da1", + "version": "10.4.0-rc.1" } }, { @@ -42,8 +42,8 @@ "repositoryURL": "https://github.com/mapbox/mapbox-directions-swift.git", "state": { "branch": null, - "revision": "2b14b8c276523a01dc730897593990ef83c71320", - "version": "2.3.0" + "revision": "f40fcea9d26fa2cb59570f21546abeffb643a02b", + "version": "2.4.0-beta.1" } }, { @@ -60,8 +60,8 @@ "repositoryURL": "https://github.com/mapbox/mapbox-maps-ios.git", "state": { "branch": null, - "revision": "47f9d2da9acea04e79244949328c3a9d10f39945", - "version": "10.3.0" + "revision": "8cf3f937cfd0df60fe741ce83a089296c85b2546", + "version": "10.4.0-rc.1" } }, { @@ -69,8 +69,8 @@ "repositoryURL": "https://github.com/mapbox/mapbox-navigation-native-ios.git", "state": { "branch": null, - "revision": "921b60a08375832088d69d023c1a7e1b4cac2231", - "version": "88.0.0" + "revision": "5aae54182a7d4d829e4f458483df44cf13dcde03", + "version": "92.0.0" } }, { @@ -114,8 +114,8 @@ "repositoryURL": "https://github.com/raphaelmor/Polyline.git", "state": { "branch": null, - "revision": "36f7b1222aaf8fa741d0d179c12e186998d97f42", - "version": "5.0.2" + "revision": "554a15b15ff33cf6757f4cb693c5c285fe58694e", + "version": "5.0.3" } }, { @@ -141,8 +141,8 @@ "repositoryURL": "https://github.com/apple/swift-argument-parser", "state": { "branch": null, - "revision": "e394bf350e38cb100b6bc4172834770ede1b7232", - "version": "1.0.3" + "revision": "82905286cc3f0fa8adc4674bf49437cab65a8373", + "version": "1.1.1" } }, { @@ -159,8 +159,8 @@ "repositoryURL": "https://github.com/mapbox/turf-swift.git", "state": { "branch": null, - "revision": "436682278bd65a1dbfbad53902af6f4cc0af1a33", - "version": "2.2.0" + "revision": "a7db78a2281ffe79966443ebe5687766d6c024c7", + "version": "2.3.0" } } ] diff --git a/MapboxNavigation.podspec b/MapboxNavigation.podspec index 4bf5cb35da3..e0158a2f01a 100644 --- a/MapboxNavigation.podspec +++ b/MapboxNavigation.podspec @@ -44,7 +44,7 @@ Pod::Spec.new do |s| s.module_name = "MapboxNavigation" s.dependency "MapboxCoreNavigation", "#{s.version.to_s}" - s.dependency "MapboxMaps", "~> 10.3" + s.dependency "MapboxMaps", "10.4.0-rc.1" s.dependency "Solar-dev", "~> 3.0" s.dependency "MapboxSpeech", "~> 2.0" s.dependency "MapboxMobileEvents", "~> 1.0" diff --git a/Package.resolved b/Package.resolved index 48179a27b77..cf9c37db645 100644 --- a/Package.resolved +++ b/Package.resolved @@ -24,8 +24,8 @@ "repositoryURL": "https://github.com/mapbox/mapbox-common-ios.git", "state": { "branch": null, - "revision": "d0adb996dfad19ed1edc611ee640e6c7506951a0", - "version": "21.1.0" + "revision": "2a1c1a6bd3e1d0c66d384c4dfbd0a03d60f3f877", + "version": "21.2.0-rc.1" } }, { @@ -33,8 +33,8 @@ "repositoryURL": "https://github.com/mapbox/mapbox-core-maps-ios.git", "state": { "branch": null, - "revision": "f346a9880cf92ea5e8bc15a4034bb18d3d4c079c", - "version": "10.3.2" + "revision": "c6aac72620f97c28a4475b62b050c69c3a589da1", + "version": "10.4.0-rc.1" } }, { @@ -42,8 +42,8 @@ "repositoryURL": "https://github.com/mapbox/mapbox-directions-swift.git", "state": { "branch": null, - "revision": "2b14b8c276523a01dc730897593990ef83c71320", - "version": "2.3.0" + "revision": "f40fcea9d26fa2cb59570f21546abeffb643a02b", + "version": "2.4.0-beta.1" } }, { @@ -60,8 +60,8 @@ "repositoryURL": "https://github.com/mapbox/mapbox-maps-ios.git", "state": { "branch": null, - "revision": "47f9d2da9acea04e79244949328c3a9d10f39945", - "version": "10.3.0" + "revision": "8cf3f937cfd0df60fe741ce83a089296c85b2546", + "version": "10.4.0-rc.1" } }, { @@ -69,8 +69,8 @@ "repositoryURL": "https://github.com/mapbox/mapbox-navigation-native-ios.git", "state": { "branch": null, - "revision": "921b60a08375832088d69d023c1a7e1b4cac2231", - "version": "88.0.0" + "revision": "5aae54182a7d4d829e4f458483df44cf13dcde03", + "version": "92.0.0" } }, { @@ -105,8 +105,8 @@ "repositoryURL": "https://github.com/raphaelmor/Polyline.git", "state": { "branch": null, - "revision": "36f7b1222aaf8fa741d0d179c12e186998d97f42", - "version": "5.0.2" + "revision": "554a15b15ff33cf6757f4cb693c5c285fe58694e", + "version": "5.0.3" } }, { @@ -132,8 +132,8 @@ "repositoryURL": "https://github.com/apple/swift-argument-parser", "state": { "branch": null, - "revision": "e394bf350e38cb100b6bc4172834770ede1b7232", - "version": "1.0.3" + "revision": "82905286cc3f0fa8adc4674bf49437cab65a8373", + "version": "1.1.1" } }, { @@ -150,8 +150,8 @@ "repositoryURL": "https://github.com/mapbox/turf-swift.git", "state": { "branch": null, - "revision": "436682278bd65a1dbfbad53902af6f4cc0af1a33", - "version": "2.2.0" + "revision": "a7db78a2281ffe79966443ebe5687766d6c024c7", + "version": "2.3.0" } } ] diff --git a/Package.swift b/Package.swift index 892a939f052..15ba3c61b0f 100644 --- a/Package.swift +++ b/Package.swift @@ -22,10 +22,10 @@ let package = Package( ) ], dependencies: [ - .package(name: "MapboxDirections", url: "https://github.com/mapbox/mapbox-directions-swift.git", from: "2.3.0"), + .package(name: "MapboxDirections", url: "https://github.com/mapbox/mapbox-directions-swift.git", .exact("2.4.0-beta.1")), .package(name: "MapboxMobileEvents", url: "https://github.com/mapbox/mapbox-events-ios.git", from: "1.0.0"), - .package(name: "MapboxNavigationNative", url: "https://github.com/mapbox/mapbox-navigation-native-ios.git", from: "88.0.0"), - .package(name: "MapboxMaps", url: "https://github.com/mapbox/mapbox-maps-ios.git", from: "10.3.0"), + .package(name: "MapboxNavigationNative", url: "https://github.com/mapbox/mapbox-navigation-native-ios.git", from: "92.0.0"), + .package(name: "MapboxMaps", url: "https://github.com/mapbox/mapbox-maps-ios.git", .exact("10.4.0-rc.1")), .package(name: "Solar", url: "https://github.com/ceeK/Solar.git", from: "3.0.0"), .package(name: "MapboxSpeech", url: "https://github.com/mapbox/mapbox-speech-swift.git", from: "2.0.0"), .package(name: "Quick", url: "https://github.com/Quick/Quick.git", from: "3.1.2"), diff --git a/Sources/MapboxCoreNavigation/CoreNavigationNavigator.swift b/Sources/MapboxCoreNavigation/CoreNavigationNavigator.swift index 162cd7b4e53..8cb4604f4fe 100644 --- a/Sources/MapboxCoreNavigation/CoreNavigationNavigator.swift +++ b/Sources/MapboxCoreNavigation/CoreNavigationNavigator.swift @@ -33,8 +33,8 @@ class Navigator { } private lazy var routeCoordinator: RoutesCoordinator = { - .init(setRoutesHandler: { [weak self] routes, completion in - self?.navigator.setRoutesFor(routes) { result in + .init(setRoutesHandler: { [weak self] route, legIndex, completion in + self?.navigator.setPrimaryRouteForRoute(route, legIndex: legIndex) { [weak self] result in if result.isValue() { let routeInfo = result.value as! RouteInfo os_log("Navigator has been updated", @@ -209,13 +209,12 @@ class Navigator { // MARK: - Navigator Updates - func setRoutes(_ routes: Routes?, uuid: UUID, completion: @escaping (Result) -> Void) { - if let routes = routes { - routeCoordinator.beginActiveNavigation(with: routes, uuid: uuid, completion: completion) - } - else { - routeCoordinator.endActiveNavigation(with: uuid, completion: completion) - } + func setRoutes(_ route: RouteInterface, uuid: UUID, legIndex: UInt32, completion: @escaping (Result) -> Void) { + routeCoordinator.beginActiveNavigation(with: route, uuid: uuid, legIndex: legIndex, completion: completion) + } + + func unsetRoutes(uuid: UUID, completion: @escaping (Result) -> Void) { + routeCoordinator.endActiveNavigation(with: uuid, completion: completion) } func updateLocation(_ location: CLLocation, completion: @escaping (Bool) -> Void) { diff --git a/Sources/MapboxCoreNavigation/EHorizon/RoadObjectKind.swift b/Sources/MapboxCoreNavigation/EHorizon/RoadObjectKind.swift index c6e76a2b252..f6636c26a98 100644 --- a/Sources/MapboxCoreNavigation/EHorizon/RoadObjectKind.swift +++ b/Sources/MapboxCoreNavigation/EHorizon/RoadObjectKind.swift @@ -55,6 +55,11 @@ extension RoadObject { */ case bridge + /** + An alert about an railroad crossing at grade, also known as a level crossing. + */ + case railroadCrossing + /** Reserved for future use. */ @@ -76,6 +81,8 @@ extension RoadObject { self = .restrictedArea case .bridge: self = .bridge + case .railwayCrossing: + self = .railroadCrossing case .custom: self = .userDefined @unknown default: @@ -99,6 +106,8 @@ extension RoadObject { self = .restrictedArea case .bridge: self = .bridge + case .railwayCrossing: + self = .railroadCrossing case .custom: self = .userDefined @unknown default: diff --git a/Sources/MapboxCoreNavigation/MapboxRoutingProvider.swift b/Sources/MapboxCoreNavigation/MapboxRoutingProvider.swift index 986c4c18c7e..98158d57620 100644 --- a/Sources/MapboxCoreNavigation/MapboxRoutingProvider.swift +++ b/Sources/MapboxCoreNavigation/MapboxRoutingProvider.swift @@ -295,33 +295,45 @@ public class MapboxRoutingProvider: RoutingProvider { return nil } - let encoder = JSONEncoder() - encoder.userInfo[.options] = routeOptions - let routeIndex = UInt32(indexedRouteResponse.routeIndex) - guard let routeData = try? encoder.encode(indexedRouteResponse.routeResponse), - let routeJSONString = String(data: routeData, encoding: .utf8) else { - preconditionFailure("Could not serialize route data for refreshing.") - } - var requestId: RequestId! let refreshOptions = RouteRefreshOptions(requestId: responseIdentifier, routeIndex: routeIndex, legIndex: startLegIndex, routingProfile: routeOptions.profileIdentifier.nativeProfile) - requestId = router.getRouteRefresh(for: refreshOptions, - route: routeJSONString) { [weak self] result, _ in + requestId = router.getRouteRefresh(for: refreshOptions, callback: { [weak self] result, _ in guard let self = self else { return } self.parseResponse(requestId: requestId, - userInfo: [.options: routeOptions, + userInfo: [.responseIdentifier: responseIdentifier, + .routeIndex: indexedRouteResponse.routeIndex, + .startLegIndex: Int(startLegIndex), .credentials: self.settings.directions.credentials], - result: result) { (response: Result) in - completionHandler(session, response) + result: result) { (response: Result) in + switch response { + case .failure(let error): + DispatchQueue.main.async { + completionHandler(session, .failure(error)) + } + case .success(let routeRefreshResponse): + DispatchQueue.global().async { + do { + let routeResponse = try indexedRouteResponse.routeResponse.copy(with: routeOptions) + routeResponse.routes?[indexedRouteResponse.routeIndex].refreshLegAttributes(from: routeRefreshResponse.route) + DispatchQueue.main.async { + completionHandler(session, .success(routeResponse)) + } + } catch { + DispatchQueue.main.async { + completionHandler(session, .failure(.unknown(response: nil, underlying: error, code: nil, message: nil))) + } + } + } + } } - } + }) let request = Request(requestIdentifier: requestId, routingProvider: self) requestsLock { diff --git a/Sources/MapboxCoreNavigation/NativeHandlersFactory.swift b/Sources/MapboxCoreNavigation/NativeHandlersFactory.swift index ab2860e1c66..01b77df3f4f 100644 --- a/Sources/MapboxCoreNavigation/NativeHandlersFactory.swift +++ b/Sources/MapboxCoreNavigation/NativeHandlersFactory.swift @@ -2,6 +2,7 @@ import MapboxCommon import MapboxNavigationNative import MapboxDirections import Foundation +@_implementationOnly import MapboxCommon_Private public let customConfigKey = "com.mapbox.navigation.custom-config" public let customConfigFeaturesKey = "features" @@ -49,10 +50,12 @@ class NativeHandlersFactory { lazy var navigator: MapboxNavigationNative.Navigator = { onMainQueueSync { // Make sure that Navigator pick ups Main Thread RunLoop. - MapboxNavigationNative.Navigator(config: configHandle, - cache: cacheHandle, - historyRecorder: historyRecorder, - router: nil) + LogConfiguration.getInstance().setFilterLevelFor(LoggingLevel.info) + + return MapboxNavigationNative.Navigator(config: configHandle, + cache: cacheHandle, + historyRecorder: historyRecorder, + router: nil) } }() diff --git a/Sources/MapboxCoreNavigation/NavigationRouteOptions.swift b/Sources/MapboxCoreNavigation/NavigationRouteOptions.swift index de47c74dec0..4fc6387a632 100644 --- a/Sources/MapboxCoreNavigation/NavigationRouteOptions.swift +++ b/Sources/MapboxCoreNavigation/NavigationRouteOptions.swift @@ -17,11 +17,13 @@ open class NavigationRouteOptions: RouteOptions, OptimizedForNavigation { - seealso: `RouteOptions` */ - public required init(waypoints: [Waypoint], profileIdentifier: ProfileIdentifier? = .automobileAvoidingTraffic) { + public required init(waypoints: [Waypoint], profileIdentifier: ProfileIdentifier? = .automobileAvoidingTraffic, queryItems: [URLQueryItem]? = nil) { super.init(waypoints: waypoints.map { $0.coordinateAccuracy = -1 return $0 - }, profileIdentifier: profileIdentifier) + }, + profileIdentifier: profileIdentifier, + queryItems: queryItems) includesAlternativeRoutes = true attributeOptions = [.expectedTravelTime, .maximumSpeedLimit] if profileIdentifier == .cycling { @@ -51,8 +53,8 @@ open class NavigationRouteOptions: RouteOptions, OptimizedForNavigation { - seealso: `RouteOptions` */ - public convenience init(locations: [CLLocation], profileIdentifier: ProfileIdentifier? = .automobileAvoidingTraffic) { - self.init(waypoints: locations.map { Waypoint(location: $0) }, profileIdentifier: profileIdentifier) + public convenience init(locations: [CLLocation], profileIdentifier: ProfileIdentifier? = .automobileAvoidingTraffic, queryItems: [URLQueryItem]? = nil) { + self.init(waypoints: locations.map { Waypoint(location: $0) }, profileIdentifier: profileIdentifier, queryItems: queryItems) } /** @@ -60,8 +62,8 @@ open class NavigationRouteOptions: RouteOptions, OptimizedForNavigation { - seealso: `RouteOptions` */ - public convenience init(coordinates: [CLLocationCoordinate2D], profileIdentifier: ProfileIdentifier? = .automobileAvoidingTraffic) { - self.init(waypoints: coordinates.map { Waypoint(coordinate: $0) }, profileIdentifier: profileIdentifier) + public convenience init(coordinates: [CLLocationCoordinate2D], profileIdentifier: ProfileIdentifier? = .automobileAvoidingTraffic, queryItems: [URLQueryItem]? = nil) { + self.init(waypoints: coordinates.map { Waypoint(coordinate: $0) }, profileIdentifier: profileIdentifier, queryItems: queryItems) } required public init(from decoder: Decoder) throws { @@ -82,11 +84,13 @@ open class NavigationMatchOptions: MatchOptions, OptimizedForNavigation { - seealso: `RouteOptions` */ - public required init(waypoints: [Waypoint], profileIdentifier: ProfileIdentifier? = .automobileAvoidingTraffic) { + public required init(waypoints: [Waypoint], profileIdentifier: ProfileIdentifier? = .automobileAvoidingTraffic, queryItems: [URLQueryItem]? = nil) { super.init(waypoints: waypoints.map { $0.coordinateAccuracy = -1 return $0 - }, profileIdentifier: profileIdentifier) + }, + profileIdentifier: profileIdentifier, + queryItems: queryItems) attributeOptions = [.numericCongestionLevel, .expectedTravelTime] if profileIdentifier == .automobile || profileIdentifier == .automobileAvoidingTraffic { attributeOptions.insert(.maximumSpeedLimit) @@ -101,8 +105,8 @@ open class NavigationMatchOptions: MatchOptions, OptimizedForNavigation { - seealso: `MatchOptions` */ - public convenience init(locations: [CLLocation], profileIdentifier: ProfileIdentifier? = .automobileAvoidingTraffic) { - self.init(waypoints: locations.map { Waypoint(location: $0) }, profileIdentifier: profileIdentifier) + public convenience init(locations: [CLLocation], profileIdentifier: ProfileIdentifier? = .automobileAvoidingTraffic, queryItems: [URLQueryItem]? = nil) { + self.init(waypoints: locations.map { Waypoint(location: $0) }, profileIdentifier: profileIdentifier, queryItems: queryItems) } /** @@ -110,8 +114,8 @@ open class NavigationMatchOptions: MatchOptions, OptimizedForNavigation { - seealso: `MatchOptions` */ - public convenience init(coordinates: [CLLocationCoordinate2D], profileIdentifier: ProfileIdentifier? = .automobileAvoidingTraffic) { - self.init(waypoints: coordinates.map { Waypoint(coordinate: $0) }, profileIdentifier: profileIdentifier) + public convenience init(coordinates: [CLLocationCoordinate2D], profileIdentifier: ProfileIdentifier? = .automobileAvoidingTraffic, queryItems: [URLQueryItem]? = nil) { + self.init(waypoints: coordinates.map { Waypoint(coordinate: $0) }, profileIdentifier: profileIdentifier, queryItems: queryItems) } required public init(from decoder: Decoder) throws { diff --git a/Sources/MapboxCoreNavigation/RouteController.swift b/Sources/MapboxCoreNavigation/RouteController.swift index 35d30f8f84e..5041ed44697 100644 --- a/Sources/MapboxCoreNavigation/RouteController.swift +++ b/Sources/MapboxCoreNavigation/RouteController.swift @@ -254,15 +254,19 @@ open class RouteController: NSObject { completion?(.failure(RouteControllerError.failedToSerializeRoute)) return } - + let routeRequest = Directions().url(forCalculating: progress.routeOptions).absoluteString - let routes = Routes(routesResponse: routeJSONString, - routeIndex: 0, - legIndex: UInt32(progress.legIndex), - routesRequest: routeRequest) - - sharedNavigator.setRoutes(routes, uuid: sessionUUID) { result in - completion?(result) + + let parsedRoutes = RouteParser.parseDirectionsResponse(forResponse: routeJSONString, + request: routeRequest) + if parsedRoutes.isValue(), + let route = (parsedRoutes.value as? [RouteInterface])?.first { + self.sharedNavigator.setRoutes(route, uuid: sessionUUID, legIndex: UInt32(progress.legIndex)) { result in + completion?(result) + } + } else if parsedRoutes.isError() { + let reason = (parsedRoutes.error as? String) ?? "" + completion?(.failure(NavigatorError.failedToUpdateRoutes(reason: reason))) } } @@ -270,7 +274,7 @@ open class RouteController: NSObject { private func updateRouteLeg(to value: Int, completionHandler: AdvanceLegCompletionHandler? = nil) { let legIndex = UInt32(value) - navigator.changeRouteLeg(forRoute: 0, leg: legIndex) { [weak self] success in + navigator.changeLeg(forLeg: legIndex) { [weak self] success in guard let self = self else { completionHandler?(.failure(RouteControllerError.internalError)) return @@ -284,7 +288,7 @@ open class RouteController: NSObject { /** NOTE: `navigator.changeRouteLeg(forRoute:leg:)` will return true if the leg actually changed. */ - BillingHandler.shared.beginNewBillingSessionIfRunning(with: self.sessionUUID) + BillingHandler.shared.beginNewBillingSessionIfRunning(with: self.sessionUUID) } else { result = .failure(RouteControllerError.failedToChangeRouteLeg) } @@ -741,7 +745,7 @@ extension RouteController: Router { } private func removeRoutes(completion: ((Error?) -> Void)?) { - sharedNavigator.setRoutes(nil, uuid: sessionUUID) { result in + sharedNavigator.unsetRoutes(uuid: sessionUUID) { result in switch result { case .success: completion?(nil) diff --git a/Sources/MapboxCoreNavigation/RoutesCoordinator.swift b/Sources/MapboxCoreNavigation/RoutesCoordinator.swift index f57bbd35a88..915a95de9bc 100644 --- a/Sources/MapboxCoreNavigation/RoutesCoordinator.swift +++ b/Sources/MapboxCoreNavigation/RoutesCoordinator.swift @@ -11,7 +11,7 @@ final class RoutesCoordinator { case activeNavigation(UUID) } - typealias SetRoutesHandler = (Routes?, _ completion: @escaping (Result) -> Void) -> Void + typealias SetRoutesHandler = (RouteInterface?, _ legIndex: UInt32, _ completion: @escaping (Result) -> Void) -> Void private struct ActiveNavigationSession { let uuid: UUID @@ -33,8 +33,10 @@ final class RoutesCoordinator { /// - Parameters: /// - uuid: The UUID of the current active guidances session. All reroutes should have the same uuid. - func beginActiveNavigation(with routes: Routes, + /// - legIndex: The index of the leg along which to begin navigating. + func beginActiveNavigation(with route: RouteInterface, uuid: UUID, + legIndex: UInt32, completion: @escaping (Result) -> Void) { lock.lock() if case .activeNavigation(let currentUUID) = state, currentUUID != uuid { @@ -44,7 +46,7 @@ final class RoutesCoordinator { state = .activeNavigation(uuid) lock.unlock() - setRoutes(routes, completion) + setRoutes(route, legIndex, completion) } /// - Parameters: @@ -58,7 +60,8 @@ final class RoutesCoordinator { } state = .passiveNavigation lock.unlock() - setRoutes(nil, completion) + // TODO: Is it safe to set the leg index to 0 when unsetting a route? + setRoutes(nil, 0, completion) } } diff --git a/Tests/CocoaPodsTest/PodInstall/PodInstall.xcodeproj/project.pbxproj b/Tests/CocoaPodsTest/PodInstall/PodInstall.xcodeproj/project.pbxproj index d20e87434bb..b62f306dc86 100644 --- a/Tests/CocoaPodsTest/PodInstall/PodInstall.xcodeproj/project.pbxproj +++ b/Tests/CocoaPodsTest/PodInstall/PodInstall.xcodeproj/project.pbxproj @@ -232,7 +232,7 @@ inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-PodInstall/Pods-PodInstall-frameworks.sh", "${BUILT_PRODUCTS_DIR}/MapboxCoreNavigation/MapboxCoreNavigation.framework", - "${BUILT_PRODUCTS_DIR}/MapboxDirections/MapboxDirections.framework", + "${BUILT_PRODUCTS_DIR}/MapboxDirections-pre/MapboxDirections.framework", "${BUILT_PRODUCTS_DIR}/MapboxMaps/MapboxMaps.framework", "${BUILT_PRODUCTS_DIR}/MapboxNavigation/MapboxNavigation.framework", "${BUILT_PRODUCTS_DIR}/MapboxSpeech/MapboxSpeech.framework", diff --git a/Tests/CocoaPodsTest/PodInstall/Podfile.lock b/Tests/CocoaPodsTest/PodInstall/Podfile.lock index 8a5300614f8..17a7d3a114c 100644 --- a/Tests/CocoaPodsTest/PodInstall/Podfile.lock +++ b/Tests/CocoaPodsTest/PodInstall/Podfile.lock @@ -1,28 +1,28 @@ PODS: - - MapboxCommon (21.1.0) - - MapboxCoreMaps (10.3.2): - - MapboxCommon (~> 21.1) + - MapboxCommon (21.2.0-rc.1) + - MapboxCoreMaps (10.4.0-rc.1): + - MapboxCommon (~> 21.2.0-rc) - MapboxCoreNavigation (2.4.0-beta.1): - - MapboxDirections (~> 2.3) + - MapboxDirections-pre (= 2.4.0-beta.1) - MapboxMobileEvents (~> 1.0) - - MapboxNavigationNative (~> 88.0) - - MapboxDirections (2.3.0): + - MapboxNavigationNative (~> 92.0) + - MapboxDirections-pre (2.4.0-beta.1): - Polyline (~> 5.0) - Turf (~> 2.0) - - MapboxMaps (10.3.0): - - MapboxCommon (= 21.1.0) - - MapboxCoreMaps (= 10.3.2) + - MapboxMaps (10.4.0-rc.1): + - MapboxCommon (= 21.2.0-rc.1) + - MapboxCoreMaps (= 10.4.0-rc.1) - MapboxMobileEvents (= 1.0.7) - Turf (~> 2.0) - MapboxMobileEvents (1.0.7) - MapboxNavigation (2.4.0-beta.1): - MapboxCoreNavigation (= 2.4.0-beta.1) - - MapboxMaps (~> 10.3) + - MapboxMaps (= 10.4.0-rc.1) - MapboxMobileEvents (~> 1.0) - MapboxSpeech (~> 2.0) - Solar-dev (~> 3.0) - - MapboxNavigationNative (88.0.0): - - MapboxCommon (~> 21.1) + - MapboxNavigationNative (92.0.0): + - MapboxCommon (~> 21.2.0-rc.1) - MapboxSpeech (2.0.0) - Polyline (5.0.2) - Solar-dev (3.0.1) @@ -36,7 +36,7 @@ SPEC REPOS: trunk: - MapboxCommon - MapboxCoreMaps - - MapboxDirections + - MapboxDirections-pre - MapboxMaps - MapboxMobileEvents - MapboxNavigationNative @@ -52,14 +52,14 @@ EXTERNAL SOURCES: :path: "../../../" SPEC CHECKSUMS: - MapboxCommon: 019dac381fcb3aab115b5d488593261c810253ad - MapboxCoreMaps: 418b6e02464c00d7227e778164f6ca67755ae6a1 - MapboxCoreNavigation: 6aa0b6cae5d864a1fd68caf3fdc5fc35988fa143 - MapboxDirections: 305d743dbee1e0b49d6100a2b946b47cec18b42c - MapboxMaps: fc490ab5b8df6f74a82d35c56bc2ea4d576ad4ee + MapboxCommon: 84983b2049b48100f62e4a0b9d285203a343c19f + MapboxCoreMaps: 2a2408bc6ce189f8a10ea13b2dee58878e23b6d3 + MapboxCoreNavigation: 520ff61a66bb98c42af95137ae72f6a8f79b8df2 + MapboxDirections-pre: b31da472d0d8190602b8ceacd74c281743fa6255 + MapboxMaps: 368fecb71f65e3f87bc69dbfcb787a01d745cf7c MapboxMobileEvents: f7f3e8daeb4b83688ae62a4172dce79169a97233 - MapboxNavigation: bb8bdc3f36b834f3c5753143efc6ced592f6c703 - MapboxNavigationNative: 7c588083547b121cf154402560364059aa5271e9 + MapboxNavigation: 4a2abc0fe93c989ff198e978044ad1515cf48d96 + MapboxNavigationNative: 8036fd05ee0e254d930eeb53d6948614d970f2f1 MapboxSpeech: e4ed02984444b6373374c72c369edaf045cc490c Polyline: fce41d72e1146c41c6d081f7656827226f643dff Solar-dev: 4612dc9878b9fed2667d23b327f1d4e54e16e8d0 diff --git a/Tests/MapboxCoreNavigationTests/RoutesCoordinatorTests.swift b/Tests/MapboxCoreNavigationTests/RoutesCoordinatorTests.swift index 507be1e78d6..3bc1f5b01ed 100644 --- a/Tests/MapboxCoreNavigationTests/RoutesCoordinatorTests.swift +++ b/Tests/MapboxCoreNavigationTests/RoutesCoordinatorTests.swift @@ -2,14 +2,17 @@ import Foundation import XCTest import TestHelper import MapboxNavigationNative +import MapboxDirections @testable import MapboxCoreNavigation +@_implementationOnly import MapboxCommon_Private +@_implementationOnly import MapboxNavigationNative_Private final class RoutesCoordinatorTests: TestCase { func testNormalCase() { let uuid = UUID() runTestCases([ - .init(routes: generateRoutes(), uuid: uuid, expectedResult: .success(())), - .init(routes: nil, uuid: uuid, expectedResult: .success(())) + .init(routes: generateRoutes(), uuid: uuid, routeIndex: 0, expectedResult: .success(())), + .init(routes: nil, uuid: uuid, routeIndex: 0, expectedResult: .success(())) ]) } @@ -17,45 +20,71 @@ final class RoutesCoordinatorTests: TestCase { let uuid1 = UUID() let uuid2 = UUID() runTestCases([ - .init(routes: generateRoutes(), uuid: uuid1, expectedResult: .success(())), - .init(routes: generateRoutes(), uuid: uuid2, expectedResult: .success(())), - .init(routes: nil, uuid: uuid1, expectedResult: .failure(.endingInvalidActiveNavigation)), + .init(routes: generateRoutes(), uuid: uuid1, routeIndex: 0, expectedResult: .success(())), + .init(routes: generateRoutes(), uuid: uuid2, routeIndex: 0, expectedResult: .success(())), + .init(routes: nil, uuid: uuid1, routeIndex: 0, expectedResult: .failure(.endingInvalidActiveNavigation)), ]) } func testReroutes() { let uuid = UUID() runTestCases([ - .init(routes: generateRoutes(), uuid: uuid, expectedResult: .success(())), - .init(routes: generateRoutes(), uuid: uuid, expectedResult: .success(())), - .init(routes: nil, uuid: uuid, expectedResult: .success(())), + .init(routes: generateRoutes(), uuid: uuid, routeIndex: 0, expectedResult: .success(())), + .init(routes: generateRoutes(), uuid: uuid, routeIndex: 0, expectedResult: .success(())), + .init(routes: nil, uuid: uuid, routeIndex: 0, expectedResult: .success(())), ]) } } private extension RoutesCoordinatorTests { - func generateRoutes() -> Routes { - .init(routesResponse: UUID().uuidString, routeIndex: 0, legIndex: 0, routesRequest: "") + func generateRoutes() -> RouteInterface? { + let route = Fixture.route(between: .init(latitude: 0, longitude: 0), + and: .init(latitude: 1, longitude: 1)) + guard case let .route(routeOptions) = route.response.options else { + XCTFail("Failed to generate test Route.") + return nil + } + let encoder = JSONEncoder() + encoder.userInfo[.options] = routeOptions + guard let routeData = try? encoder.encode(route.route), + let routeJSONString = String(data: routeData, encoding: .utf8) else { + XCTFail("Failed to encode generated test Route.") + return nil + } + + let routeRequest = Directions(credentials: Fixture.credentials).url(forCalculating: routeOptions).absoluteString + + let parsedRoutes = RouteParser.parseDirectionsResponse(forResponse: routeJSONString, + request: routeRequest) + + guard let generatedRoute = (parsedRoutes.value as? [RouteInterface])?.first else { + XCTFail("Failed to parse generated test Route.") + return nil + } + return generatedRoute } struct RoutesCoordinatorTestCase { - let routes: Routes? + let routes: RouteInterface? let uuid: UUID + let routeIndex: UInt32 let expectedResult: Result } func runTestCases(_ testCases: [RoutesCoordinatorTestCase]) { - var expectedRoutes: Routes? = generateRoutes() + var expectedRoutes: RouteInterface? = generateRoutes() + var expectedRouteIndex = UInt32.max var expectedResult: Result! - let handler: RoutesCoordinator.SetRoutesHandler = { routes, completion in - XCTAssertEqual(routes, expectedRoutes) + let handler: RoutesCoordinator.SetRoutesHandler = { routes, routeIndex, completion in + XCTAssertEqual(routes?.getRouteId(), expectedRoutes?.getRouteId()) + XCTAssertEqual(routeIndex, expectedRouteIndex) completion(expectedResult.mapError { $0 as Error }) } - let coordinator = RoutesCoordinator { routes, completion in - handler(routes, completion) + let coordinator = RoutesCoordinator { routes, routeIndex, completion in + handler(routes, routeIndex, completion) } for testCase in testCases { @@ -64,7 +93,8 @@ private extension RoutesCoordinatorTests { expectedResult = testCase.expectedResult .map { .init(alerts: []) } expectedRoutes = routes - coordinator.beginActiveNavigation(with: routes, uuid: testCase.uuid) { result in + expectedRouteIndex = testCase.routeIndex + coordinator.beginActiveNavigation(with: routes, uuid: testCase.uuid, legIndex: testCase.routeIndex) { result in switch (result, expectedResult) { case (.success(let routeInfo), .success(let expectedRouteInfo)): XCTAssertEqual(routeInfo, expectedRouteInfo) diff --git a/Tests/MapboxCoreNavigationTests/TilesetDescriptorFactoryTests.swift b/Tests/MapboxCoreNavigationTests/TilesetDescriptorFactoryTests.swift index 8de0ad67410..1cc4ca68a5b 100644 --- a/Tests/MapboxCoreNavigationTests/TilesetDescriptorFactoryTests.swift +++ b/Tests/MapboxCoreNavigationTests/TilesetDescriptorFactoryTests.swift @@ -15,13 +15,13 @@ final class TilesetDescriptorFactoryTests: TestCase { func testLatestDescriptorsAreFromGlobalNavigatorCacheHandle() { NavigationSettings.shared.initialize(directions: .mocked, tileStoreConfiguration: .custom(FileManager.default.temporaryDirectory)) - let navigator = Navigator.shared + _ = Navigator.shared let tilesetReceived = expectation(description: "Tileset received") TilesetDescriptorFactory.getLatest(completionQueue: .global(), datasetProfileIdentifier: MapboxCoreNavigation.Navigator.datasetProfileIdentifier) { latestTilesetDescriptor in - tilesetReceived.fulfill() XCTAssertEqual(latestTilesetDescriptor, TilesetDescriptorFactory.getLatestForCache(Navigator.shared.cacheHandle)) + tilesetReceived.fulfill() } waitForExpectations(timeout: 2, handler: nil) } diff --git a/Tests/MapboxNavigationTests/MapViewTests.swift b/Tests/MapboxNavigationTests/MapViewTests.swift index 9838f9e0ba4..ab2960f9361 100644 --- a/Tests/MapboxNavigationTests/MapViewTests.swift +++ b/Tests/MapboxNavigationTests/MapViewTests.swift @@ -41,6 +41,7 @@ class MapViewTests: TestCase { XCTAssertFalse(styleJSON.isEmpty, "ValueConverter should create valid JSON string.") let mapLoadingErrorExpectation = expectation(description: "Map loading error expectation") + mapLoadingErrorExpectation.assertForOverFulfill = false mapView.mapboxMap.onNext(.mapLoadingError, handler: { event in mapLoadingErrorExpectation.fulfill() @@ -111,6 +112,7 @@ class MapViewTests: TestCase { XCTAssertFalse(styleJSON.isEmpty, "ValueConverter should create valid JSON string.") let mapLoadingErrorExpectation = expectation(description: "Map loading error expectation") + mapLoadingErrorExpectation.assertForOverFulfill = false mapView.mapboxMap.onNext(.mapLoadingError, handler: { event in mapLoadingErrorExpectation.fulfill() @@ -182,6 +184,7 @@ class MapViewTests: TestCase { XCTAssertFalse(styleJSON.isEmpty, "ValueConverter should create valid JSON string.") let mapLoadingErrorExpectation = expectation(description: "Map loading error expectation") + mapLoadingErrorExpectation.assertForOverFulfill = false mapView.mapboxMap.onNext(.mapLoadingError, handler: { event in mapLoadingErrorExpectation.fulfill() diff --git a/Tests/MapboxNavigationTests/NavigationViewControllerTests.swift b/Tests/MapboxNavigationTests/NavigationViewControllerTests.swift index e7c051f3623..9834fc28d2e 100644 --- a/Tests/MapboxNavigationTests/NavigationViewControllerTests.swift +++ b/Tests/MapboxNavigationTests/NavigationViewControllerTests.swift @@ -333,7 +333,7 @@ class NavigationViewControllerTests: TestCase { newRouteResponse.identifier) } - func testPuck3DLayerPosition() { + func disabled_testPuck3DLayerPosition() { let service = MapboxNavigationService(routeResponse: initialRouteResponse, routeIndex: 0, routeOptions: routeOptions,