summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoel Fischer <joeljfischer@gmail.com>2019-05-29 09:19:36 -0400
committerGitHub <noreply@github.com>2019-05-29 09:19:36 -0400
commit70deeceb07e60d9f5c911ea30bc335eb130aaa71 (patch)
tree8966f2b138176b3b2b7ee73fe6457c38c958c7b8
parent7f0f28f94b023f7107ee896cf0c64836cc532010 (diff)
parent3b237aba6cbce79006b8e6d9d156fd5ed2dbcc3c (diff)
downloadsdl_ios-70deeceb07e60d9f5c911ea30bc335eb130aaa71.tar.gz
Merge pull request #1279 from smartdevicelink/feature/issue_1222_align_systemcapabilitymanager_callbacks
Support SDLSystemCapabilityManager Subscriptions
-rw-r--r--SmartDeviceLink-iOS.xcodeproj/project.pbxproj30
-rw-r--r--SmartDeviceLink/SDLError.h1
-rw-r--r--SmartDeviceLink/SDLError.m6
-rw-r--r--SmartDeviceLink/SDLLifecycleManager.m1
-rw-r--r--SmartDeviceLink/SDLSystemCapabilityManager.h110
-rw-r--r--SmartDeviceLink/SDLSystemCapabilityManager.m137
-rw-r--r--SmartDeviceLink/SDLSystemCapabilityObserver.h57
-rw-r--r--SmartDeviceLink/SDLSystemCapabilityObserver.m37
-rw-r--r--SmartDeviceLinkTests/DevAPISpecs/TestSystemCapabilityObserver.h24
-rw-r--r--SmartDeviceLinkTests/DevAPISpecs/TestSystemCapabilityObserver.m30
-rw-r--r--SmartDeviceLinkTests/SDLSystemCapabilityManagerSpec.m131
11 files changed, 514 insertions, 50 deletions
diff --git a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj
index 91cebebb7..46abf3e31 100644
--- a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj
+++ b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj
@@ -1004,6 +1004,9 @@
5D6F7A351BC5B9B60070BF37 /* SDLLockScreenViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D6F7A331BC5B9B60070BF37 /* SDLLockScreenViewController.h */; settings = {ATTRIBUTES = (Public, ); }; };
5D6F7A361BC5B9B60070BF37 /* SDLLockScreenViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D6F7A341BC5B9B60070BF37 /* SDLLockScreenViewController.m */; };
5D6F7A3E1BC811FC0070BF37 /* SDLAssets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5D6F7A3D1BC811FC0070BF37 /* SDLAssets.xcassets */; };
+ 5D75960D22972F830013207C /* TestSystemCapabilityObserver.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D75960C22972F830013207C /* TestSystemCapabilityObserver.m */; };
+ 5D75961122972FCA0013207C /* SDLSystemCapabilityObserver.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D75960F22972FCA0013207C /* SDLSystemCapabilityObserver.h */; };
+ 5D75961222972FCA0013207C /* SDLSystemCapabilityObserver.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D75961022972FCA0013207C /* SDLSystemCapabilityObserver.m */; };
5D76E31C1D3805FF00647CFA /* SDLLockScreenManagerSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D76E31B1D3805FF00647CFA /* SDLLockScreenManagerSpec.m */; };
5D76E3211D39742300647CFA /* SDLViewControllerPresentable.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D76E3201D39742300647CFA /* SDLViewControllerPresentable.h */; };
5D76E3241D39767000647CFA /* SDLLockScreenPresenter.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D76E3221D39767000647CFA /* SDLLockScreenPresenter.h */; };
@@ -2638,6 +2641,10 @@
5D6F7A331BC5B9B60070BF37 /* SDLLockScreenViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDLLockScreenViewController.h; sourceTree = "<group>"; };
5D6F7A341BC5B9B60070BF37 /* SDLLockScreenViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDLLockScreenViewController.m; sourceTree = "<group>"; };
5D6F7A3D1BC811FC0070BF37 /* SDLAssets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = SDLAssets.xcassets; path = Assets/SDLAssets.xcassets; sourceTree = "<group>"; };
+ 5D75960B22972F830013207C /* TestSystemCapabilityObserver.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = TestSystemCapabilityObserver.h; path = DevAPISpecs/TestSystemCapabilityObserver.h; sourceTree = "<group>"; };
+ 5D75960C22972F830013207C /* TestSystemCapabilityObserver.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = TestSystemCapabilityObserver.m; path = DevAPISpecs/TestSystemCapabilityObserver.m; sourceTree = "<group>"; };
+ 5D75960F22972FCA0013207C /* SDLSystemCapabilityObserver.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDLSystemCapabilityObserver.h; sourceTree = "<group>"; };
+ 5D75961022972FCA0013207C /* SDLSystemCapabilityObserver.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDLSystemCapabilityObserver.m; sourceTree = "<group>"; };
5D76E31B1D3805FF00647CFA /* SDLLockScreenManagerSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDLLockScreenManagerSpec.m; path = DevAPISpecs/SDLLockScreenManagerSpec.m; sourceTree = "<group>"; };
5D76E3201D39742300647CFA /* SDLViewControllerPresentable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDLViewControllerPresentable.h; sourceTree = "<group>"; };
5D76E3221D39767000647CFA /* SDLLockScreenPresenter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDLLockScreenPresenter.h; sourceTree = "<group>"; };
@@ -5060,6 +5067,24 @@
name = "Lock Screen UI";
sourceTree = "<group>";
};
+ 5D75960A22972F620013207C /* Utilities */ = {
+ isa = PBXGroup;
+ children = (
+ 5D75960B22972F830013207C /* TestSystemCapabilityObserver.h */,
+ 5D75960C22972F830013207C /* TestSystemCapabilityObserver.m */,
+ );
+ name = Utilities;
+ sourceTree = "<group>";
+ };
+ 5D75960E22972FB90013207C /* Utilities */ = {
+ isa = PBXGroup;
+ children = (
+ 5D75960F22972FCA0013207C /* SDLSystemCapabilityObserver.h */,
+ 5D75961022972FCA0013207C /* SDLSystemCapabilityObserver.m */,
+ );
+ name = Utilities;
+ sourceTree = "<group>";
+ };
5D76E31A1D3805E600647CFA /* LockScreen */ = {
isa = PBXGroup;
children = (
@@ -5782,6 +5807,7 @@
880E35B12088F73400181259 /* System Capabilities */ = {
isa = PBXGroup;
children = (
+ 5D75960E22972FB90013207C /* Utilities */,
880E35B32088F75A00181259 /* SDLSystemCapabilityManager.h */,
880E35B22088F75A00181259 /* SDLSystemCapabilityManager.m */,
);
@@ -5791,6 +5817,7 @@
880E35B62088F77C00181259 /* System Capabilities */ = {
isa = PBXGroup;
children = (
+ 5D75960A22972F620013207C /* Utilities */,
880E35B72088F78E00181259 /* SDLSystemCapabilityManagerSpec.m */,
);
name = "System Capabilities";
@@ -6315,6 +6342,7 @@
5D61FDB11A84238C00846EE7 /* SDLSubscribeVehicleData.h in Headers */,
5DB996601F28C6ED002D8795 /* SDLControlFramePayloadVideoStartServiceAck.h in Headers */,
5DCF76F51ACDBAD300BB647B /* SDLSendLocation.h in Headers */,
+ 5D75961122972FCA0013207C /* SDLSystemCapabilityObserver.h in Headers */,
5D61FC9E1A84238C00846EE7 /* SDLEncodedSyncPData.h in Headers */,
1FF7DABA1F75B2A800B46C30 /* SDLFocusableItemLocator.h in Headers */,
5D019276214994AC003500F6 /* NSMutableArray+Safe.h in Headers */,
@@ -7069,6 +7097,7 @@
88EEC5BC220A327B005AA2F9 /* SDLPublishAppServiceResponse.m in Sources */,
5D61FC951A84238C00846EE7 /* SDLDriverDistractionState.m in Sources */,
5DB996611F28C6ED002D8795 /* SDLControlFramePayloadVideoStartServiceAck.m in Sources */,
+ 5D75961222972FCA0013207C /* SDLSystemCapabilityObserver.m in Sources */,
884E702021FB983F008D53BA /* SDLAppServiceManifest.m in Sources */,
5D61FD961A84238C00846EE7 /* SDLShowResponse.m in Sources */,
5D61FD981A84238C00846EE7 /* SDLSingleTireStatus.m in Sources */,
@@ -7415,6 +7444,7 @@
162E837E1A9BDE8B00906325 /* SDLGPSDataSpec.m in Sources */,
5DAE06731BDEC6C000F9B498 /* SDLFileSpec.m in Sources */,
162E82E11A9BDE8B00906325 /* SDLHMIZoneCapabilitiesSpec.m in Sources */,
+ 5D75960D22972F830013207C /* TestSystemCapabilityObserver.m in Sources */,
88B58DBD222042500011B063 /* SDLDirectionSpec.m in Sources */,
162E83721A9BDE8B00906325 /* SDLAudioPassThruCapabilitiesSpec.m in Sources */,
162E83681A9BDE8B00906325 /* SDLSpeakResponseSpec.m in Sources */,
diff --git a/SmartDeviceLink/SDLError.h b/SmartDeviceLink/SDLError.h
index 9c6209c5b..280c20309 100644
--- a/SmartDeviceLink/SDLError.h
+++ b/SmartDeviceLink/SDLError.h
@@ -90,6 +90,7 @@ extern SDLErrorDomain *const SDLErrorDomainRPCStore;
+ (NSException *)sdl_invalidSoftButtonStateException;
+ (NSException *)sdl_carWindowOrientationException;
+ (NSException *)sdl_invalidLockscreenSetupException;
++ (NSException *)sdl_invalidSelectorExceptionWithSelector:(SEL)selector;
@end
diff --git a/SmartDeviceLink/SDLError.m b/SmartDeviceLink/SDLError.m
index 7e5a0e95b..f72608a31 100644
--- a/SmartDeviceLink/SDLError.m
+++ b/SmartDeviceLink/SDLError.m
@@ -328,6 +328,12 @@ SDLErrorDomain *const SDLErrorDomainRPCStore = @"com.sdl.rpcStore.error";
userInfo:nil];
}
++ (NSException *)sdl_invalidSelectorExceptionWithSelector:(SEL)selector {
+ return [NSException exceptionWithName:@"com.sdl.systemCapabilityManager.selectorException"
+ reason:[NSString stringWithFormat:@"Capability observation selector: %@ does not match possible selectors, which must have either 0 or 1 parameters", NSStringFromSelector(selector)]
+ userInfo:nil];
+}
+
@end
NS_ASSUME_NONNULL_END
diff --git a/SmartDeviceLink/SDLLifecycleManager.m b/SmartDeviceLink/SDLLifecycleManager.m
index 0c3b257ab..7ba94a6f7 100644
--- a/SmartDeviceLink/SDLLifecycleManager.m
+++ b/SmartDeviceLink/SDLLifecycleManager.m
@@ -385,6 +385,7 @@ SDLLifecycleState *const SDLLifecycleStateReady = @"Ready";
dispatch_group_enter(managerGroup);
SDLLogD(@"Setting up assistant managers");
[self.lockScreenManager start];
+ [self.systemCapabilityManager start];
dispatch_group_enter(managerGroup);
[self.fileManager startWithCompletionHandler:^(BOOL success, NSError *_Nullable error) {
diff --git a/SmartDeviceLink/SDLSystemCapabilityManager.h b/SmartDeviceLink/SDLSystemCapabilityManager.h
index 11e31a1c2..38030365a 100644
--- a/SmartDeviceLink/SDLSystemCapabilityManager.h
+++ b/SmartDeviceLink/SDLSystemCapabilityManager.h
@@ -24,6 +24,7 @@
@class SDLPresetBankCapabilities;
@class SDLRemoteControlCapabilities;
@class SDLSoftButtonCapabilities;
+@class SDLSystemCapability;
@class SDLSystemCapabilityManager;
@class SDLVideoStreamingCapability;
@@ -31,7 +32,6 @@
NS_ASSUME_NONNULL_BEGIN
-
/**
* A completion handler called after a request for the capability type is returned from the remote system.
*
@@ -40,10 +40,24 @@ NS_ASSUME_NONNULL_BEGIN
*/
typedef void (^SDLUpdateCapabilityHandler)(NSError * _Nullable error, SDLSystemCapabilityManager *systemCapabilityManager);
+/**
+ An observer block for whenever a subscription is called.
+
+ @param capability The capability that was updated.
+ */
+typedef void (^SDLCapabilityUpdateHandler)(SDLSystemCapability *capability);
+/**
+ A manager that handles updating and subscribing to SDL capabilities.
+ */
@interface SDLSystemCapabilityManager : NSObject
/**
+ YES if subscriptions are available on the connected head unit. If NO, calls to `subscribeToCapabilityType:withBlock` and `subscribeToCapabilityType:withObserver:selector` will fail.
+ */
+@property (assign, nonatomic, readonly) BOOL supportsSubscriptions;
+
+/**
* @see SDLDisplayCapabilities
*
* Optional
@@ -134,59 +148,64 @@ typedef void (^SDLUpdateCapabilityHandler)(NSError * _Nullable error, SDLSystemC
@property (nullable, strong, nonatomic, readonly) SDLAppServicesCapabilities *appServicesCapabilities;
/**
- * If returned, the platform supports navigation
- *
- * @see SDLNavigationCapability
- *
- * Optional
+ If returned, the platform supports navigation
+
+ @see SDLNavigationCapability
+
+ Optional
*/
@property (nullable, strong, nonatomic, readonly) SDLNavigationCapability *navigationCapability;
/**
- * If returned, the platform supports making phone calls
- *
- * @see SDLPhoneCapability
- *
- * Optional
+ If returned, the platform supports making phone calls
+
+ @see SDLPhoneCapability
+
+ Optional
*/
@property (nullable, strong, nonatomic, readonly) SDLPhoneCapability *phoneCapability;
/**
- * If returned, the platform supports video streaming
- *
- * @see SDLVideoStreamingCapability
- *
- * Optional
+ If returned, the platform supports video streaming
+
+ @see SDLVideoStreamingCapability
+
+ Optional
*/
@property (nullable, strong, nonatomic, readonly) SDLVideoStreamingCapability *videoStreamingCapability;
/**
- * If returned, the platform supports remote control capabilities
- *
- * @see SDLRemoteControlCapabilities
- *
- * Optional
+ If returned, the platform supports remote control capabilities
+
+ @see SDLRemoteControlCapabilities
+
+ Optional
*/
@property (nullable, strong, nonatomic, readonly) SDLRemoteControlCapabilities *remoteControlCapability;
/**
- * Init is unavailable. Dependencies must be injected using initWithConnectionManager:
- *
- * @return nil
+ Init is unavailable. Dependencies must be injected using initWithConnectionManager:
+
+ @return nil
*/
- (instancetype)init NS_UNAVAILABLE;
/**
- * Creates a new system capability manager with a specified connection manager
- *
- * @param manager A connection manager to use to forward on RPCs
- *
- * @return An instance of SDLSystemCapabilityManager
+ Creates a new system capability manager with a specified connection manager
+
+ @param manager A connection manager to use to forward on RPCs
+
+ @return An instance of SDLSystemCapabilityManager
*/
- (instancetype)initWithConnectionManager:(id<SDLConnectionManagerType>)manager NS_DESIGNATED_INITIALIZER;
/**
- * Stops the manager. This method is used internally.
+ Starts the manager. This method is used internally.
+ */
+- (void)start;
+
+/**
+ Stops the manager. This method is used internally.
*/
- (void)stop;
@@ -198,6 +217,37 @@ typedef void (^SDLUpdateCapabilityHandler)(NSError * _Nullable error, SDLSystemC
*/
- (void)updateCapabilityType:(SDLSystemCapabilityType)type completionHandler:(SDLUpdateCapabilityHandler)handler;
+/**
+ Subscribe to a particular capability type using a block callback
+
+ @param type The type of capability to subscribe to
+ @param block The block to be called when the capability is updated
+ @return An object that can be used to unsubscribe the block using unsubscribeFromCapabilityType:withObserver: by passing it in the observer callback, or nil if subscriptions aren't available on this head unit
+ */
+- (nullable id<NSObject>)subscribeToCapabilityType:(SDLSystemCapabilityType)type usingBlock:(SDLCapabilityUpdateHandler)block;
+
+/**
+ * Subscribe to a particular capability type with a selector callback. The selector supports the following parameters:
+ *
+ * 1. No parameters e.g. `- (void)phoneCapabilityUpdated;`
+ * 2. One `SDLSystemCapability *` parameter e.g. `- (void)phoneCapabilityUpdated:(SDLSystemCapability *)capability`
+ *
+ * This method will be called immediately with the current value and called every time the value is updated on RPC v5.1.0+ systems (`supportsSubscriptions == YES`). If this method is called on a sub-v5.1.0 system (`supportsSubscriptions == NO`), the method will return `NO` and the selector will never be called.
+ *
+ * @param type The type of the system capability to subscribe to
+ * @param observer The object that will have `selector` called whenever the capability is updated
+ * @param selector The selector on `observer` that will be called whenever the capability is updated
+ * @return Whether or not the subscription succeeded. `NO` if the connected system doesn't support capability subscriptions, or if the `selector` doesn't support the correct parameters (see above)
+ */
+- (BOOL)subscribeToCapabilityType:(SDLSystemCapabilityType)type withObserver:(id)observer selector:(SEL)selector;
+
+/**
+ * Unsubscribe from a particular capability type. If it was subscribed with a block, the return value should be passed to the `observer` to unsubscribe the block. If it was subscribed with a selector, the `observer` object should be passed to unsubscribe the object selector.
+ *
+ * @param type The type of the system capability to unsubscribe from
+ * @param observer The object that will be unsubscribed. If a block was subscribed, the return value should be passed. If a selector was subscribed, the observer object should be passed.
+ */
+- (void)unsubscribeFromCapabilityType:(SDLSystemCapabilityType)type withObserver:(id)observer;
@end
diff --git a/SmartDeviceLink/SDLSystemCapabilityManager.m b/SmartDeviceLink/SDLSystemCapabilityManager.m
index 0a48ce71f..82d7c8c82 100644
--- a/SmartDeviceLink/SDLSystemCapabilityManager.m
+++ b/SmartDeviceLink/SDLSystemCapabilityManager.m
@@ -12,19 +12,24 @@
#import "SDLAppServiceRecord.h"
#import "SDLAppServicesCapabilities.h"
#import "SDLConnectionManagerType.h"
+#import "SDLError.h"
#import "SDLGenericResponse.h"
#import "SDLGetSystemCapability.h"
#import "SDLGetSystemCapabilityResponse.h"
#import "SDLGlobals.h"
#import "SDLLogMacros.h"
+#import "SDLNavigationCapability.h"
#import "SDLNotificationConstants.h"
#import "SDLOnHMIStatus.h"
#import "SDLOnSystemCapabilityUpdated.h"
+#import "SDLPhoneCapability.h"
#import "SDLRegisterAppInterfaceResponse.h"
+#import "SDLRemoteControlCapabilities.h"
#import "SDLRPCNotificationNotification.h"
#import "SDLRPCResponseNotification.h"
#import "SDLSetDisplayLayoutResponse.h"
#import "SDLSystemCapability.h"
+#import "SDLSystemCapabilityObserver.h"
#import "SDLVersion.h"
#import "SDLVideoStreamingCapability.h"
@@ -55,6 +60,11 @@ typedef NSString * SDLServiceID;
@property (nullable, strong, nonatomic) NSMutableDictionary<SDLServiceID, SDLAppServiceCapability *> *appServicesCapabilitiesDictionary;
+@property (assign, nonatomic, readwrite) BOOL supportsSubscriptions;
+@property (strong, nonatomic) NSMutableDictionary<SDLSystemCapabilityType, NSMutableArray<SDLSystemCapabilityObserver *> *> *capabilityObservers;
+
+@property (nullable, strong, nonatomic) SDLSystemCapability *lastReceivedCapability;
+
@property (assign, nonatomic) BOOL isFirstHMILevelFull;
@end
@@ -73,11 +83,24 @@ typedef NSString * SDLServiceID;
_isFirstHMILevelFull = NO;
_appServicesCapabilitiesDictionary = [NSMutableDictionary dictionary];
+ _capabilityObservers = [NSMutableDictionary dictionary];
+ for (SDLSystemCapabilityType capabilityType in [self.class sdl_systemCapabilityTypes]) {
+ _capabilityObservers[capabilityType] = [NSMutableArray array];
+ }
+
[self sdl_registerForNotifications];
return self;
}
+- (void)start {
+ SDLVersion *onSystemCapabilityNotificationRPCVersion = [SDLVersion versionWithString:@"5.1.0"];
+ SDLVersion *headUnitRPCVersion = SDLGlobals.sharedGlobals.rpcVersion;
+ if ([headUnitRPCVersion isGreaterThanOrEqualToVersion:onSystemCapabilityNotificationRPCVersion]) {
+ _supportsSubscriptions = YES;
+ }
+}
+
/**
* Resets the capabilities when a transport session is closed.
*/
@@ -100,6 +123,11 @@ typedef NSString * SDLServiceID;
_remoteControlCapability = nil;
_appServicesCapabilitiesDictionary = [NSMutableDictionary dictionary];
+ _supportsSubscriptions = NO;
+ for (SDLSystemCapabilityType capabilityType in [self.class sdl_systemCapabilityTypes]) {
+ _capabilityObservers[capabilityType] = [NSMutableArray array];
+ }
+
_isFirstHMILevelFull = NO;
}
@@ -120,6 +148,7 @@ typedef NSString * SDLServiceID;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sdl_registerResponse:) name:SDLDidReceiveRegisterAppInterfaceResponse object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sdl_displayLayoutResponse:) name:SDLDidReceiveSetDisplayLayoutResponse object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sdl_systemCapabilityUpdatedNotification:) name:SDLDidReceiveSystemCapabilityUpdatedNotification object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sdl_systemCapabilityResponseNotification:) name:SDLDidReceiveGetSystemCapabilitiesResponse object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(sdl_hmiStatusNotification:) name:SDLDidChangeHMIStatusNotification object:nil];
}
@@ -171,6 +200,16 @@ typedef NSString * SDLServiceID;
}
/**
+ Called with a `GetSystemCapabilityResponse` notification is received from core. The updated system capability is saved.
+
+ @param notification The `GetSystemCapabilityResponse` notification received from Core
+ */
+- (void)sdl_systemCapabilityResponseNotification:(SDLRPCResponseNotification *)notification {
+ SDLGetSystemCapabilityResponse *systemCapabilityResponse = (SDLGetSystemCapabilityResponse *)notification.response;
+ [self sdl_saveSystemCapability:systemCapabilityResponse.systemCapability completionHandler:nil];
+}
+
+/**
* Called when an `OnHMIStatus` notification is received from Core. The first time the `hmiLevel` is `FULL` attempt to subscribe to system capabilty updates.
*
* @param notification The `OnHMIStatus` notification received from Core
@@ -188,9 +227,7 @@ typedef NSString * SDLServiceID;
#pragma mark - System Capabilities
- (void)updateCapabilityType:(SDLSystemCapabilityType)type completionHandler:(SDLUpdateCapabilityHandler)handler {
- SDLVersion *onSystemCapabilityNotificationRPCVersion = [SDLVersion versionWithString:@"5.1.0"];
- SDLVersion *headUnitRPCVersion = SDLGlobals.sharedGlobals.rpcVersion;
- if ([headUnitRPCVersion isGreaterThanOrEqualToVersion:onSystemCapabilityNotificationRPCVersion]) {
+ if (self.supportsSubscriptions) {
// Just return the cached data because we get `onSystemCapability` callbacks
handler(nil, self);
} else {
@@ -215,9 +252,7 @@ typedef NSString * SDLServiceID;
- (void)sdl_subscribeToSystemCapabilityUpdates {
for (SDLSystemCapabilityType type in [self.class sdl_systemCapabilityTypes]) {
SDLGetSystemCapability *getSystemCapability = [[SDLGetSystemCapability alloc] initWithType:type];
- SDLVersion *onSystemCapabilityNotificationRPCVersion = [SDLVersion versionWithString:@"5.1.0"];
- SDLVersion *headUnitRPCVersion = SDLGlobals.sharedGlobals.rpcVersion;
- if ([headUnitRPCVersion isGreaterThanOrEqualToVersion:onSystemCapabilityNotificationRPCVersion]) {
+ if (self.supportsSubscriptions) {
getSystemCapability.subscribe = @YES;
}
@@ -231,47 +266,87 @@ typedef NSString * SDLServiceID;
* @param getSystemCapability The `GetSystemCapability` request to send
*/
- (void)sdl_sendGetSystemCapability:(SDLGetSystemCapability *)getSystemCapability completionHandler:(nullable SDLUpdateCapabilityHandler)handler {
+ __weak typeof(self) weakSelf = self;
[self.connectionManager sendConnectionRequest:getSystemCapability withResponseHandler:^(__kindof SDLRPCRequest * _Nullable request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error) {
if (error != nil) {
// An error is returned if the request was unsuccessful or if a Generic Response was returned
if (handler == nil) { return; }
- handler(error, self);
+ handler(error, weakSelf);
return;
}
SDLGetSystemCapabilityResponse *getSystemCapabilityResponse = (SDLGetSystemCapabilityResponse *)response;
- if (!getSystemCapabilityResponse.success.boolValue) { return; }
- [self sdl_saveSystemCapability:getSystemCapabilityResponse.systemCapability completionHandler:handler];
+ [weakSelf sdl_saveSystemCapability:getSystemCapabilityResponse.systemCapability completionHandler:handler];
}];
}
/**
- * Saves a system capability. All system capabilities will update with the full object except for app services. For app services only the updated app service capabilities will be included in the `SystemCapability` sent from Core. The cached `appServicesCapabilities` will be updated with the new `appService` data.
- *
- * @param systemCapability The system capability
+ Saves a system capability. All system capabilities will update with the full object except for app services. For app services only the updated app service capabilities will be included in the `SystemCapability` sent from Core. The cached `appServicesCapabilities` will be updated with the new `appService` data.
+
+ @param systemCapability The system capability to be saved
+ @param handler The handler to be called when the save completes
+ @return Whether or not the save occurred. This can be `NO` if the new system capability is equivalent to the old capability.
*/
-- (void)sdl_saveSystemCapability:(SDLSystemCapability *)systemCapability completionHandler:(nullable SDLUpdateCapabilityHandler)handler {
+- (BOOL)sdl_saveSystemCapability:(SDLSystemCapability *)systemCapability completionHandler:(nullable SDLUpdateCapabilityHandler)handler {
+ if ([self.lastReceivedCapability isEqual:systemCapability]) {
+ return [self sdl_callSaveHandlerForCapability:systemCapability andReturnWithValue:NO handler:handler];
+ }
+ self.lastReceivedCapability = systemCapability;
+
SDLSystemCapabilityType systemCapabilityType = systemCapability.systemCapabilityType;
if ([systemCapabilityType isEqualToEnum:SDLSystemCapabilityTypePhoneCall]) {
+ if ([self.phoneCapability isEqual:systemCapability.phoneCapability]) { return [self sdl_callSaveHandlerForCapability:systemCapability andReturnWithValue:NO handler:handler]; }
self.phoneCapability = systemCapability.phoneCapability;
} else if ([systemCapabilityType isEqualToEnum:SDLSystemCapabilityTypeNavigation]) {
+ if ([self.navigationCapability isEqual:systemCapability.navigationCapability]) { return [self sdl_callSaveHandlerForCapability:systemCapability andReturnWithValue:NO handler:handler]; }
self.navigationCapability = systemCapability.navigationCapability;
} else if ([systemCapabilityType isEqualToEnum:SDLSystemCapabilityTypeRemoteControl]) {
+ if ([self.remoteControlCapability isEqual:systemCapability.remoteControlCapability]) { return [self sdl_callSaveHandlerForCapability:systemCapability andReturnWithValue:NO handler:handler]; }
self.remoteControlCapability = systemCapability.remoteControlCapability;
} else if ([systemCapabilityType isEqualToEnum:SDLSystemCapabilityTypeVideoStreaming]) {
+ if ([self.videoStreamingCapability isEqual:systemCapability.videoStreamingCapability]) { return [self sdl_callSaveHandlerForCapability:systemCapability andReturnWithValue:NO handler:handler]; }
self.videoStreamingCapability = systemCapability.videoStreamingCapability;
} else if ([systemCapabilityType isEqualToEnum:SDLSystemCapabilityTypeAppServices]) {
[self sdl_saveAppServicesCapabilitiesUpdate:systemCapability.appServicesCapabilities];
+ systemCapability = [[SDLSystemCapability alloc] initWithAppServicesCapabilities:self.appServicesCapabilities];
} else {
SDLLogW(@"Received response for unknown System Capability Type: %@", systemCapabilityType);
- return;
+ return NO;
}
SDLLogD(@"Updated system capability manager with new data: %@", systemCapability);
- if (handler == nil) { return; }
+ return [self sdl_callSaveHandlerForCapability:systemCapability andReturnWithValue:YES handler:handler];
+}
+
+- (BOOL)sdl_callSaveHandlerForCapability:(SDLSystemCapability *)capability andReturnWithValue:(BOOL)value handler:(nullable SDLUpdateCapabilityHandler)handler {
+ for (SDLSystemCapabilityObserver *observer in self.capabilityObservers[capability.systemCapabilityType]) {
+ if (observer.block != nil) {
+ observer.block(capability);
+ } else {
+ NSUInteger numberOfParametersInSelector = [NSStringFromSelector(observer.selector) componentsSeparatedByString:@":"].count - 1;
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
+ if (numberOfParametersInSelector == 0) {
+ if ([observer.observer respondsToSelector:observer.selector]) {
+ [observer.observer performSelector:observer.selector];
+ }
+ } else if (numberOfParametersInSelector == 1) {
+ if ([observer.observer respondsToSelector:observer.selector]) {
+ [observer.observer performSelector:observer.selector withObject:capability];
+ }
+ } else {
+ @throw [NSException sdl_invalidSelectorExceptionWithSelector:observer.selector];
+ }
+#pragma clang diagnostic pop
+ }
+ }
+
+ if (handler == nil) { return value; }
handler(nil, self);
+
+ return value;
}
- (void)sdl_saveAppServicesCapabilitiesUpdate:(SDLAppServicesCapabilities *)newCapabilities {
@@ -288,6 +363,38 @@ typedef NSString * SDLServiceID;
}
}
+#pragma mark - Subscriptions
+
+- (nullable id<NSObject>)subscribeToCapabilityType:(SDLSystemCapabilityType)type usingBlock:(SDLCapabilityUpdateHandler)block {
+ if (!self.supportsSubscriptions) { return nil; }
+
+ SDLSystemCapabilityObserver *observerObject = [[SDLSystemCapabilityObserver alloc] initWithObserver:[[NSObject alloc] init] block:block];
+ [self.capabilityObservers[type] addObject:observerObject];
+
+ return observerObject.observer;
+}
+
+- (BOOL)subscribeToCapabilityType:(SDLSystemCapabilityType)type withObserver:(id<NSObject>)observer selector:(SEL)selector {
+ if (!self.supportsSubscriptions) { return NO; }
+
+ NSUInteger numberOfParametersInSelector = [NSStringFromSelector(selector) componentsSeparatedByString:@":"].count - 1;
+ if (numberOfParametersInSelector > 1) { return NO; }
+
+ SDLSystemCapabilityObserver *observerObject = [[SDLSystemCapabilityObserver alloc] initWithObserver:observer selector:selector];
+ [self.capabilityObservers[type] addObject:observerObject];
+
+ return observerObject.observer;
+}
+
+- (void)unsubscribeFromCapabilityType:(SDLSystemCapabilityType)type withObserver:(id)observer {
+ for (SDLSystemCapabilityObserver *capabilityObserver in self.capabilityObservers[type]) {
+ if ([observer isEqual:capabilityObserver.observer]) {
+ [self.capabilityObservers[type] removeObject:capabilityObserver];
+ break;
+ }
+ }
+}
+
@end
NS_ASSUME_NONNULL_END
diff --git a/SmartDeviceLink/SDLSystemCapabilityObserver.h b/SmartDeviceLink/SDLSystemCapabilityObserver.h
new file mode 100644
index 000000000..f5450d60e
--- /dev/null
+++ b/SmartDeviceLink/SDLSystemCapabilityObserver.h
@@ -0,0 +1,57 @@
+//
+// SDLSystemCapabilityObserver.h
+// SmartDeviceLink
+//
+// Created by Joel Fischer on 5/23/19.
+// Copyright © 2019 smartdevicelink. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@class SDLSystemCapability;
+
+NS_ASSUME_NONNULL_BEGIN
+
+typedef void (^SDLCapabilityUpdateHandler)(SDLSystemCapability *capability);
+
+/**
+ An observer object for SDLSystemCapabilityManager
+ */
+@interface SDLSystemCapabilityObserver : NSObject
+
+/**
+ The object that will be used to call the selector if available, and to unsubscribe this observer
+ */
+@property (strong, nonatomic) id<NSObject> observer;
+
+/**
+ A selector called when the observer is triggered
+ */
+@property (assign, nonatomic) SEL selector;
+
+/**
+ A block called when the observer is triggered
+ */
+@property (copy, nonatomic) SDLCapabilityUpdateHandler block;
+
+/**
+ Create an observer using an object and a selector on that object
+
+ @param observer The object to be called when the subscription triggers
+ @param selector The selector to be called when the subscription triggers
+ @return The observer
+ */
+- (instancetype)initWithObserver:(id<NSObject>)observer selector:(SEL)selector;
+
+/**
+ Create an observer using an object and a callback block
+
+ @param observer The object that can be used to unsubscribe the block
+ @param block The block that will be called when the subscription triggers
+ @return The observer
+ */
+- (instancetype)initWithObserver:(id<NSObject>)observer block:(SDLCapabilityUpdateHandler)block;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/SmartDeviceLink/SDLSystemCapabilityObserver.m b/SmartDeviceLink/SDLSystemCapabilityObserver.m
new file mode 100644
index 000000000..64832f1a7
--- /dev/null
+++ b/SmartDeviceLink/SDLSystemCapabilityObserver.m
@@ -0,0 +1,37 @@
+//
+// SDLSystemCapabilityObserver.m
+// SmartDeviceLink
+//
+// Created by Joel Fischer on 5/23/19.
+// Copyright © 2019 smartdevicelink. All rights reserved.
+//
+
+#import "SDLSystemCapabilityObserver.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@implementation SDLSystemCapabilityObserver
+
+- (instancetype)initWithObserver:(id<NSObject>)observer selector:(SEL)selector {
+ self = [super init];
+ if (!self) { return nil; }
+
+ _observer = observer;
+ _selector = selector;
+
+ return self;
+}
+
+- (instancetype)initWithObserver:(id<NSObject>)observer block:(SDLCapabilityUpdateHandler)block {
+ self = [super init];
+ if (!self) { return nil; }
+
+ _observer = observer;
+ _block = block;
+
+ return self;
+}
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/SmartDeviceLinkTests/DevAPISpecs/TestSystemCapabilityObserver.h b/SmartDeviceLinkTests/DevAPISpecs/TestSystemCapabilityObserver.h
new file mode 100644
index 000000000..0e6c8bcbb
--- /dev/null
+++ b/SmartDeviceLinkTests/DevAPISpecs/TestSystemCapabilityObserver.h
@@ -0,0 +1,24 @@
+//
+// TestSystemCapabilityObserver.h
+// SmartDeviceLinkTests
+//
+// Created by Joel Fischer on 5/23/19.
+// Copyright © 2019 smartdevicelink. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@class SDLSystemCapabilityManager;
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface TestSystemCapabilityObserver : NSObject
+
+@property (assign, nonatomic) NSUInteger selectorCalledCount;
+
+- (void)capabilityUpdated;
+- (void)capabilityUpdatedWithNotification:(SDLSystemCapabilityManager *)capabilityManager;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/SmartDeviceLinkTests/DevAPISpecs/TestSystemCapabilityObserver.m b/SmartDeviceLinkTests/DevAPISpecs/TestSystemCapabilityObserver.m
new file mode 100644
index 000000000..5366b0884
--- /dev/null
+++ b/SmartDeviceLinkTests/DevAPISpecs/TestSystemCapabilityObserver.m
@@ -0,0 +1,30 @@
+//
+// TestSystemCapabilityObserver.m
+// SmartDeviceLinkTests
+//
+// Created by Joel Fischer on 5/23/19.
+// Copyright © 2019 smartdevicelink. All rights reserved.
+//
+
+#import "TestSystemCapabilityObserver.h"
+
+@implementation TestSystemCapabilityObserver
+
+- (instancetype)init {
+ self = [super init];
+ if (!self) { return nil; }
+
+ _selectorCalledCount = 0;
+
+ return self;
+}
+
+- (void)capabilityUpdated {
+ self.selectorCalledCount++;
+}
+
+- (void)capabilityUpdatedWithNotification:(SDLSystemCapabilityManager *)capabilityManager {
+ self.selectorCalledCount++;
+}
+
+@end
diff --git a/SmartDeviceLinkTests/SDLSystemCapabilityManagerSpec.m b/SmartDeviceLinkTests/SDLSystemCapabilityManagerSpec.m
index 109b8ea2d..4fc9a6547 100644
--- a/SmartDeviceLinkTests/SDLSystemCapabilityManagerSpec.m
+++ b/SmartDeviceLinkTests/SDLSystemCapabilityManagerSpec.m
@@ -29,6 +29,14 @@
#import "SDLSystemCapabilityManager.h"
#import "SDLVideoStreamingCapability.h"
#import "TestConnectionManager.h"
+#import "TestSystemCapabilityObserver.h"
+
+
+@interface SDLSystemCapabilityManager ()
+
+@property (assign, nonatomic, readwrite) BOOL supportsSubscriptions;
+
+@end
QuickSpecBegin(SDLSystemCapabilityManagerSpec)
@@ -181,7 +189,7 @@ describe(@"System capability manager", ^{
});
});
- context(@"When notified of a Set Display Layout Response", ^ {
+ context(@"When notified of a SetDisplayLayout Response", ^ {
__block SDLSetDisplayLayoutResponse *testSetDisplayLayoutResponse = nil;
__block SDLDisplayCapabilities *testDisplayCapabilities = nil;
__block NSArray<SDLSoftButtonCapabilities *> *testSoftButtonCapabilities = nil;
@@ -216,7 +224,7 @@ describe(@"System capability manager", ^{
testSetDisplayLayoutResponse.presetBankCapabilities = testPresetBankCapabilities;
});
- describe(@"If the Set Display Layout request fails", ^{
+ describe(@"If the SetDisplayLayout request fails", ^{
beforeEach(^{
testSetDisplayLayoutResponse.success = @NO;
SDLRPCResponseNotification *notification = [[SDLRPCResponseNotification alloc] initWithName:SDLDidReceiveSetDisplayLayoutResponse object:self rpcResponse:testSetDisplayLayoutResponse];
@@ -231,7 +239,7 @@ describe(@"System capability manager", ^{
});
});
- describe(@"If the Set Display Layout request succeeds", ^{
+ describe(@"If the SetDisplayLayout request succeeds", ^{
beforeEach(^{
testSetDisplayLayoutResponse.success = @YES;
SDLRPCResponseNotification *notification = [[SDLRPCResponseNotification alloc] initWithName:SDLDidReceiveSetDisplayLayoutResponse object:self rpcResponse:testSetDisplayLayoutResponse];
@@ -263,8 +271,8 @@ describe(@"System capability manager", ^{
});
});
- context(@"When sending a Get System Capability request", ^{
- __block SDLGetSystemCapabilityResponse *testGetSystemCapabilityResponse;
+ context(@"When sending a GetSystemCapability request", ^{
+ __block SDLGetSystemCapabilityResponse *testGetSystemCapabilityResponse = nil;
__block SDLPhoneCapability *testPhoneCapability = nil;
beforeEach(^{
@@ -347,6 +355,7 @@ describe(@"System capability manager", ^{
SDLSystemCapability *newCapability = [[SDLSystemCapability alloc] initWithPhoneCapability:phoneCapability];
SDLOnSystemCapabilityUpdated *update = [[SDLOnSystemCapabilityUpdated alloc] initWithSystemCapability:newCapability];
SDLRPCNotificationNotification *notification = [[SDLRPCNotificationNotification alloc] initWithName:SDLDidReceiveSystemCapabilityUpdatedNotification object:nil rpcNotification:update];
+
[[NSNotificationCenter defaultCenter] postNotification:notification];
});
@@ -355,6 +364,118 @@ describe(@"System capability manager", ^{
});
});
+ describe(@"subscribing to capability types", ^{
+ __block TestSystemCapabilityObserver *phoneObserver = nil;
+ __block TestSystemCapabilityObserver *navigationObserver = nil;
+
+ __block NSUInteger blockObserverTriggeredCount = 0;
+
+ beforeEach(^{
+ blockObserverTriggeredCount = 0;
+ testSystemCapabilityManager.supportsSubscriptions = YES;
+
+ phoneObserver = [[TestSystemCapabilityObserver alloc] init];
+ [testSystemCapabilityManager subscribeToCapabilityType:SDLSystemCapabilityTypePhoneCall withObserver:phoneObserver selector:@selector(capabilityUpdatedWithNotification:)];
+ navigationObserver = [[TestSystemCapabilityObserver alloc] init];
+ [testSystemCapabilityManager subscribeToCapabilityType:SDLSystemCapabilityTypeNavigation withObserver:navigationObserver selector:@selector(capabilityUpdatedWithNotification:)];
+ });
+
+ describe(@"when observers aren't supported", ^{
+ __block BOOL observationSuccess = NO;
+
+ beforeEach(^{
+ testSystemCapabilityManager.supportsSubscriptions = NO;
+
+ observationSuccess = [testSystemCapabilityManager subscribeToCapabilityType:SDLSystemCapabilityTypePhoneCall withObserver:phoneObserver selector:@selector(capabilityUpdatedWithNotification:)];
+ });
+
+ it(@"should fail to subscribe", ^{
+ expect(observationSuccess).to(beFalse());
+ });
+ });
+
+ context(@"from a GetSystemCapabilitiesResponse", ^{
+ __block id blockObserver = nil;
+
+ beforeEach(^{
+ blockObserver = [testSystemCapabilityManager subscribeToCapabilityType:SDLSystemCapabilityTypePhoneCall usingBlock:^(SDLSystemCapability * _Nonnull systemCapability) {
+ blockObserverTriggeredCount++;
+ }];
+
+ SDLGetSystemCapabilityResponse *testResponse = [[SDLGetSystemCapabilityResponse alloc] init];
+ testResponse.systemCapability = [[SDLSystemCapability alloc] initWithPhoneCapability:[[SDLPhoneCapability alloc] initWithDialNumber:YES]];
+ SDLRPCResponseNotification *notification = [[SDLRPCResponseNotification alloc] initWithName:SDLDidReceiveGetSystemCapabilitiesResponse object:nil rpcResponse:testResponse];
+
+ [[NSNotificationCenter defaultCenter] postNotification:notification];
+ });
+
+ it(@"should notify subscribers of the new data", ^{
+ expect(phoneObserver.selectorCalledCount).toEventually(equal(1));
+ expect(blockObserverTriggeredCount).toEventually(equal(1));
+ expect(navigationObserver.selectorCalledCount).toEventually(equal(0));
+ });
+
+ describe(@"unsubscribing", ^{
+ beforeEach(^{
+ [testSystemCapabilityManager unsubscribeFromCapabilityType:SDLSystemCapabilityTypePhoneCall withObserver:phoneObserver];
+ [testSystemCapabilityManager unsubscribeFromCapabilityType:SDLSystemCapabilityTypePhoneCall withObserver:blockObserver];
+
+ SDLGetSystemCapabilityResponse *testResponse = [[SDLGetSystemCapabilityResponse alloc] init];
+ testResponse.systemCapability = [[SDLSystemCapability alloc] initWithPhoneCapability:[[SDLPhoneCapability alloc] initWithDialNumber:YES]];
+ SDLRPCResponseNotification *notification = [[SDLRPCResponseNotification alloc] initWithName:SDLDidReceiveGetSystemCapabilitiesResponse object:nil rpcResponse:testResponse];
+
+ [[NSNotificationCenter defaultCenter] postNotification:notification];
+ });
+
+ it(@"should not notify the subscriber of the new data", ^{
+ expect(phoneObserver.selectorCalledCount).toEventually(equal(1)); // No change from above
+ expect(blockObserverTriggeredCount).toEventually(equal(1));
+ expect(navigationObserver.selectorCalledCount).toEventually(equal(0));
+ });
+ });
+ });
+
+ context(@"from an OnSystemCapabilities notification", ^{
+ __block id blockObserver = nil;
+
+ beforeEach(^{
+ blockObserver = [testSystemCapabilityManager subscribeToCapabilityType:SDLSystemCapabilityTypePhoneCall usingBlock:^(SDLSystemCapability * _Nonnull systemCapability) {
+ blockObserverTriggeredCount++;
+ }];
+
+ SDLOnSystemCapabilityUpdated *testNotification = [[SDLOnSystemCapabilityUpdated alloc] initWithSystemCapability:[[SDLSystemCapability alloc] initWithPhoneCapability:[[SDLPhoneCapability alloc] initWithDialNumber:YES]]];
+ SDLRPCNotificationNotification *notification = [[SDLRPCNotificationNotification alloc] initWithName:SDLDidReceiveSystemCapabilityUpdatedNotification object:nil rpcNotification:testNotification];
+
+ [[NSNotificationCenter defaultCenter] postNotification:notification];
+ });
+
+ it(@"should notify subscribers of the new data", ^{
+ expect(phoneObserver.selectorCalledCount).toEventually(equal(1));
+ expect(blockObserverTriggeredCount).toEventually(equal(1));
+ expect(navigationObserver.selectorCalledCount).toEventually(equal(0));
+ });
+
+ describe(@"unsubscribing", ^{
+ beforeEach(^{
+ [testSystemCapabilityManager unsubscribeFromCapabilityType:SDLSystemCapabilityTypePhoneCall withObserver:phoneObserver];
+ [testSystemCapabilityManager unsubscribeFromCapabilityType:SDLSystemCapabilityTypePhoneCall withObserver:blockObserver];
+
+ SDLGetSystemCapabilityResponse *testResponse = [[SDLGetSystemCapabilityResponse alloc] init];
+ testResponse.systemCapability = [[SDLSystemCapability alloc] initWithPhoneCapability:[[SDLPhoneCapability alloc] initWithDialNumber:YES]];
+ SDLRPCResponseNotification *notification = [[SDLRPCResponseNotification alloc] initWithName:SDLDidReceiveGetSystemCapabilitiesResponse object:nil rpcResponse:testResponse];
+
+ [[NSNotificationCenter defaultCenter] postNotification:notification];
+ });
+
+ it(@"should not notify the subscriber of the new data", ^{
+ expect(phoneObserver.selectorCalledCount).toEventually(equal(1)); // No change from above
+ expect(blockObserverTriggeredCount).toEventually(equal(1));
+ expect(navigationObserver.selectorCalledCount).toEventually(equal(0));
+ });
+ });
+ });
+ });
+
describe(@"merging app services capability changes", ^{
__block SDLAppServicesCapabilities *baseAppServices = nil;
__block SDLAppServiceCapability *deleteCapability = nil;