diff --git a/Cartfile b/Cartfile index 0d7a46b3199..edf056b7ee8 100644 --- a/Cartfile +++ b/Cartfile @@ -1,7 +1,7 @@ binary "https://www.mapbox.com/ios-sdk/Mapbox-iOS-SDK.json" ~> 4.3 binary "https://www.mapbox.com/ios-sdk/MapboxNavigationNative.json" ~> 5.0.0 -github "mapbox/MapboxDirections.swift" ~> 0.26.0 +github "mapbox/MapboxDirections.swift" ~> 0.27.0 github "mapbox/turf-swift" ~> 0.3 -github "mapbox/mapbox-events-ios" ~> 0.6 +github "mapbox/mapbox-events-ios" ~> 0.8.1 github "ceeK/Solar" ~> 2.1.0 github "mapbox/mapbox-speech-swift" ~> 0.1.0 diff --git a/Cartfile.resolved b/Cartfile.resolved index 4ee90c58a75..35a47804c27 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -4,9 +4,9 @@ github "CedarBDD/Cedar" "v1.0" github "Quick/Nimble" "v7.3.3" github "Quick/Quick" "v1.3.4" github "ceeK/Solar" "2.1.0" -github "mapbox/MapboxDirections.swift" "v0.26.1" +github "mapbox/MapboxDirections.swift" "v0.27.0" github "mapbox/MapboxGeocoder.swift" "v0.10.1" -github "mapbox/mapbox-events-ios" "v0.6.0" +github "mapbox/mapbox-events-ios" "v0.8.1" github "mapbox/mapbox-speech-swift" "v0.1.0" github "mapbox/turf-swift" "v0.3.0" github "raphaelmor/Polyline" "v4.2.0" diff --git a/MapboxCoreNavigation.podspec b/MapboxCoreNavigation.podspec index 60e938cbcdd..1bfa0a21252 100644 --- a/MapboxCoreNavigation.podspec +++ b/MapboxCoreNavigation.podspec @@ -41,8 +41,8 @@ Pod::Spec.new do |s| s.module_name = "MapboxCoreNavigation" s.dependency "MapboxNavigationNative", "~> 5.0.0" - s.dependency "MapboxDirections.swift", "~> 0.26.0" # Always pin to a patch release if pre-1.0 - s.dependency "MapboxMobileEvents", "~> 0.6.0" # Always pin to a patch release if pre-1.0 + s.dependency "MapboxDirections.swift", "~> 0.27.0" # Always pin to a patch release if pre-1.0 + s.dependency "MapboxMobileEvents", "~> 0.8.1" # Always pin to a patch release if pre-1.0 s.dependency "Turf", "~> 0.3.0" # Always pin to a patch release if pre-1.0 # `swift_version` was introduced in CocoaPods 1.4.0. Without this check, if a user were to diff --git a/MapboxCoreNavigation/EventDetails.swift b/MapboxCoreNavigation/EventDetails.swift index 64249e1b09e..65c89d0533b 100644 --- a/MapboxCoreNavigation/EventDetails.swift +++ b/MapboxCoreNavigation/EventDetails.swift @@ -5,8 +5,54 @@ import UIKit import AVFoundation import MapboxDirections +protocol EventDetails: Encodable { + var event: String? { get } + var created: Date { get } + var sessionIdentifier: String { get } +} + +struct PerformanceEventDetails: EventDetails { + let event: String? + let created: Date + let sessionIdentifier: String + var counters: [Counter] = [] + var attributes: [Attribute] = [] + + private enum CodingKeys: String, CodingKey { + case event + case created + case sessionIdentifier = "sessionId" + case counters + case attributes + } + + struct Counter: Encodable { + let name: String + let value: Double + } + + struct Attribute: Encodable { + let name: String + let value: String + } + + init(event: String?, session: SessionState, createdOn created: Date?) { + self.event = event + sessionIdentifier = session.identifier.uuidString + self.created = created ?? Date() + } + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encodeIfPresent(event, forKey: .event) + try container.encode(created.ISO8601, forKey: .created) + try container.encode(sessionIdentifier, forKey: .sessionIdentifier) + try container.encode(counters, forKey: .counters) + try container.encode(attributes, forKey: .attributes) + } +} -struct EventDetails: Encodable { +struct NavigationEventDetails: EventDetails { let audioType: String = AVAudioSession.sharedInstance().audioType let applicationState: UIApplicationState = UIApplication.shared.applicationState @@ -286,12 +332,11 @@ extension RouteLegProgress: Encodable { } } +enum EventDetailsError: Error { + case EncodingError(String) +} + extension EventDetails { - - enum EventDetailsError: Error { - case EncodingError(String) - } - func asDictionary() throws -> [String: Any] { let data = try JSONEncoder().encode(self) if let dictionary = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String: Any] { diff --git a/MapboxCoreNavigation/NavigationEventsManager.swift b/MapboxCoreNavigation/NavigationEventsManager.swift index 421f9296401..156e6499896 100644 --- a/MapboxCoreNavigation/NavigationEventsManager.swift +++ b/MapboxCoreNavigation/NavigationEventsManager.swift @@ -2,6 +2,8 @@ import Foundation import MapboxMobileEvents import MapboxDirections +let NavigationEventTypeRouteRetrieval = "mobile.performance_trace" + /** The `EventsManagerDataSource` protocol declares values required for recording route following events. */ @@ -89,11 +91,11 @@ open class NavigationEventsManager: NSObject { mobileEventsManager.sendTurnstileEvent() } - func navigationCancelEvent(rating potentialRating: Int? = nil, comment: String? = nil) -> EventDetails? { + func navigationCancelEvent(rating potentialRating: Int? = nil, comment: String? = nil) -> NavigationEventDetails? { guard let dataSource = dataSource, let sessionState = sessionState else { return nil } let rating = potentialRating ?? MMEEventsManager.unrated - var event = EventDetails(dataSource: dataSource, session: sessionState, defaultInterface: usesDefaultUserInterface) + var event = NavigationEventDetails(dataSource: dataSource, session: sessionState, defaultInterface: usesDefaultUserInterface) event.event = MMEEventTypeNavigationCancel event.arrivalTimestamp = sessionState.arrivalTimestamp @@ -107,18 +109,34 @@ open class NavigationEventsManager: NSObject { return event } - func navigationDepartEvent() -> EventDetails? { + func navigationRouteRetrievalEvent() -> PerformanceEventDetails? { + guard let sessionState = sessionState, + let responseEndDate = sessionState.currentRoute.responseEndDate, + let fetchStartDate = sessionState.currentRoute.fetchStartDate else { + return nil + } + + var event = PerformanceEventDetails(event: NavigationEventTypeRouteRetrieval, session: sessionState, createdOn: sessionState.currentRoute.responseEndDate) + event.counters.append(PerformanceEventDetails.Counter(name: "elapsed_time", + value: responseEndDate.timeIntervalSince(fetchStartDate))) + if let routeIdentifier = sessionState.currentRoute.routeIdentifier { + event.attributes.append(PerformanceEventDetails.Attribute(name: "route_uuid", value: routeIdentifier)) + } + return event + } + + func navigationDepartEvent() -> NavigationEventDetails? { guard let dataSource = dataSource, let sessionState = sessionState else { return nil } - var event = EventDetails(dataSource: dataSource, session: sessionState, defaultInterface: usesDefaultUserInterface) + var event = NavigationEventDetails(dataSource: dataSource, session: sessionState, defaultInterface: usesDefaultUserInterface) event.event = MMEEventTypeNavigationDepart return event } - func navigationArriveEvent() -> EventDetails? { + func navigationArriveEvent() -> NavigationEventDetails? { guard let dataSource = dataSource, let sessionState = sessionState else { return nil } - var event = EventDetails(dataSource: dataSource, session: sessionState, defaultInterface: usesDefaultUserInterface) + var event = NavigationEventDetails(dataSource: dataSource, session: sessionState, defaultInterface: usesDefaultUserInterface) event.event = MMEEventTypeNavigationArrive return event } @@ -131,10 +149,10 @@ open class NavigationEventsManager: NSObject { return eventDictionary } - func navigationFeedbackEvent(type: FeedbackType, description: String?) -> EventDetails? { + func navigationFeedbackEvent(type: FeedbackType, description: String?) -> NavigationEventDetails? { guard let dataSource = dataSource, let sessionState = sessionState else { return nil } - var event = EventDetails(dataSource: dataSource, session: sessionState, defaultInterface: usesDefaultUserInterface) + var event = NavigationEventDetails(dataSource: dataSource, session: sessionState, defaultInterface: usesDefaultUserInterface) event.event = MMEEventTypeNavigationFeedback event.userId = UIDevice.current.identifierForVendor?.uuidString @@ -146,11 +164,11 @@ open class NavigationEventsManager: NSObject { return event } - func navigationRerouteEvent(eventType: String = MMEEventTypeNavigationReroute) -> EventDetails? { + func navigationRerouteEvent(eventType: String = MMEEventTypeNavigationReroute) -> NavigationEventDetails? { guard let dataSource = dataSource, let sessionState = sessionState else { return nil } let timestamp = Date() - var event = EventDetails(dataSource: dataSource, session: sessionState, defaultInterface: usesDefaultUserInterface) + var event = NavigationEventDetails(dataSource: dataSource, session: sessionState, defaultInterface: usesDefaultUserInterface) event.event = eventType if let lastRerouteDate = sessionState.lastRerouteDate { event.secondsSinceLastReroute = round(timestamp.timeIntervalSince(lastRerouteDate)) @@ -178,6 +196,12 @@ open class NavigationEventsManager: NSObject { mobileEventsManager.enqueueEvent(withName: MMEventTypeNavigationCarplayDisconnect, attributes: [MMEEventKeyEvent: MMEventTypeNavigationCarplayDisconnect, MMEEventKeyCreated: date.ISO8601]) mobileEventsManager.flush() } + + func sendRouteRetrievalEvent() { + guard let attributes = try? navigationRouteRetrievalEvent()?.asDictionary() else { return } + mobileEventsManager.enqueueEvent(withName: NavigationEventTypeRouteRetrieval, attributes: attributes ?? [:]) + mobileEventsManager.flush() + } func sendDepartEvent() { guard let attributes = try? navigationDepartEvent()?.asDictionary() else { return } diff --git a/MapboxCoreNavigation/NavigationService.swift b/MapboxCoreNavigation/NavigationService.swift index b839a1a36d1..1335e5b6b86 100644 --- a/MapboxCoreNavigation/NavigationService.swift +++ b/MapboxCoreNavigation/NavigationService.swift @@ -334,6 +334,8 @@ public class MapboxNavigationService: NSObject, NavigationService, DefaultInterf if simulationMode == .always { simulate() } + + eventsManager.sendRouteRetrievalEvent() } public func stop() { diff --git a/MapboxCoreNavigationTests/CocoaPodsTest/PodInstall/PodInstall.xcodeproj/project.pbxproj b/MapboxCoreNavigationTests/CocoaPodsTest/PodInstall/PodInstall.xcodeproj/project.pbxproj index b991843313a..72b7df64edf 100644 --- a/MapboxCoreNavigationTests/CocoaPodsTest/PodInstall/PodInstall.xcodeproj/project.pbxproj +++ b/MapboxCoreNavigationTests/CocoaPodsTest/PodInstall/PodInstall.xcodeproj/project.pbxproj @@ -228,7 +228,7 @@ files = ( ); inputPaths = ( - "${SRCROOT}/Pods/Target Support Files/Pods-PodInstall/Pods-PodInstall-frameworks.sh", + "${PODS_ROOT}/Target Support Files/Pods-PodInstall/Pods-PodInstall-frameworks.sh", "${PODS_ROOT}/Mapbox-iOS-SDK/dynamic/Mapbox.framework", "${PODS_ROOT}/Mapbox-iOS-SDK/dynamic/Mapbox.framework.dSYM", "${BUILT_PRODUCTS_DIR}/MapboxCoreNavigation/MapboxCoreNavigation.framework", @@ -259,7 +259,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-PodInstall/Pods-PodInstall-frameworks.sh\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-PodInstall/Pods-PodInstall-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; FCAEDB6CF8C116409B047EA1 /* [CP] Check Pods Manifest.lock */ = { diff --git a/MapboxCoreNavigationTests/CocoaPodsTest/PodInstall/Podfile.lock b/MapboxCoreNavigationTests/CocoaPodsTest/PodInstall/Podfile.lock index 45f1cee05d2..a72d1504d6d 100644 --- a/MapboxCoreNavigationTests/CocoaPodsTest/PodInstall/Podfile.lock +++ b/MapboxCoreNavigationTests/CocoaPodsTest/PodInstall/Podfile.lock @@ -1,13 +1,13 @@ PODS: - - Mapbox-iOS-SDK (4.7.1) + - Mapbox-iOS-SDK (4.8.0) - MapboxCoreNavigation (0.28.0): - - MapboxDirections.swift (~> 0.26.0) - - MapboxMobileEvents (~> 0.6.0) + - MapboxDirections.swift (~> 0.27.0) + - MapboxMobileEvents (~> 0.8.1) - MapboxNavigationNative (~> 5.0.0) - Turf (~> 0.3.0) - - MapboxDirections.swift (0.26.1): + - MapboxDirections.swift (0.27.0): - Polyline (~> 4.2) - - MapboxMobileEvents (0.6.0) + - MapboxMobileEvents (0.8.1) - MapboxNavigation (0.28.0): - Mapbox-iOS-SDK (~> 4.3) - MapboxCoreNavigation (= 0.28.0) @@ -41,11 +41,11 @@ EXTERNAL SOURCES: :path: "../../../" SPEC CHECKSUMS: - Mapbox-iOS-SDK: f9292a75e8cd5eb828f4432623bfc48aac42146a - MapboxCoreNavigation: d1e430193a9e0e6dafbf05261037f6d31f94c4ce - MapboxDirections.swift: 514610ac52a9cd080062085f35b8a9fdb27a83cb - MapboxMobileEvents: 0218869f556a7fdac9f869e2f9c10e8c6dd7eed5 - MapboxNavigation: 548acd4bf7acfada61a071b37f8be82d08a7e14c + Mapbox-iOS-SDK: 864bc732a20c183d87f04923295e988b504453d9 + MapboxCoreNavigation: ac3a29edeae405c0679e74d4e9e71acda6244870 + MapboxDirections.swift: 622bfe28c216eb6124331a0a03c97d7f587b286f + MapboxMobileEvents: 0f30fe39687f26fd8f255335053023ba039a0392 + MapboxNavigation: e6e29545545e1c715fd23406c6c829ca9285aa02 MapboxNavigationNative: bb220f45f93123adae2c05dfb74080773af128f3 MapboxSpeech: d7569ee91dd8bdca3ef8d19b6828e8754c1073ec Polyline: 3d69f75bb136357e27291d439d0436c6ebda57a4 @@ -54,4 +54,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 55ae5a7edbc3dedda5149ba3d58b992ab7327f95 -COCOAPODS: 1.5.3 +COCOAPODS: 1.6.0 diff --git a/MapboxCoreNavigationTests/NavigationServiceTests.swift b/MapboxCoreNavigationTests/NavigationServiceTests.swift index cfe32e18161..3f9d2759f59 100644 --- a/MapboxCoreNavigationTests/NavigationServiceTests.swift +++ b/MapboxCoreNavigationTests/NavigationServiceTests.swift @@ -233,6 +233,9 @@ class NavigationServiceTests: XCTestCase { navigationService.eventsManager.delaysEventFlushing = false navigationService.start() + let eventsManagerSpy = navigationService.eventsManager as! NavigationEventsManagerSpy + XCTAssertTrue(eventsManagerSpy.hasFlushedEvent(with: NavigationEventTypeRouteRetrieval)) + router.route = alternateRoute let simulatedLocationManager = navigationService.locationManager as! SimulatedLocationManager diff --git a/MapboxNavigation-Documentation.podspec b/MapboxNavigation-Documentation.podspec index dac6b563b22..125773dc37e 100644 --- a/MapboxNavigation-Documentation.podspec +++ b/MapboxNavigation-Documentation.podspec @@ -45,10 +45,10 @@ Pod::Spec.new do |s| s.frameworks = ['CarPlay'] - s.dependency "MapboxDirections.swift", "~> 0.26.0" + s.dependency "MapboxDirections.swift", "~> 0.27.0" s.dependency "MapboxGeocoder.swift", "~> 0.10.0" s.dependency "Mapbox-iOS-SDK", "~> 4.3" - s.dependency "MapboxMobileEvents", "~> 0.6.0" + s.dependency "MapboxMobileEvents", "~> 0.8.1" s.dependency "Solar", "~> 2.1" s.dependency "Turf", "~> 0.3.0" s.dependency "MapboxSpeech", "~> 0.1" diff --git a/TestHelper/Fixture.swift b/TestHelper/Fixture.swift index 03d6980b885..40c4fec4c69 100644 --- a/TestHelper/Fixture.swift +++ b/TestHelper/Fixture.swift @@ -77,7 +77,15 @@ public class Fixture: NSObject { let response = JSONFromFileNamed(name: jsonFile) let waypoints = Fixture.waypoints(from: jsonFile) let jsonRoute = (response["routes"] as! [AnyObject]).first as! [String : Any] - return Route(json: jsonRoute, waypoints: waypoints, options: NavigationRouteOptions(waypoints: waypoints)) + let route = Route(json: jsonRoute, waypoints: waypoints, options: NavigationRouteOptions(waypoints: waypoints)) + + // Like `Directions.postprocess(_:fetchStartDate:uuid:)` + route.routeIdentifier = response["uuid"] as? String + let fetchStartDate = Date(timeIntervalSince1970: 3600) + route.fetchStartDate = fetchStartDate + route.responseEndDate = Date(timeInterval: 1, since: fetchStartDate) + + return route } public class func waypoints(from jsonFile: String) -> [Waypoint] {