The SDLGetWayPoints
and SDLSubscribeWayPoints
RPCs are designed to allow you to get the navigation destination(s) from the active navigation app when the user has activated in-car navigation.
Both the SDLGetWayPoints
and SDLSubscribeWayPoints
RPCs are restricted by most OEMs. As a result, a module may reject your request if your app does not have the correct permissions. Your SDL app may also be restricted to only being allowed to get waypoints when your app is open (i.e. the hmiLevel
is non-NONE
) or when it is the currently active app (i.e. the hmiLevel
is FULL
).
SDLPermissionElement *getWayPoints = [[SDLPermissionElement alloc] initWithRPCName:SDLRPCFunctionNameGetWayPoints parameterPermissions:nil]; SDLPermissionElement *subscribeWayPoints = [[SDLPermissionElement alloc] initWithRPCName:SDLRPCFunctionNameSubscribeWayPoints parameterPermissions:nil]; id observerId = [self.sdlManager.permissionManager subscribeToRPCPermissions:@[getWayPoints, subscribeWayPoints] groupType:SDLPermissionGroupTypeAny withHandler:^(NSDictionary<SDLRPCFunctionName, SDLRPCPermissionStatus *> * _Nonnull updatedPermissionStatuses, SDLPermissionGroupStatus status) { // This handler will be called whenever the permission status changes BOOL getWayPointPermissionStatus = updatedPermissionStatuses[SDLRPCFunctionNameGetWayPoints]; if (getWayPointPermissionStatus) { // Your app has permission to send the `SDLGetWayPoints` request for its current HMI level } else { // Your app does not have permission to send the `SDLGetWayPoints` request for its current HMI level } BOOL subscribeWayPointsPermissionStatus = updatedPermissionStatuses[SDLRPCFunctionNameSubscribeWayPoints]; if (subscribeWayPointsPermissionStatus) { // Your app has permission to send the `SDLSubscribeWayPoints` request for its current HMI level } else { // Your app does not have permission to send the `SDLSubscribeWayPoints` request for its current HMI level } }];
let getWayPointsPermissionElement = SDLPermissionElement(rpcName: .getWayPoints, parameterPermissions: nil) let subscribeWayPointsPermissionElement = SDLPermissionElement(rpcName: .subscribeWayPoints, parameterPermissions: nil) let observerId = sdlManager.permissionManager.subscribe(toRPCPermissions: [getWayPointsPermissionElement, subscribeWayPointsPermissionElement], groupType: .any, withHandler: { (allChanges, groupStatus) in // This handler will be called whenever the permission status changes if let getWayPointPermissionStatus = allChanges[.getWayPoints], getWayPointPermissionStatus.isRPCAllowed == true { // Your app has permission to send the `SDLGetWayPoints` request for its current HMI level } else { // Your app does not have permission to send the `SDLGetWayPoints` request for its current HMI level } if let subscribeWayPointsPermissionStatus = allChanges[.subscribeWayPoints], subscribeWayPointsPermissionStatus.isRPCAllowed == true { // Your app has permission to send the `SubscribeWayPoints` request for its current HMI level } else { // Your app does not have permission to send the `SubscribeWayPoints` request for its current HMI level } })
Since some modules will not support getting waypoints, you should check if the module supports this feature before trying to use it. Once you have successfully connected to the module, you can check the module's capabilities via the SDLManager.systemCapabilityManager
as shown in the example below. Please note that you only need to check once if the module supports getting waypoints, however you must wait to perform this check until you know that the SDL app has been opened (i.e. the hmiLevel
is non-NONE
).
If you discover that the module does not support getting navigation waypoints or that your app does not have the right permissions, you should disable any buttons, voice commands, menu items, etc. in your app that would send the SDLGetWayPoints
or SDLSubscribeWayPoints
requests.
- (void)isGetWaypointsSupportedWithHandler:(void (^) (BOOL success, NSError * _Nullable error))handler { // Check if the module has navigation capabilities if (![self.sdlManager.systemCapabilityManager isCapabilitySupported:SDLSystemCapabilityTypeNavigation]) { return handler(false, nil); } // Legacy modules (pre-RPC Spec v4.5) do not support system capabilities, so for versions less than 4.5 we will assume `SDLGetWayPoints` and `SDLSubscribeWayPoints` are supported if `isCapabilitySupported` returns true SDLMsgVersion *sdlMsgVersion = self.sdlManager.registerResponse.sdlMsgVersion; if (sdlMsgVersion == nil) { return handler(true, nil); } SDLVersion *rpcSpecVersion = [[SDLVersion alloc] initWithSDLMsgVersion:sdlMsgVersion]; if (![rpcSpecVersion isGreaterThanOrEqualToVersion:[[SDLVersion alloc] initWithMajor:4 minor:5 patch:0]]) { return handler(true, nil); } // Check if the navigation capability has already been retrieved from the module SDLNavigationCapability *navigationCapability = self.sdlManager.systemCapabilityManager.navigationCapability; if (navigationCapability != nil) { return handler(navigationCapability.getWayPointsEnabled.boolValue, nil); } // Retrieve the navigation capability from the module [self.sdlManager.systemCapabilityManager updateCapabilityType:SDLSystemCapabilityTypeNavigation completionHandler:^(NSError * _Nullable error, SDLSystemCapabilityManager * _Nonnull systemCapabilityManager) { if (error != nil) { return handler(NO, error); } return handler(systemCapabilityManager.navigationCapability.getWayPointsEnabled.boolValue, nil); }]; }
func isGetWaypointsSupported(handler: @escaping (_ success: Bool, _ error: Error?) -> Void) { // Check if the module has navigation capabilities guard sdlManager.systemCapabilityManager.isCapabilitySupported(type: .navigation) else { return handler(false, nil) } // Legacy modules (pre-RPC Spec v4.5) do not support system capabilities, so for versions less than 4.5 we will assume `SDLGetWayPoints` and `SDLSubscribeWayPoints` are supported if `isCapabilitySupported` returns true guard let sdlMsgVersion = sdlManager.registerResponse?.sdlMsgVersion, SDLVersion(sdlMsgVersion: sdlMsgVersion).isGreaterThanOrEqual(to: SDLVersion(major: 4, minor: 5, patch: 0)) else { return handler(true, nil) } // Check if the navigation capability has already been retrieved from the module if let navigationCapability = sdlManager.systemCapabilityManager.navigationCapability { return handler(navigationCapability.getWayPointsEnabled?.boolValue ?? false, nil) } // Retrieve the navigation capability from the module sdlManager.systemCapabilityManager.updateCapabilityType(.navigation) { (error, systemCapabilityManager) in if (error != nil) { return handler(false, error) } return handler(systemCapabilityManager.navigationCapability?.getWayPointsEnabled?.boolValue ?? false, nil) } }
To subscribe to the navigation waypoints, you will have to set up your callback for whenever the waypoints are updated, then send the SDLSubscribeWayPoints
RPC.
// You can subscribe any time before SDL would send the notification (such as when you call `sdlManager.start` or at initialization of your manager) [self.sdlManager subscribeToRPC:SDLDidReceiveWaypointNotification withObserver:self selector:@selector(waypointsDidUpdate:)]; // Create this method to receive the subscription callback - (void)waypointsDidUpdate:(SDLRPCNotificationNotification *)notification { SDLOnWayPointChange *waypointUpdate = (SDLOnWayPointChange *)notification.notification; NSArray<SDLLocationDetails *> *waypoints = waypointUpdate.waypoints; <#Use the waypoint data#> } // After SDL has started your connection, at whatever point you want to subscribe, send the subscribe RPC SDLSubscribeWayPoints *subscribeWaypoints = [[SDLSubscribeWayPoints alloc] init]; [self.sdlManager sendRequest:subscribeWaypoints withResponseHandler:^(__kindof SDLRPCRequest * _Nullable request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error) { if (!response.success) { // Handle the error return; } // You are now subscribed }];
// You can subscribe any time before SDL would send the notification (such as when you call `sdlManager.start` or at initialization of your manager) sdlManager.subscribe(to: .SDLDidReceiveWaypoint, observer: self, selector: #selector(waypointsDidUpdate(_:))) // Create this method to receive the subscription callback @objc func waypointsDidUpdate(_ notification: SDLRPCNotificationNotification) { guard let waypointUpdate = notification.notification as? SDLOnWayPointChange else { return } let waypoints = waypointUpdate.waypoints <#Use the waypoint data#> } // After SDL has started your connection, at whatever point you want to subscribe, send the subscribe RPC let subscribeWaypoints = SDLSubscribeWayPoints() sdlManager.send(request: subscribeWaypoints) { (request, response, error) in guard let response = response, response.success.boolValue else { // Handle the errors return } // You are now subscribed }
To unsubscribe from waypoint data, you must send the SDLUnsubscribeWayPoints
RPC.
You do not have to unsubscribe from the sdlManager.subscribe
method, you must simply send the unsubscribe RPC and no more callbacks will be received.
SDLUnsubscribeWayPoints *unsubscribeWaypoints = [[SDLUnsubscribeWayPoints alloc] init]; [self.sdlManager sendRequest:unsubscribeWaypoints withResponseHandler:^(__kindof SDLRPCRequest * _Nullable request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error) { if (!response.success) { // Handle the error return; } // You are now unsubscribed }];
let unsubscribeWaypoints = SDLUnsubscribeWayPoints() sdlManager.send(request: unsubscribeWaypoints) { (request, response, error) in guard let response = response as? SDLGetWayPointsResponse, response.success.boolValue else { // Handle the error return } // You are now unsubscribed }
If you only need waypoint data once without an ongoing subscription, you can use SDLGetWayPoints
instead of SDLSubscribeWayPoints
.
SDLGetWayPoints *getWaypoints = [[SDLGetWayPoints alloc] initWithType:SDLWayPointTypeAll]; [self.sdlManager sendRequest:getWaypoints withResponseHandler:^(__kindof SDLRPCRequest * _Nullable request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error) { if (!response.success) { // Handle the error return; } SDLGetWayPointsResponse *waypointResponse = response; NSArray<SDLLocationDetails *> *waypointLocations = waypointResponse.waypoints; <#Use the waypoint data#> }];
let getWaypoints = SDLGetWayPoints(type: .all) sdlManager.send(request: getWaypoints) { (request, response, error) in guard let response = response as? SDLGetWayPointsResponse, response.success.boolValue else { // Handle the errors return } let waypointLocations = response.waypoints; <#Use the waypoint data#> }