summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--SmartDeviceLink-iOS.xcodeproj/project.pbxproj80
-rw-r--r--SmartDeviceLink/SDLAsynchronousRPCRequestOperation.h26
-rw-r--r--SmartDeviceLink/SDLAsynchronousRPCRequestOperation.m165
-rw-r--r--SmartDeviceLink/SDLConnectionManagerType.h10
-rw-r--r--SmartDeviceLink/SDLDeleteFileOperation.m2
-rw-r--r--SmartDeviceLink/SDLError.h1
-rw-r--r--SmartDeviceLink/SDLError.m6
-rw-r--r--SmartDeviceLink/SDLErrorConstants.h6
-rw-r--r--SmartDeviceLink/SDLFocusableItemLocator.m2
-rw-r--r--SmartDeviceLink/SDLLifecycleManager.h24
-rw-r--r--SmartDeviceLink/SDLLifecycleManager.m182
-rw-r--r--SmartDeviceLink/SDLListFilesOperation.m4
-rw-r--r--SmartDeviceLink/SDLManager.h52
-rw-r--r--SmartDeviceLink/SDLManager.m12
-rw-r--r--SmartDeviceLink/SDLSequentialRPCRequestOperation.h24
-rw-r--r--SmartDeviceLink/SDLSequentialRPCRequestOperation.m124
-rw-r--r--SmartDeviceLink/SDLStreamingMediaLifecycleManager.m6
-rw-r--r--SmartDeviceLink/SDLTouchManager.m36
-rw-r--r--SmartDeviceLink/SDLUploadFileOperation.m2
-rw-r--r--SmartDeviceLinkTests/DevAPISpecs/SDLAsynchronousRPCRequestOperationSpec.m105
-rw-r--r--SmartDeviceLinkTests/DevAPISpecs/SDLFileManagerSpec.m51
-rw-r--r--SmartDeviceLinkTests/DevAPISpecs/SDLLifecycleManagerSpec.m17
-rw-r--r--SmartDeviceLinkTests/DevAPISpecs/SDLSequentialRPCRequestOperationSpec.m136
-rw-r--r--SmartDeviceLinkTests/ProxySpecs/SDLHapticManagerSpec.m2
-rw-r--r--SmartDeviceLinkTests/TestFileProgressResponse.h (renamed from SmartDeviceLinkTests/TestProgressResponse.h)4
-rw-r--r--SmartDeviceLinkTests/TestFileProgressResponse.m (renamed from SmartDeviceLinkTests/TestProgressResponse.m)4
-rw-r--r--SmartDeviceLinkTests/TestMultipleFilesConnectionManager.m4
-rw-r--r--SmartDeviceLinkTests/TestUtilities/SDLSpecUtilities.h20
-rw-r--r--SmartDeviceLinkTests/TestUtilities/SDLSpecUtilities.m28
-rw-r--r--SmartDeviceLinkTests/TestUtilities/TestConnectionManager.m8
-rw-r--r--SmartDeviceLinkTests/TestUtilities/TestMultipleRequestsConnectionManager.h20
-rw-r--r--SmartDeviceLinkTests/TestUtilities/TestMultipleRequestsConnectionManager.m39
-rw-r--r--SmartDeviceLinkTests/TestUtilities/TestRequestProgressResponse.h21
-rw-r--r--SmartDeviceLinkTests/TestUtilities/TestRequestProgressResponse.m24
-rw-r--r--SmartDeviceLink_Example/Classes/ProxyManager.m129
35 files changed, 1156 insertions, 220 deletions
diff --git a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj
index 49b0a07a7..d98084a9e 100644
--- a/SmartDeviceLink-iOS.xcodeproj/project.pbxproj
+++ b/SmartDeviceLink-iOS.xcodeproj/project.pbxproj
@@ -382,6 +382,11 @@
5D5DBF0B1D48E5E600D4F914 /* SDLLockScreenViewControllerSnapshotTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D5DBF0A1D48E5E600D4F914 /* SDLLockScreenViewControllerSnapshotTests.m */; };
5D60088A1BE3ED540094A505 /* SDLStateMachine.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D6008881BE3ED540094A505 /* SDLStateMachine.h */; };
5D60088B1BE3ED540094A505 /* SDLStateMachine.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D6008891BE3ED540094A505 /* SDLStateMachine.m */; };
+ 5D6035D2202CD46200A429C9 /* SDLSpecUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D6035D1202CD46200A429C9 /* SDLSpecUtilities.m */; };
+ 5D6035D5202CE4A500A429C9 /* TestMultipleRequestsConnectionManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D6035D4202CE4A500A429C9 /* TestMultipleRequestsConnectionManager.m */; };
+ 5D6035D8202CF5C900A429C9 /* TestRequestProgressResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D6035D7202CF5C900A429C9 /* TestRequestProgressResponse.m */; };
+ 5D60DF24202B7A80001EDA01 /* SDLAsynchronousRPCRequestOperationSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D60DF23202B7A80001EDA01 /* SDLAsynchronousRPCRequestOperationSpec.m */; };
+ 5D60DF26202B7A97001EDA01 /* SDLSequentialRPCRequestOperationSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D60DF25202B7A97001EDA01 /* SDLSequentialRPCRequestOperationSpec.m */; };
5D616B451D552F7A00553F6B /* SDLLockScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5D616B481D552F7A00553F6B /* SDLLockScreen.storyboard */; };
5D616B461D552F7A00553F6B /* SDLLockScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5D616B481D552F7A00553F6B /* SDLLockScreen.storyboard */; };
5D616B531D59044400553F6B /* SDLErrorConstants.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D616B511D59044400553F6B /* SDLErrorConstants.h */; settings = {ATTRIBUTES = (Public, ); }; };
@@ -925,11 +930,9 @@
5DA102A51D4122C700C15826 /* NSMutableDictionary+SafeRemove.m in Sources */ = {isa = PBXBuildFile; fileRef = 5DA102A31D4122C700C15826 /* NSMutableDictionary+SafeRemove.m */; };
5DA22CB71D075CF200245F5F /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5DA22CB31D075CF200245F5F /* Nimble.framework */; };
5DA22CB81D075CF200245F5F /* OCMock.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5DA22CB41D075CF200245F5F /* OCMock.framework */; };
- 5DA22CB91D075CF200245F5F /* OHHTTPStubs.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5DA22CB51D075CF200245F5F /* OHHTTPStubs.framework */; };
5DA22CBA1D075CF200245F5F /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5DA22CB61D075CF200245F5F /* Quick.framework */; };
5DA22CBB1D075DE800245F5F /* Nimble.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 5DA22CB31D075CF200245F5F /* Nimble.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
5DA22CBC1D075DE800245F5F /* OCMock.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 5DA22CB41D075CF200245F5F /* OCMock.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
- 5DA22CBD1D075DE800245F5F /* OHHTTPStubs.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 5DA22CB51D075CF200245F5F /* OHHTTPStubs.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
5DA22CBE1D075DE800245F5F /* Quick.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 5DA22CB61D075CF200245F5F /* Quick.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
5DA22CBF1D075DEC00245F5F /* SmartDeviceLink.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 5D61FA1C1A84237100846EE7 /* SmartDeviceLink.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
5DA23FF01F2FA0FF009C0313 /* SDLControlFramePayloadEndServiceSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 5DA23FEF1F2FA0FF009C0313 /* SDLControlFramePayloadEndServiceSpec.m */; };
@@ -956,6 +959,10 @@
5DADA7781F4E059E0084D17D /* SDLRectangleSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 5DADA7771F4E059E0084D17D /* SDLRectangleSpec.m */; };
5DAE06731BDEC6C000F9B498 /* SDLFileSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE06721BDEC6C000F9B498 /* SDLFileSpec.m */; };
5DAE06751BDEC6D600F9B498 /* SDLArtworkSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE06741BDEC6D600F9B498 /* SDLArtworkSpec.m */; };
+ 5DB16144202115FC00F310DF /* SDLAsynchronousRPCRequestOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 5DB16142202115FC00F310DF /* SDLAsynchronousRPCRequestOperation.h */; };
+ 5DB16145202115FC00F310DF /* SDLAsynchronousRPCRequestOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 5DB16143202115FC00F310DF /* SDLAsynchronousRPCRequestOperation.m */; };
+ 5DB1616C2022276A00F310DF /* SDLSequentialRPCRequestOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 5DB1616A2022276A00F310DF /* SDLSequentialRPCRequestOperation.h */; };
+ 5DB1616D2022276A00F310DF /* SDLSequentialRPCRequestOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 5DB1616B2022276A00F310DF /* SDLSequentialRPCRequestOperation.m */; };
5DB1BCD31D243A8E002FFC37 /* SDLDeleteFileOperationSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 5DB1BCD01D243A8E002FFC37 /* SDLDeleteFileOperationSpec.m */; };
5DB1BCD41D243A8E002FFC37 /* SDLListFilesOperationSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 5DB1BCD11D243A8E002FFC37 /* SDLListFilesOperationSpec.m */; };
5DB1BCD51D243A8E002FFC37 /* SDLUploadFileOperationSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 5DB1BCD21D243A8E002FFC37 /* SDLUploadFileOperationSpec.m */; };
@@ -1034,7 +1041,7 @@
8877F5EF1F34A72200DC128A /* SDLSendHapticDataResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 8877F5ED1F34A72200DC128A /* SDLSendHapticDataResponse.m */; };
8877F5F11F34AA2D00DC128A /* SDLSendHapticDataResponseSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 8877F5F01F34AA2D00DC128A /* SDLSendHapticDataResponseSpec.m */; };
88B848C31F45E1A600DED768 /* TestResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 88B848C21F45E1A600DED768 /* TestResponse.m */; };
- 88B848C91F462E3600DED768 /* TestProgressResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 88B848C81F462E3600DED768 /* TestProgressResponse.m */; };
+ 88B848C91F462E3600DED768 /* TestFileProgressResponse.m in Sources */ = {isa = PBXBuildFile; fileRef = 88B848C81F462E3600DED768 /* TestFileProgressResponse.m */; };
88D2AAE41F682BB20078D5B2 /* SDLLogConstantsSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 88D2AAE31F682BB20078D5B2 /* SDLLogConstantsSpec.m */; };
88EED8381F33AE1700E6C42E /* SDLHapticRect.h in Headers */ = {isa = PBXBuildFile; fileRef = 88EED8361F33AE1700E6C42E /* SDLHapticRect.h */; settings = {ATTRIBUTES = (Public, ); }; };
88EED8391F33AE1700E6C42E /* SDLHapticRect.m in Sources */ = {isa = PBXBuildFile; fileRef = 88EED8371F33AE1700E6C42E /* SDLHapticRect.m */; };
@@ -1205,7 +1212,6 @@
5DA22CBF1D075DEC00245F5F /* SmartDeviceLink.framework in CopyFiles */,
5DA22CBB1D075DE800245F5F /* Nimble.framework in CopyFiles */,
5DA22CBC1D075DE800245F5F /* OCMock.framework in CopyFiles */,
- 5DA22CBD1D075DE800245F5F /* OHHTTPStubs.framework in CopyFiles */,
5DA22CBE1D075DE800245F5F /* Quick.framework in CopyFiles */,
5D5DBF091D48E3AC00D4F914 /* FBSnapshotTestCase.framework in CopyFiles */,
);
@@ -1608,6 +1614,14 @@
5D5DBF0A1D48E5E600D4F914 /* SDLLockScreenViewControllerSnapshotTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDLLockScreenViewControllerSnapshotTests.m; path = DevAPISpecs/SDLLockScreenViewControllerSnapshotTests.m; sourceTree = "<group>"; };
5D6008881BE3ED540094A505 /* SDLStateMachine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDLStateMachine.h; sourceTree = "<group>"; };
5D6008891BE3ED540094A505 /* SDLStateMachine.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDLStateMachine.m; sourceTree = "<group>"; };
+ 5D6035D0202CD46200A429C9 /* SDLSpecUtilities.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SDLSpecUtilities.h; path = TestUtilities/SDLSpecUtilities.h; sourceTree = "<group>"; };
+ 5D6035D1202CD46200A429C9 /* SDLSpecUtilities.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLSpecUtilities.m; path = TestUtilities/SDLSpecUtilities.m; sourceTree = "<group>"; };
+ 5D6035D3202CE4A500A429C9 /* TestMultipleRequestsConnectionManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = TestMultipleRequestsConnectionManager.h; path = TestUtilities/TestMultipleRequestsConnectionManager.h; sourceTree = "<group>"; };
+ 5D6035D4202CE4A500A429C9 /* TestMultipleRequestsConnectionManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = TestMultipleRequestsConnectionManager.m; path = TestUtilities/TestMultipleRequestsConnectionManager.m; sourceTree = "<group>"; };
+ 5D6035D6202CF5C900A429C9 /* TestRequestProgressResponse.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = TestRequestProgressResponse.h; path = TestUtilities/TestRequestProgressResponse.h; sourceTree = "<group>"; };
+ 5D6035D7202CF5C900A429C9 /* TestRequestProgressResponse.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = TestRequestProgressResponse.m; path = TestUtilities/TestRequestProgressResponse.m; sourceTree = "<group>"; };
+ 5D60DF23202B7A80001EDA01 /* SDLAsynchronousRPCRequestOperationSpec.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLAsynchronousRPCRequestOperationSpec.m; path = DevAPISpecs/SDLAsynchronousRPCRequestOperationSpec.m; sourceTree = "<group>"; };
+ 5D60DF25202B7A97001EDA01 /* SDLSequentialRPCRequestOperationSpec.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDLSequentialRPCRequestOperationSpec.m; path = DevAPISpecs/SDLSequentialRPCRequestOperationSpec.m; sourceTree = "<group>"; };
5D616B471D552F7A00553F6B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/SDLLockScreen.storyboard; sourceTree = "<group>"; };
5D616B511D59044400553F6B /* SDLErrorConstants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDLErrorConstants.h; sourceTree = "<group>"; };
5D616B561D5A233100553F6B /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/SDLLockScreen.strings; sourceTree = "<group>"; };
@@ -2185,6 +2199,10 @@
5DADA7771F4E059E0084D17D /* SDLRectangleSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDLRectangleSpec.m; sourceTree = "<group>"; };
5DAE06721BDEC6C000F9B498 /* SDLFileSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDLFileSpec.m; path = DevAPISpecs/SDLFileSpec.m; sourceTree = "<group>"; };
5DAE06741BDEC6D600F9B498 /* SDLArtworkSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDLArtworkSpec.m; path = DevAPISpecs/SDLArtworkSpec.m; sourceTree = "<group>"; };
+ 5DB16142202115FC00F310DF /* SDLAsynchronousRPCRequestOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDLAsynchronousRPCRequestOperation.h; sourceTree = "<group>"; };
+ 5DB16143202115FC00F310DF /* SDLAsynchronousRPCRequestOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDLAsynchronousRPCRequestOperation.m; sourceTree = "<group>"; };
+ 5DB1616A2022276A00F310DF /* SDLSequentialRPCRequestOperation.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDLSequentialRPCRequestOperation.h; sourceTree = "<group>"; };
+ 5DB1616B2022276A00F310DF /* SDLSequentialRPCRequestOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDLSequentialRPCRequestOperation.m; sourceTree = "<group>"; };
5DB1BCD01D243A8E002FFC37 /* SDLDeleteFileOperationSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDLDeleteFileOperationSpec.m; path = DevAPISpecs/SDLDeleteFileOperationSpec.m; sourceTree = "<group>"; };
5DB1BCD11D243A8E002FFC37 /* SDLListFilesOperationSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDLListFilesOperationSpec.m; path = DevAPISpecs/SDLListFilesOperationSpec.m; sourceTree = "<group>"; };
5DB1BCD21D243A8E002FFC37 /* SDLUploadFileOperationSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDLUploadFileOperationSpec.m; path = DevAPISpecs/SDLUploadFileOperationSpec.m; sourceTree = "<group>"; };
@@ -2271,8 +2289,8 @@
8877F5F01F34AA2D00DC128A /* SDLSendHapticDataResponseSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDLSendHapticDataResponseSpec.m; sourceTree = "<group>"; };
88B848C11F45E1A600DED768 /* TestResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TestResponse.h; sourceTree = "<group>"; };
88B848C21F45E1A600DED768 /* TestResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TestResponse.m; sourceTree = "<group>"; };
- 88B848C71F462E3600DED768 /* TestProgressResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TestProgressResponse.h; sourceTree = "<group>"; };
- 88B848C81F462E3600DED768 /* TestProgressResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TestProgressResponse.m; sourceTree = "<group>"; };
+ 88B848C71F462E3600DED768 /* TestFileProgressResponse.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TestFileProgressResponse.h; sourceTree = "<group>"; };
+ 88B848C81F462E3600DED768 /* TestFileProgressResponse.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = TestFileProgressResponse.m; sourceTree = "<group>"; };
88D2AAE31F682BB20078D5B2 /* SDLLogConstantsSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDLLogConstantsSpec.m; sourceTree = "<group>"; };
88EED8361F33AE1700E6C42E /* SDLHapticRect.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDLHapticRect.h; sourceTree = "<group>"; };
88EED8371F33AE1700E6C42E /* SDLHapticRect.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDLHapticRect.m; sourceTree = "<group>"; };
@@ -2421,7 +2439,6 @@
buildActionMask = 2147483647;
files = (
167ED93C1A9BCB8A00797BE5 /* SmartDeviceLink.framework in Frameworks */,
- 5DA22CB91D075CF200245F5F /* OHHTTPStubs.framework in Frameworks */,
5DA22CB81D075CF200245F5F /* OCMock.framework in Frameworks */,
5DA22CBA1D075CF200245F5F /* Quick.framework in Frameworks */,
5D5DBF081D48E39C00D4F914 /* FBSnapshotTestCase.framework in Frameworks */,
@@ -2843,6 +2860,8 @@
isa = PBXGroup;
children = (
5D1654551D3E754F00554D93 /* SDLLifecycleManagerSpec.m */,
+ 5D60DF23202B7A80001EDA01 /* SDLAsynchronousRPCRequestOperationSpec.m */,
+ 5D60DF25202B7A97001EDA01 /* SDLSequentialRPCRequestOperationSpec.m */,
);
name = Lifecycle;
sourceTree = "<group>";
@@ -2850,6 +2869,7 @@
5D1654571D3E79CA00554D93 /* Lifecycle */ = {
isa = PBXGroup;
children = (
+ 5DB16141202115D900F310DF /* Request Operations */,
5D1654581D3E7A1600554D93 /* SDLLifecycleManager.h */,
5D1654591D3E7A1600554D93 /* SDLLifecycleManager.m */,
);
@@ -3838,6 +3858,17 @@
name = "State Machine";
sourceTree = "<group>";
};
+ 5D6035CD202CD3F600A429C9 /* Test Utilities */ = {
+ isa = PBXGroup;
+ children = (
+ 88B848C41F45E20900DED768 /* Helpers */,
+ 5DB1BCE21D2455FD002FFC37 /* Connection Manager */,
+ 5D6035D0202CD46200A429C9 /* SDLSpecUtilities.h */,
+ 5D6035D1202CD46200A429C9 /* SDLSpecUtilities.m */,
+ );
+ name = "Test Utilities";
+ sourceTree = "<group>";
+ };
5D616B501D59042B00553F6B /* Errors */ = {
isa = PBXGroup;
children = (
@@ -3884,6 +3915,7 @@
5D61FA2C1A84237100846EE7 /* SmartDeviceLinkTests */ = {
isa = PBXGroup;
children = (
+ 5D6035CD202CD3F600A429C9 /* Test Utilities */,
167ED9231A9BB86300797BE5 /* Libraries */,
5DAE06711BDEC68700F9B498 /* Developer API Tests */,
5D4346431E6F0B5600B639C6 /* LoggingSpecs */,
@@ -4066,12 +4098,10 @@
5D9F507E1BE7E46600FEF399 /* Files */ = {
isa = PBXGroup;
children = (
- 5DB1BCE21D2455FD002FFC37 /* Connection Manager */,
5DB1BCD61D243A91002FFC37 /* Operations */,
5DAE06721BDEC6C000F9B498 /* SDLFileSpec.m */,
5DAE06741BDEC6D600F9B498 /* SDLArtworkSpec.m */,
5D9F50821BEA5C6100FEF399 /* SDLFileManagerSpec.m */,
- 88B848C41F45E20900DED768 /* Helpers */,
);
name = Files;
sourceTree = "<group>";
@@ -4217,6 +4247,17 @@
name = "Developer API Tests";
sourceTree = "<group>";
};
+ 5DB16141202115D900F310DF /* Request Operations */ = {
+ isa = PBXGroup;
+ children = (
+ 5DB16142202115FC00F310DF /* SDLAsynchronousRPCRequestOperation.h */,
+ 5DB16143202115FC00F310DF /* SDLAsynchronousRPCRequestOperation.m */,
+ 5DB1616A2022276A00F310DF /* SDLSequentialRPCRequestOperation.h */,
+ 5DB1616B2022276A00F310DF /* SDLSequentialRPCRequestOperation.m */,
+ );
+ name = "Request Operations";
+ sourceTree = "<group>";
+ };
5DB1BCD61D243A91002FFC37 /* Operations */ = {
isa = PBXGroup;
children = (
@@ -4246,6 +4287,8 @@
5D9F50861BED412E00FEF399 /* TestConnectionManager.m */,
8850DB5E1F4475D30053A48D /* TestMultipleFilesConnectionManager.h */,
8850DB5F1F4475D30053A48D /* TestMultipleFilesConnectionManager.m */,
+ 5D6035D3202CE4A500A429C9 /* TestMultipleRequestsConnectionManager.h */,
+ 5D6035D4202CE4A500A429C9 /* TestMultipleRequestsConnectionManager.m */,
);
name = "Connection Manager";
sourceTree = "<group>";
@@ -4499,8 +4542,10 @@
children = (
88B848C11F45E1A600DED768 /* TestResponse.h */,
88B848C21F45E1A600DED768 /* TestResponse.m */,
- 88B848C71F462E3600DED768 /* TestProgressResponse.h */,
- 88B848C81F462E3600DED768 /* TestProgressResponse.m */,
+ 88B848C71F462E3600DED768 /* TestFileProgressResponse.h */,
+ 88B848C81F462E3600DED768 /* TestFileProgressResponse.m */,
+ 5D6035D6202CF5C900A429C9 /* TestRequestProgressResponse.h */,
+ 5D6035D7202CF5C900A429C9 /* TestRequestProgressResponse.m */,
);
name = Helpers;
sourceTree = "<group>";
@@ -4899,6 +4944,7 @@
5D61FD331A84238C00846EE7 /* SDLPowerModeQualificationStatus.h in Headers */,
5D61FE011A84238C00846EE7 /* SDLVehicleDataNotificationStatus.h in Headers */,
5D61FDC91A84238C00846EE7 /* SDLTextField.h in Headers */,
+ 5DB16144202115FC00F310DF /* SDLAsynchronousRPCRequestOperation.h in Headers */,
5D6F7A351BC5B9B60070BF37 /* SDLLockScreenViewController.h in Headers */,
DA9F7E7F1DCC028B00ACAE48 /* SDLOnWayPointChange.h in Headers */,
5D61FCE41A84238C00846EE7 /* SDLKeyboardProperties.h in Headers */,
@@ -4933,6 +4979,7 @@
1E5AD04C1F1F79640029B8AF /* SDLDefrostZone.h in Headers */,
1E5AD0601F207AB10029B8AF /* SDLRadioState.h in Headers */,
1E5AD0801F20B73E0029B8AF /* SDLButtonPress.h in Headers */,
+ 5DB1616C2022276A00F310DF /* SDLSequentialRPCRequestOperation.h in Headers */,
1E5AD05C1F2064A80029B8AF /* SDLRDSData.h in Headers */,
1E5AD0341F1F3AA30029B8AF /* SDLRemoteControlCapabilities.h in Headers */,
5DA102A41D4122C700C15826 /* NSMutableDictionary+SafeRemove.h in Headers */,
@@ -5446,6 +5493,7 @@
DA9F7E681DCBFAD400ACAE48 /* SDLOasisAddress.m in Sources */,
5D61FC7B1A84238C00846EE7 /* SDLDeleteInteractionChoiceSet.m in Sources */,
5D61FDC01A84238C00846EE7 /* SDLSystemRequest.m in Sources */,
+ 5DB1616D2022276A00F310DF /* SDLSequentialRPCRequestOperation.m in Sources */,
5D61FD021A84238C00846EE7 /* SDLOnAudioPassThru.m in Sources */,
5D61FCE51A84238C00846EE7 /* SDLKeyboardProperties.m in Sources */,
5DBF062E1E64A93A00A5CF03 /* SDLLogFilter.m in Sources */,
@@ -5524,6 +5572,7 @@
5D1665C51CF8CA2700CC4CA1 /* SDLListFilesOperation.m in Sources */,
8B9376D81F3349FC009605C4 /* SDLMetadataTags.m in Sources */,
5D61FE021A84238C00846EE7 /* SDLVehicleDataNotificationStatus.m in Sources */,
+ 5DB16145202115FC00F310DF /* SDLAsynchronousRPCRequestOperation.m in Sources */,
5D61FDD81A84238C00846EE7 /* SDLTouchType.m in Sources */,
DA4F47961E771AA100FC809E /* SDLEnum.m in Sources */,
5D61FDD61A84238C00846EE7 /* SDLTouchEventCapabilities.m in Sources */,
@@ -5706,6 +5755,7 @@
5D0A9F9A1F15636800CC80DD /* SDLGetSystemCapabilitiesSpec.m in Sources */,
162E82F31A9BDE8B00906325 /* SDLPrerecordedSpeechSpec.m in Sources */,
1EE8C45A1F387BBB00FDC2CF /* SDLGetInteriorVehicleDataSpec.m in Sources */,
+ 5D6035D5202CE4A500A429C9 /* TestMultipleRequestsConnectionManager.m in Sources */,
162E83691A9BDE8B00906325 /* SDLSubscribeButtonResponseSpec.m in Sources */,
5DAE06751BDEC6D600F9B498 /* SDLArtworkSpec.m in Sources */,
5DA23FF01F2FA0FF009C0313 /* SDLControlFramePayloadEndServiceSpec.m in Sources */,
@@ -5717,8 +5767,9 @@
162E82E51A9BDE8B00906325 /* SDLImageTypeSpec.m in Sources */,
162E83421A9BDE8B00906325 /* SDLSubscribeVehicleDataSpec.m in Sources */,
162E83811A9BDE8B00906325 /* SDLImageFieldSpec.m in Sources */,
+ 5D60DF24202B7A80001EDA01 /* SDLAsynchronousRPCRequestOperationSpec.m in Sources */,
162E834F1A9BDE8B00906325 /* SDLDeleteCommandResponseSpec.m in Sources */,
- 88B848C91F462E3600DED768 /* TestProgressResponse.m in Sources */,
+ 88B848C91F462E3600DED768 /* TestFileProgressResponse.m in Sources */,
162E83231A9BDE8B00906325 /* SDLAddSubMenuSpec.m in Sources */,
DA4353E91D2721680099B8C4 /* DispatchTimerSpec.m in Sources */,
1EE8C45D1F387D1C00FDC2CF /* SDLGetInteriorVehicleDataResponseSpec.m in Sources */,
@@ -5794,6 +5845,7 @@
162E82FB1A9BDE8B00906325 /* SDLSpeechCapabilitiesSpec.m in Sources */,
5D0A9F9C1F1565EB00CC80DD /* SDLGetSystemCapabilityResponseSpec.m in Sources */,
162E830D1A9BDE8B00906325 /* SDLWiperStatusSpec.m in Sources */,
+ 5D60DF26202B7A97001EDA01 /* SDLSequentialRPCRequestOperationSpec.m in Sources */,
162E832C1A9BDE8B00906325 /* SDLDiagnosticMessageSpec.m in Sources */,
162E83381A9BDE8B00906325 /* SDLScrollableMessageSpec.m in Sources */,
162E82E81A9BDE8B00906325 /* SDLKeyboardLayoutSpec.m in Sources */,
@@ -5807,6 +5859,7 @@
5DB1BCD41D243A8E002FFC37 /* SDLListFilesOperationSpec.m in Sources */,
162E834B1A9BDE8B00906325 /* SDLAlertManeuverResponseSpec.m in Sources */,
162E833E1A9BDE8B00906325 /* SDLShowSpec.m in Sources */,
+ 5D6035D8202CF5C900A429C9 /* TestRequestProgressResponse.m in Sources */,
162E83241A9BDE8B00906325 /* SDLAlertManeuverSpec.m in Sources */,
5D43466F1E6F55BD00B639C6 /* SDLLogManagerSpec.m in Sources */,
162E83451A9BDE8B00906325 /* SDLUnregisterAppInterfaceSpec.m in Sources */,
@@ -5940,6 +5993,7 @@
162E83731A9BDE8B00906325 /* SDLBeltStatusSpec.m in Sources */,
162E83551A9BDE8B00906325 /* SDLEndAudioPassThruResponseSpec.m in Sources */,
162E83251A9BDE8B00906325 /* SDLAlertSpec.m in Sources */,
+ 5D6035D2202CD46200A429C9 /* SDLSpecUtilities.m in Sources */,
DA9F7EA81DCC060B00ACAE48 /* SDLGetWaypointsResponseSpec.m in Sources */,
162E830A1A9BDE8B00906325 /* SDLVehicleDataTypeSpec.m in Sources */,
5DB1BCD31D243A8E002FFC37 /* SDLDeleteFileOperationSpec.m in Sources */,
@@ -6344,7 +6398,7 @@
PRODUCT_BUNDLE_IDENTIFIER = "com.smartdevicelink.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
- SWIFT_VERSION = 3.0;
+ SWIFT_VERSION = 4.0;
};
name = Debug;
};
diff --git a/SmartDeviceLink/SDLAsynchronousRPCRequestOperation.h b/SmartDeviceLink/SDLAsynchronousRPCRequestOperation.h
new file mode 100644
index 000000000..b08c700a2
--- /dev/null
+++ b/SmartDeviceLink/SDLAsynchronousRPCRequestOperation.h
@@ -0,0 +1,26 @@
+//
+// SDLRPCRequestOperation.h
+// SmartDeviceLink
+//
+// Created by Joel Fischer on 1/30/18.
+// Copyright © 2018 smartdevicelink. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+#import "SDLAsynchronousOperation.h"
+#import "SDLLifecycleManager.h"
+
+@protocol SDLConnectionManagerType;
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface SDLAsynchronousRPCRequestOperation : SDLAsynchronousOperation
+
+- (instancetype)initWithConnectionManager:(id<SDLConnectionManagerType>)connectionManager requests:(NSArray<SDLRPCRequest *> *)requests progressHandler:(nullable SDLMultipleAsyncRequestProgressHandler)progressHandler completionHandler:(nullable SDLMultipleRequestCompletionHandler)completionHandler;
+
+- (instancetype)initWithConnectionManager:(id<SDLConnectionManagerType>)connectionManager request:(SDLRPCRequest *)request responseHandler:(nullable SDLResponseHandler)responseHandler;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/SmartDeviceLink/SDLAsynchronousRPCRequestOperation.m b/SmartDeviceLink/SDLAsynchronousRPCRequestOperation.m
new file mode 100644
index 000000000..a79b30b09
--- /dev/null
+++ b/SmartDeviceLink/SDLAsynchronousRPCRequestOperation.m
@@ -0,0 +1,165 @@
+//
+// SDLRPCRequestOperation.m
+// SmartDeviceLink
+//
+// Created by Joel Fischer on 1/30/18.
+// Copyright © 2018 smartdevicelink. All rights reserved.
+//
+
+#import "SDLAsynchronousRPCRequestOperation.h"
+
+#import "SDLConnectionManagerType.h"
+#import "SDLError.h"
+#import "SDLGlobals.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface SDLAsynchronousRPCRequestOperation ()
+
+@property (copy, nonatomic) NSArray<SDLRPCRequest *> *requests;
+@property (weak, nonatomic) id<SDLConnectionManagerType> connectionManager;
+@property (copy, nonatomic, nullable) SDLMultipleAsyncRequestProgressHandler progressHandler;
+@property (copy, nonatomic, nullable) SDLMultipleRequestCompletionHandler completionHandler;
+@property (copy, nonatomic, nullable) SDLResponseHandler responseHandler;
+
+@property (strong, nonatomic) NSUUID *operationId;
+@property (assign, nonatomic) NSUInteger requestsComplete;
+@property (assign, nonatomic) NSUInteger requestsStarted;
+@property (assign, nonatomic, readonly) float percentComplete;
+@property (assign, nonatomic) BOOL requestFailed;
+
+@end
+
+@implementation SDLAsynchronousRPCRequestOperation {
+ BOOL executing;
+ BOOL finished;
+}
+
+- (instancetype)init {
+ self = [super init];
+ if (!self) { return nil; }
+
+ executing = NO;
+ finished = NO;
+
+ _operationId = [NSUUID UUID];
+ _requestsComplete = 0;
+ _requestsStarted = 0;
+ _requestFailed = NO;
+
+ return self;
+}
+
+- (instancetype)initWithConnectionManager:(id<SDLConnectionManagerType>)connectionManager requests:(NSArray<SDLRPCRequest *> *)requests progressHandler:(nullable SDLMultipleAsyncRequestProgressHandler)progressHandler completionHandler:(nullable SDLMultipleRequestCompletionHandler)completionHandler {
+ self = [self init];
+
+ _connectionManager = connectionManager;
+ _requests = requests;
+ _progressHandler = progressHandler;
+ _completionHandler = completionHandler;
+
+ return self;
+}
+
+- (instancetype)initWithConnectionManager:(id<SDLConnectionManagerType>)connectionManager request:(SDLRPCRequest *)request responseHandler:(nullable SDLResponseHandler)responseHandler {
+ self = [self init];
+
+ _connectionManager = connectionManager;
+ _requests = @[request];
+ _responseHandler = responseHandler;
+
+ return self;
+}
+
+- (void)start {
+ [super start];
+
+ [self sdl_sendRequests];
+}
+
+- (void)sdl_sendRequests {
+ for (SDLRPCRequest *request in self.requests) {
+ if (self.isCancelled) {
+ [self sdl_abortOperationWithRequest:request];
+ break;
+ }
+
+ [self sdl_sendRequest:request];
+ self.requestsStarted++;
+ }
+}
+
+- (void)sdl_sendRequest:(SDLRPCRequest *)request {
+ __weak typeof(self) weakSelf = self;
+ [self.connectionManager sendConnectionRequest:request withResponseHandler:^(__kindof SDLRPCRequest * _Nullable request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error) {
+ __strong typeof(self) strongSelf = weakSelf;
+
+ if (strongSelf.isCancelled) {
+ [self sdl_abortOperationWithRequest:request];
+ BLOCK_RETURN;
+ }
+
+ strongSelf.requestsComplete++;
+
+ // If this request failed set our internal request failed to YES
+ if (error != nil) {
+ strongSelf.requestFailed = YES;
+ }
+
+ if (strongSelf.progressHandler != NULL) {
+ strongSelf.progressHandler(request, response, error, strongSelf.percentComplete);
+ } else if (strongSelf.responseHandler != NULL) {
+ strongSelf.responseHandler(request, response, error);
+ }
+
+ // If we've received responses for all requests, call the completion handler.
+ if (strongSelf.requestsComplete >= strongSelf.requests.count) {
+ [strongSelf finishOperation];
+ }
+ }];
+}
+
+- (void)sdl_abortOperationWithRequest:(SDLRPCRequest *)request {
+ if (self.isFinished) { return; }
+ self.requestFailed = YES;
+
+ for (NSUInteger i = self.requestsComplete; i <= self.requests.count; i++) {
+ if (self.progressHandler != NULL) {
+ self.progressHandler(self.requests[i], nil, [NSError sdl_lifecycle_multipleRequestsCancelled], self.percentComplete);
+ }
+
+ if (self.responseHandler != NULL) {
+ self.responseHandler(request, nil, [NSError sdl_lifecycle_multipleRequestsCancelled]);
+ }
+ }
+
+ [self finishOperation];
+}
+
+#pragma mark - Getters
+
+- (float)percentComplete {
+ return (float)self.requestsComplete / (float)self.requests.count;
+}
+
+#pragma mark - Property Overrides
+
+- (void)finishOperation {
+ if (self.completionHandler != NULL) {
+ self.completionHandler(!self.requestFailed);
+ }
+
+ [super finishOperation];
+}
+
+- (nullable NSString *)name {
+ return [NSString stringWithFormat:@"%@ - %@", self.class, self.operationId];
+}
+
+- (NSOperationQueuePriority)queuePriority {
+ return NSOperationQueuePriorityNormal;
+}
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/SmartDeviceLink/SDLConnectionManagerType.h b/SmartDeviceLink/SDLConnectionManagerType.h
index 5f2e03a39..8c5fa6ce9 100644
--- a/SmartDeviceLink/SDLConnectionManagerType.h
+++ b/SmartDeviceLink/SDLConnectionManagerType.h
@@ -22,7 +22,15 @@ NS_ASSUME_NONNULL_BEGIN
* @param request The RPC request to be sent to the remote head unit.
* @param handler A completion block called when the response is received.
*/
-- (void)sendManagerRequest:(__kindof SDLRPCRequest *)request withResponseHandler:(nullable SDLResponseHandler)handler;
+- (void)sendConnectionManagerRequest:(__kindof SDLRPCRequest *)request withResponseHandler:(nullable SDLResponseHandler)handler;
+
+/**
+ Send an RPC without bypassing the block on RPC sends before managers complete setup.
+
+ @param request The RPC request to be sent to the remote head unit.
+ @param handler A completion block called when the response is received.
+ */
+- (void)sendConnectionRequest:(__kindof SDLRPCRequest *)request withResponseHandler:(nullable SDLResponseHandler)handler;
@end
diff --git a/SmartDeviceLink/SDLDeleteFileOperation.m b/SmartDeviceLink/SDLDeleteFileOperation.m
index 7ebecffa5..f45f3cbdd 100644
--- a/SmartDeviceLink/SDLDeleteFileOperation.m
+++ b/SmartDeviceLink/SDLDeleteFileOperation.m
@@ -48,7 +48,7 @@ NS_ASSUME_NONNULL_BEGIN
SDLDeleteFile *deleteFile = [[SDLDeleteFile alloc] initWithFileName:self.fileName];
typeof(self) weakself = self;
- [self.connectionManager sendManagerRequest:deleteFile
+ [self.connectionManager sendConnectionManagerRequest:deleteFile
withResponseHandler:^(__kindof SDLRPCRequest *request, __kindof SDLRPCResponse *response, NSError *error) {
// Pull out the parameters
SDLDeleteFileResponse *deleteFileResponse = (SDLDeleteFileResponse *)response;
diff --git a/SmartDeviceLink/SDLError.h b/SmartDeviceLink/SDLError.h
index 35a919f37..220f5941d 100644
--- a/SmartDeviceLink/SDLError.h
+++ b/SmartDeviceLink/SDLError.h
@@ -32,6 +32,7 @@ extern SDLErrorDomain *const SDLErrorDomainFileManager;
+ (NSError *)sdl_lifecycle_startedWithBadResult:(SDLResult)result info:(NSString *)info;
+ (NSError *)sdl_lifecycle_startedWithWarning:(SDLResult)result info:(NSString *)info;
+ (NSError *)sdl_lifecycle_failedWithBadResult:(SDLResult)result info:(NSString *)info;
++ (NSError *)sdl_lifecycle_multipleRequestsCancelled;
#pragma mark SDLFileManager
diff --git a/SmartDeviceLink/SDLError.m b/SmartDeviceLink/SDLError.m
index 04f8e05fd..7be6feeb9 100644
--- a/SmartDeviceLink/SDLError.m
+++ b/SmartDeviceLink/SDLError.m
@@ -101,6 +101,12 @@ SDLErrorDomain *const SDLErrorDomainFileManager = @"com.sdl.filemanager.error";
userInfo:userInfo];
}
++ (NSError *)sdl_lifecycle_multipleRequestsCancelled {
+ return [NSError errorWithDomain:SDLErrorDomainLifecycleManager
+ code:SDLManagerErrorCancelled
+ userInfo:nil];
+}
+
#pragma mark SDLFileManager
diff --git a/SmartDeviceLink/SDLErrorConstants.h b/SmartDeviceLink/SDLErrorConstants.h
index 649d7cb27..f44108255 100644
--- a/SmartDeviceLink/SDLErrorConstants.h
+++ b/SmartDeviceLink/SDLErrorConstants.h
@@ -39,7 +39,11 @@ typedef NS_ENUM(NSInteger, SDLManagerError) {
/**
* Registering with the remote system was successful, but had a warning.
*/
- SDLManagerErrorRegistrationSuccessWithWarning = -7
+ SDLManagerErrorRegistrationSuccessWithWarning = -7,
+ /**
+ * Request operations were cancelled before they could be sent
+ */
+ SDLManagerErrorCancelled = -8
};
/**
diff --git a/SmartDeviceLink/SDLFocusableItemLocator.m b/SmartDeviceLink/SDLFocusableItemLocator.m
index d44f8113c..dbd3e0fef 100644
--- a/SmartDeviceLink/SDLFocusableItemLocator.m
+++ b/SmartDeviceLink/SDLFocusableItemLocator.m
@@ -121,7 +121,7 @@ NS_ASSUME_NONNULL_BEGIN
}
SDLSendHapticData* hapticRPC = [[SDLSendHapticData alloc] initWithHapticRectData:hapticRects];
- [self.connectionManager sendManagerRequest:hapticRPC withResponseHandler:nil];
+ [self.connectionManager sendConnectionManagerRequest:hapticRPC withResponseHandler:nil];
}
#pragma mark SDLFocusableItemHitTester functions
diff --git a/SmartDeviceLink/SDLLifecycleManager.h b/SmartDeviceLink/SDLLifecycleManager.h
index 65943d8d2..b37541688 100644
--- a/SmartDeviceLink/SDLLifecycleManager.h
+++ b/SmartDeviceLink/SDLLifecycleManager.h
@@ -50,7 +50,9 @@ extern SDLLifecycleState *const SDLLifecycleStateSettingUpHMI;
extern SDLLifecycleState *const SDLLifecycleStateUnregistering;
extern SDLLifecycleState *const SDLLifecycleStateReady;
-
+typedef void (^SDLMultipleRequestCompletionHandler)(BOOL success);
+typedef BOOL (^SDLMultipleSequentialRequestProgressHandler)(__kindof SDLRPCRequest *request, __kindof SDLRPCResponse *__nullable response, NSError *__nullable error, float percentComplete);
+typedef void (^SDLMultipleAsyncRequestProgressHandler)(__kindof SDLRPCRequest *request, __kindof SDLRPCResponse *__nullable response, NSError *__nullable error, float percentComplete);
typedef void (^SDLManagerReadyBlock)(BOOL success, NSError *_Nullable error);
@@ -81,6 +83,8 @@ typedef void (^SDLManagerReadyBlock)(BOOL success, NSError *_Nullable error);
@property (copy, nonatomic, nullable) SDLSystemContext systemContext;
@property (strong, nonatomic, nullable) SDLRegisterAppInterfaceResponse *registerResponse;
+@property (strong, nonatomic) NSOperationQueue *rpcOperationQueue;
+
#pragma mark Lifecycle
/**
@@ -123,6 +127,24 @@ typedef void (^SDLManagerReadyBlock)(BOOL success, NSError *_Nullable error);
*/
- (void)sendRequest:(SDLRPCRequest *)request withResponseHandler:(nullable SDLResponseHandler)handler;
+/**
+ Send all of the requests given as quickly as possible, but in order. Call the completionHandler after all requests have either failed or given a response.
+
+ @param requests The requests to be sent
+ @param progressHandler A handler called every time a response is received.
+ @param completionHandler A handler to call when all requests have been responded to
+ */
+- (void)sendRequests:(NSArray<SDLRPCRequest *> *)requests progressHandler:(nullable SDLMultipleAsyncRequestProgressHandler)progressHandler completionHandler:(nullable SDLMultipleRequestCompletionHandler)completionHandler;
+
+/**
+ Send all of the requests one at a time, with the next one going out only after the previous one has received a response. Call the completionHandler after all requests have either failed or given a response.
+
+ @param requests The requests to be sent
+ @param progressHandler A handler called every time a response is received
+ @param completionHandler A handler to call when all requests have been responded to
+ */
+- (void)sendSequentialRequests:(NSArray<SDLRPCRequest *> *)requests progressHandler:(nullable SDLMultipleSequentialRequestProgressHandler)progressHandler completionHandler:(nullable SDLMultipleRequestCompletionHandler)completionHandler;
+
@end
NS_ASSUME_NONNULL_END
diff --git a/SmartDeviceLink/SDLLifecycleManager.m b/SmartDeviceLink/SDLLifecycleManager.m
index 481ceb9ff..1323c3cd1 100644
--- a/SmartDeviceLink/SDLLifecycleManager.m
+++ b/SmartDeviceLink/SDLLifecycleManager.m
@@ -12,6 +12,7 @@
#import "NSMapTable+Subscripting.h"
#import "SDLAbstractProtocol.h"
+#import "SDLAsynchronousRPCRequestOperation.h"
#import "SDLConfiguration.h"
#import "SDLConnectionManagerType.h"
#import "SDLLogMacros.h"
@@ -39,6 +40,7 @@
#import "SDLRegisterAppInterfaceResponse.h"
#import "SDLResponseDispatcher.h"
#import "SDLResult.h"
+#import "SDLSequentialRPCRequestOperation.h"
#import "SDLSetAppIcon.h"
#import "SDLStateMachine.h"
#import "SDLStreamingMediaConfiguration.h"
@@ -74,6 +76,7 @@ SDLLifecycleState *const SDLLifecycleStateReady = @"Ready";
// Private properties
@property (copy, nonatomic) SDLManagerReadyBlock readyHandler;
+@property (copy, nonatomic) dispatch_queue_t lifecycleQueue;
@end
@@ -108,6 +111,11 @@ SDLLifecycleState *const SDLLifecycleStateReady = @"Ready";
_responseDispatcher = [[SDLResponseDispatcher alloc] initWithNotificationDispatcher:_notificationDispatcher];
_registerResponse = nil;
+ _rpcOperationQueue = [[NSOperationQueue alloc] init];
+ _rpcOperationQueue.name = @"com.sdl.lifecycle.rpcOperation.concurrent";
+ _rpcOperationQueue.maxConcurrentOperationCount = 3;
+ _lifecycleQueue = dispatch_queue_create("com.sdl.lifecycle", DISPATCH_QUEUE_SERIAL);
+
// Managers
_fileManager = [[SDLFileManager alloc] initWithConnectionManager:self];
_permissionManager = [[SDLPermissionManager alloc] init];
@@ -132,6 +140,12 @@ SDLLifecycleState *const SDLLifecycleStateReady = @"Ready";
}
- (void)startWithReadyHandler:(SDLManagerReadyBlock)readyHandler {
+ dispatch_sync(_lifecycleQueue, ^{
+ [self sdl_startWithReadyHandler:readyHandler];
+ });
+}
+
+- (void)sdl_startWithReadyHandler:(SDLManagerReadyBlock)readyHandler {
if (![self.lifecycleStateMachine isCurrentState:SDLLifecycleStateStopped]) {
SDLLogW(@"Warning: SDL has already been started, this attempt will be ignored");
return;
@@ -144,12 +158,14 @@ SDLLifecycleState *const SDLLifecycleStateReady = @"Ready";
}
- (void)stop {
- SDLLogD(@"Lifecycle manager stopped");
- if ([self.lifecycleStateMachine isCurrentState:SDLLifecycleStateReady]) {
- [self.lifecycleStateMachine transitionToState:SDLLifecycleStateUnregistering];
- } else {
- [self.lifecycleStateMachine transitionToState:SDLLifecycleStateStopped];
- }
+ dispatch_sync(_lifecycleQueue, ^{
+ SDLLogD(@"Lifecycle manager stopped");
+ if ([self.lifecycleStateMachine isCurrentState:SDLLifecycleStateReady]) {
+ [self.lifecycleStateMachine transitionToState:SDLLifecycleStateUnregistering];
+ } else {
+ [self.lifecycleStateMachine transitionToState:SDLLifecycleStateStopped];
+ }
+ });
}
@@ -211,6 +227,8 @@ SDLLifecycleState *const SDLLifecycleStateReady = @"Ready";
[self.streamManager stop];
[self.responseDispatcher clear];
+ [self.rpcOperationQueue cancelAllOperations];
+
self.registerResponse = nil;
self.lastCorrelationId = 0;
self.hmiLevel = nil;
@@ -244,20 +262,22 @@ SDLLifecycleState *const SDLLifecycleStateReady = @"Ready";
__weak typeof(self) weakSelf = self;
[self sdl_sendRequest:regRequest
withResponseHandler:^(__kindof SDLRPCRequest *_Nullable request, __kindof SDLRPCResponse *_Nullable response, NSError *_Nullable error) {
- // If the success BOOL is NO or we received an error at this point, we failed. Call the ready handler and transition to the DISCONNECTED state.
- if (error != nil || ![response.success boolValue]) {
- SDLLogE(@"Failed to register the app. Error: %@, Response: %@", error, response);
- weakSelf.readyHandler(NO, error);
+ dispatch_async(weakSelf.lifecycleQueue, ^{
+ // If the success BOOL is NO or we received an error at this point, we failed. Call the ready handler and transition to the DISCONNECTED state.
+ if (error != nil || ![response.success boolValue]) {
+ SDLLogE(@"Failed to register the app. Error: %@, Response: %@", error, response);
+ weakSelf.readyHandler(NO, error);
+
+ if (weakSelf.lifecycleState != SDLLifecycleStateReconnecting) {
+ [weakSelf.lifecycleStateMachine transitionToState:SDLLifecycleStateStopped];
+ }
- if (weakSelf.lifecycleState != SDLLifecycleStateReconnecting) {
- [weakSelf.lifecycleStateMachine transitionToState:SDLLifecycleStateStopped];
+ return;
}
-
- return;
- }
- weakSelf.registerResponse = (SDLRegisterAppInterfaceResponse *)response;
- [weakSelf.lifecycleStateMachine transitionToState:SDLLifecycleStateRegistered];
+ weakSelf.registerResponse = (SDLRegisterAppInterfaceResponse *)response;
+ [weakSelf.lifecycleStateMachine transitionToState:SDLLifecycleStateRegistered];
+ });
}];
}
@@ -338,18 +358,21 @@ SDLLifecycleState *const SDLLifecycleStateReady = @"Ready";
dispatch_group_leave(managerGroup);
// When done, we want to transition, even if there were errors. They may be expected, e.g. on head units that do not support files.
- dispatch_group_notify(managerGroup, dispatch_get_main_queue(), ^{
+ dispatch_group_notify(managerGroup, self.lifecycleQueue, ^{
[self.lifecycleStateMachine transitionToState:SDLLifecycleStateSettingUpAppIcon];
});
}
- (void)didEnterStateSettingUpAppIcon {
// We only want to send the app icon when the file manager is complete, and when that's done, wait for hmi status to be ready
+ __weak typeof(self) weakself = self;
[self sdl_sendAppIcon:self.configuration.lifecycleConfig.appIcon withCompletion:^() {
- // We could have been shut down while setting up the app icon, make sure we still want to continue or we could crash
- if (self.lifecycleState == SDLLifecycleStateSettingUpAppIcon) {
- [self.lifecycleStateMachine transitionToState:SDLLifecycleStateSettingUpHMI];
- }
+ dispatch_async(weakself.lifecycleQueue, ^{
+ // We could have been shut down while setting up the app icon, make sure we still want to continue or we could crash
+ if (weakself.lifecycleState == SDLLifecycleStateSettingUpAppIcon) {
+ [weakself.lifecycleStateMachine transitionToState:SDLLifecycleStateSettingUpHMI];
+ }
+ });
}];
}
@@ -375,16 +398,21 @@ SDLLifecycleState *const SDLLifecycleStateReady = @"Ready";
}
// If we got to this point, we succeeded, send the error if there was a warning.
- self.readyHandler(YES, startError);
+ dispatch_async(dispatch_get_main_queue(), ^{
+ self.readyHandler(YES, startError);
+ });
+
[self.notificationDispatcher postNotificationName:SDLDidBecomeReady infoObject:nil];
// Send the hmi level going from NONE to whatever we're at now (could still be NONE)
- [self.delegate hmiLevel:SDLHMILevelNone didChangeToLevel:self.hmiLevel];
+ dispatch_async(dispatch_get_main_queue(), ^{
+ [self.delegate hmiLevel:SDLHMILevelNone didChangeToLevel:self.hmiLevel];
- // Send the audio streaming state going from NOT_AUDIBLE to whatever we're at now (could still be NOT_AUDIBLE)
- if ([self.delegate respondsToSelector:@selector(audioStreamingState:didChangeToState:)]) {
- [self.delegate audioStreamingState:SDLAudioStreamingStateNotAudible didChangeToState:self.audioStreamingState];
- }
+ // Send the audio streaming state going from NOT_AUDIBLE to whatever we're at now (could still be NOT_AUDIBLE)
+ if ([self.delegate respondsToSelector:@selector(audioStreamingState:didChangeToState:)]) {
+ [self.delegate audioStreamingState:SDLAudioStreamingStateNotAudible didChangeToState:self.audioStreamingState];
+ }
+ });
}
- (void)didEnterStateUnregistering {
@@ -447,25 +475,46 @@ SDLLifecycleState *const SDLLifecycleStateReady = @"Ready";
}
- (void)sendRequest:(__kindof SDLRPCRequest *)request withResponseHandler:(nullable SDLResponseHandler)handler {
+ SDLAsynchronousRPCRequestOperation *op = [[SDLAsynchronousRPCRequestOperation alloc] initWithConnectionManager:self request:request responseHandler:handler];
+ [self.rpcOperationQueue addOperation:op];
+}
+
+- (void)sendRequests:(NSArray<SDLRPCRequest *> *)requests progressHandler:(nullable SDLMultipleAsyncRequestProgressHandler)progressHandler completionHandler:(nullable SDLMultipleRequestCompletionHandler)completionHandler {
+ SDLAsynchronousRPCRequestOperation *op = [[SDLAsynchronousRPCRequestOperation alloc] initWithConnectionManager:self requests:requests progressHandler:progressHandler completionHandler:completionHandler];
+ [self.rpcOperationQueue addOperation:op];
+}
+
+- (void)sendSequentialRequests:(NSArray<SDLRPCRequest *> *)requests progressHandler:(nullable SDLMultipleSequentialRequestProgressHandler)progressHandler completionHandler:(nullable SDLMultipleRequestCompletionHandler)completionHandler {
+ SDLSequentialRPCRequestOperation *op = [[SDLSequentialRPCRequestOperation alloc] initWithConnectionManager:self requests:requests progressHandler:progressHandler completionHandler:completionHandler];
+ [self.rpcOperationQueue addOperation:op];
+}
+
+- (void)sendConnectionRequest:(__kindof SDLRPCRequest *)request withResponseHandler:(nullable SDLResponseHandler)handler {
if (![self.lifecycleStateMachine isCurrentState:SDLLifecycleStateReady]) {
SDLLogW(@"Manager not ready, message not sent (%@)", request);
if (handler) {
- handler(request, nil, [NSError sdl_lifecycle_notReadyError]);
+ dispatch_async(dispatch_get_main_queue(), ^{
+ handler(request, nil, [NSError sdl_lifecycle_notReadyError]);
+ });
}
return;
}
- [self sdl_sendRequest:request withResponseHandler:handler];
+ dispatch_async(_lifecycleQueue, ^{
+ [self sdl_sendRequest:request withResponseHandler:handler];
+ });
}
// Managers need to avoid state checking. Part of <SDLConnectionManagerType>.
-- (void)sendManagerRequest:(__kindof SDLRPCRequest *)request withResponseHandler:(nullable SDLResponseHandler)block {
- [self sdl_sendRequest:request withResponseHandler:block];
+- (void)sendConnectionManagerRequest:(__kindof SDLRPCRequest *)request withResponseHandler:(nullable SDLResponseHandler)handler {
+ dispatch_async(_lifecycleQueue, ^{
+ [self sdl_sendRequest:request withResponseHandler:handler];
+ });
}
- (void)sdl_sendRequest:(SDLRPCRequest *)request withResponseHandler:(nullable SDLResponseHandler)handler {
- // We will allow things to be sent in a "SDLLifeCycleStateConnected" state in the private method, but block it in the public method sendRequest:withCompletionHandler: so that the lifecycle manager can complete its setup without being bothered by developer error
+ // We will allow things to be sent in a "SDLLifecycleStateConnected" state in the private method, but block it in the public method sendRequest:withCompletionHandler: so that the lifecycle manager can complete its setup without being bothered by developer error
NSParameterAssert(request != nil);
// If, for some reason, the request is nil we should error out.
@@ -473,7 +522,9 @@ SDLLifecycleState *const SDLLifecycleStateReady = @"Ready";
NSError *error = [NSError sdl_lifecycle_rpcErrorWithDescription:@"Nil Request Sent" andReason:@"A nil RPC request was passed and cannot be sent."];
SDLLogW(@"%@", error);
if (handler) {
- handler(nil, nil, error);
+ dispatch_async(dispatch_get_main_queue(), ^{
+ handler(nil, nil, error);
+ });
}
return;
}
@@ -510,20 +561,31 @@ SDLLifecycleState *const SDLLifecycleStateReady = @"Ready";
- (void)transportDidConnect {
SDLLogD(@"Transport connected");
- [self.lifecycleStateMachine transitionToState:SDLLifecycleStateConnected];
+
+ dispatch_async(self.lifecycleQueue, ^{
+ [self.lifecycleStateMachine transitionToState:SDLLifecycleStateConnected];
+ });
}
- (void)transportDidDisconnect {
SDLLogD(@"Transport Disconnected");
- if (self.lifecycleState == SDLLifecycleStateUnregistering || self.lifecycleState == SDLLifecycleStateStopped) {
- [self.lifecycleStateMachine transitionToState:SDLLifecycleStateStopped];
- } else {
- [self.lifecycleStateMachine transitionToState:SDLLifecycleStateReconnecting];
- }
+ dispatch_async(self.lifecycleQueue, ^{
+ if (self.lifecycleState == SDLLifecycleStateUnregistering || self.lifecycleState == SDLLifecycleStateStopped) {
+ [self.lifecycleStateMachine transitionToState:SDLLifecycleStateStopped];
+ } else {
+ [self.lifecycleStateMachine transitionToState:SDLLifecycleStateReconnecting];
+ }
+ });
}
- (void)hmiStatusDidChange:(SDLRPCNotificationNotification *)notification {
+ dispatch_async(self.lifecycleQueue, ^{
+ [self sdl_hmiStatusDidChange:notification];
+ });
+}
+
+- (void)sdl_hmiStatusDidChange:(SDLRPCNotificationNotification *)notification {
if (![notification isNotificationMemberOfClass:[SDLOnHMIStatus class]]) {
return;
}
@@ -531,17 +593,17 @@ SDLLifecycleState *const SDLLifecycleStateReady = @"Ready";
SDLOnHMIStatus *hmiStatusNotification = notification.notification;
SDLHMILevel oldHMILevel = self.hmiLevel;
self.hmiLevel = hmiStatusNotification.hmiLevel;
-
+
SDLAudioStreamingState oldStreamingState = self.audioStreamingState;
self.audioStreamingState = hmiStatusNotification.audioStreamingState;
-
+
SDLSystemContext oldSystemContext = self.systemContext;
self.systemContext = hmiStatusNotification.systemContext;
SDLLogD(@"HMI level changed from %@ to %@", oldHMILevel, self.hmiLevel);
SDLLogD(@"Audio streaming state changed from %@ to %@", oldStreamingState, self.audioStreamingState);
- if ([self.lifecycleStateMachine isCurrentState:SDLLifecycleStateSettingUpHMI]) {
+ if ([self.lifecycleStateMachine isCurrentState:SDLLifecycleStateSettingUpHMI]) {
[self.lifecycleStateMachine transitionToState:SDLLifecycleStateReady];
}
@@ -549,22 +611,30 @@ SDLLifecycleState *const SDLLifecycleStateReady = @"Ready";
return;
}
- if (![oldHMILevel isEqualToEnum:self.hmiLevel]) {
- [self.delegate hmiLevel:oldHMILevel didChangeToLevel:self.hmiLevel];
- }
-
- if (![oldStreamingState isEqualToEnum:self.audioStreamingState]
- && [self.delegate respondsToSelector:@selector(audioStreamingState:didChangeToState:)]) {
- [self.delegate audioStreamingState:oldStreamingState didChangeToState:self.audioStreamingState];
- }
-
- if (![oldSystemContext isEqualToEnum:self.systemContext]
- && [self.delegate respondsToSelector:@selector(systemContext:didChangeToContext:)]) {
- [self.delegate systemContext:oldSystemContext didChangeToContext:self.systemContext];
- }
+ dispatch_async(dispatch_get_main_queue(), ^{
+ if (![oldHMILevel isEqualToEnum:self.hmiLevel]) {
+ [self.delegate hmiLevel:oldHMILevel didChangeToLevel:self.hmiLevel];
+ }
+
+ if (![oldStreamingState isEqualToEnum:self.audioStreamingState]
+ && [self.delegate respondsToSelector:@selector(audioStreamingState:didChangeToState:)]) {
+ [self.delegate audioStreamingState:oldStreamingState didChangeToState:self.audioStreamingState];
+ }
+
+ if (![oldSystemContext isEqualToEnum:self.systemContext]
+ && [self.delegate respondsToSelector:@selector(systemContext:didChangeToContext:)]) {
+ [self.delegate systemContext:oldSystemContext didChangeToContext:self.systemContext];
+ }
+ });
}
- (void)remoteHardwareDidUnregister:(SDLRPCNotificationNotification *)notification {
+ dispatch_async(self.lifecycleQueue, ^{
+ [self sdl_remoteHardwareDidUnregister:notification];
+ });
+}
+
+- (void)sdl_remoteHardwareDidUnregister:(SDLRPCNotificationNotification *)notification {
if (![notification isNotificationMemberOfClass:[SDLOnAppInterfaceUnregistered class]]) {
return;
}
diff --git a/SmartDeviceLink/SDLListFilesOperation.m b/SmartDeviceLink/SDLListFilesOperation.m
index 4fbc067fa..6929f1f84 100644
--- a/SmartDeviceLink/SDLListFilesOperation.m
+++ b/SmartDeviceLink/SDLListFilesOperation.m
@@ -47,7 +47,7 @@ NS_ASSUME_NONNULL_BEGIN
SDLListFiles *listFiles = [[SDLListFiles alloc] init];
__weak typeof(self) weakSelf = self;
- [self.connectionManager sendManagerRequest:listFiles
+ [self.connectionManager sendConnectionManagerRequest:listFiles
withResponseHandler:^(__kindof SDLRPCRequest *request, __kindof SDLRPCResponse *response, NSError *error) {
SDLListFilesResponse *listFilesResponse = (SDLListFilesResponse *)response;
BOOL success = [listFilesResponse.success boolValue];
@@ -75,4 +75,4 @@ NS_ASSUME_NONNULL_BEGIN
@end
-NS_ASSUME_NONNULL_END \ No newline at end of file
+NS_ASSUME_NONNULL_END
diff --git a/SmartDeviceLink/SDLManager.h b/SmartDeviceLink/SDLManager.h
index 615189675..98a1406b3 100644
--- a/SmartDeviceLink/SDLManager.h
+++ b/SmartDeviceLink/SDLManager.h
@@ -25,6 +25,35 @@
NS_ASSUME_NONNULL_BEGIN
+/**
+ A completion handler called after a sequential or simultaneous set of requests have completed sending.
+
+ @param success True if every request succeeded, false if any failed. See the progress handler for more details on failures.
+ */
+typedef void (^SDLMultipleRequestCompletionHandler)(BOOL success);
+
+/**
+ A handler called after each response to a request comes in in a multiple request send.
+
+ @param request The request that received a response
+ @param response The response received
+ @param error The error that occurred during the request if any occurred.
+ @param percentComplete The percentage of requests that have received a response
+ @return continueSendingRequests NO to cancel any requests that have not yet been sent. This is really only useful for a sequential send (sendSequentialRequests:progressHandler:completionHandler:). Return YES to continue sending requests.
+ */
+typedef BOOL (^SDLMultipleSequentialRequestProgressHandler)(__kindof SDLRPCRequest *request, __kindof SDLRPCResponse *__nullable response, NSError *__nullable error, float percentComplete);
+
+/**
+ A handler called after each response to a request comes in in a multiple request send.
+
+ @param request The request that received a response
+ @param response The response received
+ @param error The error that occurred during the request if any occurred.
+ @param percentComplete The percentage of requests that have received a response
+ */
+typedef void (^SDLMultipleAsyncRequestProgressHandler)(__kindof SDLRPCRequest *request, __kindof SDLRPCResponse *__nullable response, NSError *__nullable error, float percentComplete);
+
+
typedef void (^SDLManagerReadyBlock)(BOOL success, NSError *_Nullable error);
@@ -76,6 +105,11 @@ typedef void (^SDLManagerReadyBlock)(BOOL success, NSError *_Nullable error);
@property (weak, nonatomic, nullable) id<SDLManagerDelegate> delegate;
/**
+ The currently pending RPC request send transactions
+ */
+@property (copy, nonatomic, readonly) NSArray<__kindof NSOperation *> *pendingRPCTransactions;
+
+/**
* Deprecated internal proxy object. This should only be accessed when the Manager is READY. This property may go to nil at any time.
* The only reason to use this is to access the `putFileStream:withRequest:` method. All other functionality exists on managers in 4.3. This will be removed in 5.0 and the functionality replicated on `SDLFileManager`.
*/
@@ -129,6 +163,24 @@ typedef void (^SDLManagerReadyBlock)(BOOL success, NSError *_Nullable error);
*/
- (void)sendRequest:(SDLRPCRequest *)request withResponseHandler:(nullable SDLResponseHandler)handler NS_SWIFT_NAME(send(request:responseHandler:));
+/**
+ Send all of the requests given as quickly as possible, but in order. Call the completionHandler after all requests have either failed or given a response.
+
+ @param requests The requests to be sent
+ @param progressHandler A handler called every time a response is received
+ @param completionHandler A handler to call when all requests have been responded to
+ */
+- (void)sendRequests:(NSArray<SDLRPCRequest *> *)requests progressHandler:(nullable SDLMultipleAsyncRequestProgressHandler)progressHandler completionHandler:(nullable SDLMultipleRequestCompletionHandler)completionHandler;
+
+/**
+ Send all of the requests one at a time, with the next one going out only after the previous one has received a response. Call the completionHandler after all requests have either failed or given a response.
+
+ @param requests The requests to be sent
+ @param progressHandler A handler called every time a response is received
+ @param completionHandler A handler to call when all requests have been responded to
+ */
+- (void)sendSequentialRequests:(NSArray<SDLRPCRequest *> *)requests progressHandler:(nullable SDLMultipleSequentialRequestProgressHandler)progressHandler completionHandler:(nullable SDLMultipleRequestCompletionHandler)completionHandler NS_SWIFT_NAME(sendSequential(requests:progressHandler:completionHandler:));
+
@end
NS_ASSUME_NONNULL_END
diff --git a/SmartDeviceLink/SDLManager.m b/SmartDeviceLink/SDLManager.m
index fa982a51b..390d42026 100644
--- a/SmartDeviceLink/SDLManager.m
+++ b/SmartDeviceLink/SDLManager.m
@@ -94,6 +94,10 @@ NS_ASSUME_NONNULL_BEGIN
self.lifecycleManager.delegate = delegate;
}
+- (NSArray<__kindof NSOperation *> *)pendingRPCTransactions {
+ return self.lifecycleManager.rpcOperationQueue.operations;
+}
+
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
- (nullable SDLProxy *)proxy {
@@ -112,6 +116,14 @@ NS_ASSUME_NONNULL_BEGIN
[self.lifecycleManager sendRequest:request withResponseHandler:handler];
}
+- (void)sendRequests:(NSArray<SDLRPCRequest *> *)requests progressHandler:(nullable SDLMultipleAsyncRequestProgressHandler)progressHandler completionHandler:(nullable SDLMultipleRequestCompletionHandler)completionHandler {
+ [self.lifecycleManager sendRequests:requests progressHandler:progressHandler completionHandler:completionHandler];
+}
+
+- (void)sendSequentialRequests:(NSArray<SDLRPCRequest *> *)requests progressHandler:(nullable SDLMultipleSequentialRequestProgressHandler)progressHandler completionHandler:(nullable SDLMultipleRequestCompletionHandler)completionHandler {
+ [self.lifecycleManager sendSequentialRequests:requests progressHandler:progressHandler completionHandler:completionHandler];
+}
+
@end
NS_ASSUME_NONNULL_END
diff --git a/SmartDeviceLink/SDLSequentialRPCRequestOperation.h b/SmartDeviceLink/SDLSequentialRPCRequestOperation.h
new file mode 100644
index 000000000..ee2a499a2
--- /dev/null
+++ b/SmartDeviceLink/SDLSequentialRPCRequestOperation.h
@@ -0,0 +1,24 @@
+//
+// SDLSequentialRPCRequestOperation.h
+// SmartDeviceLink
+//
+// Created by Joel Fischer on 1/31/18.
+// Copyright © 2018 smartdevicelink. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+#import "SDLAsynchronousOperation.h"
+#import "SDLLifecycleManager.h"
+
+@protocol SDLConnectionManagerType;
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface SDLSequentialRPCRequestOperation : SDLAsynchronousOperation
+
+- (instancetype)initWithConnectionManager:(id<SDLConnectionManagerType>)connectionManager requests:(NSArray<SDLRPCRequest *> *)requests progressHandler:(nullable SDLMultipleSequentialRequestProgressHandler)progressHandler completionHandler:(nullable SDLMultipleRequestCompletionHandler)completionHandler;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/SmartDeviceLink/SDLSequentialRPCRequestOperation.m b/SmartDeviceLink/SDLSequentialRPCRequestOperation.m
new file mode 100644
index 000000000..142812d40
--- /dev/null
+++ b/SmartDeviceLink/SDLSequentialRPCRequestOperation.m
@@ -0,0 +1,124 @@
+//
+// SDLSequentialRPCRequestOperation.m
+// SmartDeviceLink
+//
+// Created by Joel Fischer on 1/31/18.
+// Copyright © 2018 smartdevicelink. All rights reserved.
+//
+
+#import "SDLSequentialRPCRequestOperation.h"
+
+#import "SDLConnectionManagerType.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface SDLSequentialRPCRequestOperation ()
+
+@property (copy, nonatomic) NSArray<SDLRPCRequest *> *requests;
+@property (weak, nonatomic) id<SDLConnectionManagerType> connectionManager;
+@property (copy, nonatomic, nullable) SDLMultipleSequentialRequestProgressHandler progressHandler;
+@property (copy, nonatomic, nullable) SDLMultipleRequestCompletionHandler completionHandler;
+
+@property (strong, nonatomic) NSUUID *operationId;
+@property (assign, nonatomic) NSUInteger requestsComplete;
+@property (assign, nonatomic) NSUInteger currentRequestIndex;
+@property (assign, nonatomic, readonly) float percentComplete;
+@property (assign, nonatomic) BOOL requestFailed;
+
+@end
+
+@implementation SDLSequentialRPCRequestOperation {
+ BOOL executing;
+ BOOL finished;
+}
+
+- (instancetype)initWithConnectionManager:(id<SDLConnectionManagerType>)connectionManager requests:(NSArray<SDLRPCRequest *> *)requests progressHandler:(nullable SDLMultipleSequentialRequestProgressHandler)progressHandler completionHandler:(nullable SDLMultipleRequestCompletionHandler)completionHandler {
+ self = [super init];
+ if (!self) { return nil; }
+
+ executing = NO;
+ finished = NO;
+
+ _connectionManager = connectionManager;
+ _requests = requests;
+ _progressHandler = progressHandler;
+ _completionHandler = completionHandler;
+
+ _operationId = [NSUUID UUID];
+ _requestsComplete = 0;
+ _currentRequestIndex = 0;
+ _requestFailed = NO;
+
+ return self;
+}
+
+- (void)start {
+ [super start];
+
+ [self sdl_sendNextRequest];
+}
+
+- (void)sdl_sendNextRequest {
+ // The operation was canceled while in progress
+ if (self.cancelled) {
+ if (self.completionHandler != nil) {
+ self.completionHandler(NO);
+ }
+
+ [self finishOperation];
+ return;
+ }
+
+ // The operation is done, all requests have been sent and all responses received
+ if (self.currentRequestIndex >= self.requests.count) {
+ if (self.completionHandler != nil) {
+ self.completionHandler(!self.requestFailed);
+ }
+
+ [self finishOperation];
+ return;
+ }
+
+ // Send the next request
+ SDLRPCRequest *request = self.requests[self.currentRequestIndex];
+ [self.connectionManager sendConnectionRequest:request withResponseHandler:^(__kindof SDLRPCRequest * _Nullable request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error) {
+ self.requestsComplete++;
+
+ // If this request failed and no request has yet failed, set our internal request failed to YES
+ if (!self.requestFailed && error != nil) {
+ self.requestFailed = YES;
+ }
+
+ if (self.progressHandler != NULL) {
+ BOOL continueWithRemainingRequests = self.progressHandler(request, response, error, self.percentComplete);
+
+ // If the user decided to cancel, cancel for our next go around.
+ if (!continueWithRemainingRequests) {
+ [self cancel];
+ }
+ }
+
+ self.currentRequestIndex++;
+ [self sdl_sendNextRequest];
+ }];
+}
+
+#pragma mark - Getters
+
+- (float)percentComplete {
+ return (float)self.requestsComplete / (float)self.requests.count;
+}
+
+#pragma mark - Property Overrides
+
+- (nullable NSString *)name {
+ return [NSString stringWithFormat:@"%@ - %@", self.class, self.operationId];
+}
+
+- (NSOperationQueuePriority)queuePriority {
+ return NSOperationQueuePriorityNormal;
+}
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/SmartDeviceLink/SDLStreamingMediaLifecycleManager.m b/SmartDeviceLink/SDLStreamingMediaLifecycleManager.m
index 3478257f5..c21e5ffe2 100644
--- a/SmartDeviceLink/SDLStreamingMediaLifecycleManager.m
+++ b/SmartDeviceLink/SDLStreamingMediaLifecycleManager.m
@@ -291,7 +291,9 @@ typedef void(^SDLVideoCapabilityResponseHandler)(SDLVideoStreamingCapability *_N
[self sdl_sendBackgroundFrames];
[self.touchManager cancelPendingTouches];
- self.restartVideoStream = YES;
+
+ [self sdl_stopAudioSession];
+ [self sdl_stopVideoSession];
}
// Per Apple's guidelines: https://developer.apple.com/library/content/documentation/iPhone/Conceptual/iPhoneOSProgrammingGuide/StrategiesforHandlingAppStateTransitions/StrategiesforHandlingAppStateTransitions.html
@@ -767,7 +769,7 @@ typedef void(^SDLVideoCapabilityResponseHandler)(SDLVideoStreamingCapability *_N
SDLLogD(@"Requesting video capabilities");
SDLGetSystemCapability *getVideoCapabilityRequest = [[SDLGetSystemCapability alloc] initWithType:SDLSystemCapabilityTypeVideoStreaming];
- [self.connectionManager sendManagerRequest:getVideoCapabilityRequest withResponseHandler:^(__kindof SDLRPCRequest * _Nullable request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error) {
+ [self.connectionManager sendConnectionManagerRequest:getVideoCapabilityRequest withResponseHandler:^(__kindof SDLRPCRequest * _Nullable request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error) {
if (!response.success || [response isMemberOfClass:SDLGenericResponse.class]) {
SDLLogW(@"Video capabilities response failed: %@", error);
responseHandler(nil);
diff --git a/SmartDeviceLink/SDLTouchManager.m b/SmartDeviceLink/SDLTouchManager.m
index 40b3b1c54..b411619f1 100644
--- a/SmartDeviceLink/SDLTouchManager.m
+++ b/SmartDeviceLink/SDLTouchManager.m
@@ -215,8 +215,10 @@ static NSUInteger const MaximumNumberOfTouches = 2;
self.currentPinchGesture = [[SDLPinchGesture alloc] initWithFirstTouch:self.previousTouch secondTouch:touch];
self.previousPinchDistance = self.currentPinchGesture.distance;
if ([self.touchEventDelegate respondsToSelector:@selector(touchManager:pinchDidStartInView:atCenterPoint:)]) {
- UIView *hitView = (self.hitTester != nil) ? [self.hitTester viewForPoint:self.currentPinchGesture.center] : nil;
- [self.touchEventDelegate touchManager:self pinchDidStartInView:hitView atCenterPoint:self.currentPinchGesture.center];
+ dispatch_async(dispatch_get_main_queue(), ^{
+ UIView *hitView = (self.hitTester != nil) ? [self.hitTester viewForPoint:self.currentPinchGesture.center] : nil;
+ [self.touchEventDelegate touchManager:self pinchDidStartInView:hitView atCenterPoint:self.currentPinchGesture.center];
+ });
}
} break;
}
@@ -257,8 +259,10 @@ static NSUInteger const MaximumNumberOfTouches = 2;
_performingTouchType = SDLPerformingTouchTypePanningTouch;
if ([self.touchEventDelegate respondsToSelector:@selector(touchManager:panningDidStartInView:atPoint:)]) {
- UIView *hitView = (self.hitTester != nil) ? [self.hitTester viewForPoint:touch.location] : nil;
- [self.touchEventDelegate touchManager:self panningDidStartInView:hitView atPoint:touch.location];
+ dispatch_async(dispatch_get_main_queue(), ^{
+ UIView *hitView = (self.hitTester != nil) ? [self.hitTester viewForPoint:touch.location] : nil;
+ [self.touchEventDelegate touchManager:self panningDidStartInView:hitView atPoint:touch.location];
+ });
}
} break;
case SDLPerformingTouchTypePanningTouch: {
@@ -284,16 +288,20 @@ static NSUInteger const MaximumNumberOfTouches = 2;
[self sdl_setMultiTouchFingerTouchForTouch:touch];
if (self.currentPinchGesture.isValid) {
if ([self.touchEventDelegate respondsToSelector:@selector(touchManager:pinchDidEndInView:atCenterPoint:)]) {
- UIView *hitView = (self.hitTester != nil) ? [self.hitTester viewForPoint:self.currentPinchGesture.center] : nil;
- [self.touchEventDelegate touchManager:self pinchDidEndInView:hitView atCenterPoint:self.currentPinchGesture.center];
+ dispatch_async(dispatch_get_main_queue(), ^{
+ UIView *hitView = (self.hitTester != nil) ? [self.hitTester viewForPoint:self.currentPinchGesture.center] : nil;
+ [self.touchEventDelegate touchManager:self pinchDidEndInView:hitView atCenterPoint:self.currentPinchGesture.center];
+ });
}
self.currentPinchGesture = nil;
}
} break;
case SDLPerformingTouchTypePanningTouch: {
if ([self.touchEventDelegate respondsToSelector:@selector(touchManager:panningDidEndInView:atPoint:)]) {
- UIView *hitView = (self.hitTester != nil) ? [self.hitTester viewForPoint:touch.location] : nil;
- [self.touchEventDelegate touchManager:self panningDidEndInView:hitView atPoint:touch.location];
+ dispatch_async(dispatch_get_main_queue(), ^{
+ UIView *hitView = (self.hitTester != nil) ? [self.hitTester viewForPoint:touch.location] : nil;
+ [self.touchEventDelegate touchManager:self panningDidEndInView:hitView atPoint:touch.location];
+ });
}
} break;
case SDLPerformingTouchTypeSingleTouch: {
@@ -313,8 +321,10 @@ static NSUInteger const MaximumNumberOfTouches = 2;
CGPoint centerPoint = CGPointCenterOfPoints(touch.location,
self.singleTapTouch.location);
if ([self.touchEventDelegate respondsToSelector:@selector(touchManager:didReceiveDoubleTapForView:atPoint:)]) {
- UIView *hitView = (self.hitTester != nil) ? [self.hitTester viewForPoint:centerPoint] : nil;
- [self.touchEventDelegate touchManager:self didReceiveDoubleTapForView:hitView atPoint:centerPoint];
+ dispatch_async(dispatch_get_main_queue(), ^{
+ UIView *hitView = (self.hitTester != nil) ? [self.hitTester viewForPoint:centerPoint] : nil;
+ [self.touchEventDelegate touchManager:self didReceiveDoubleTapForView:hitView atPoint:centerPoint];
+ });
}
}
@@ -398,8 +408,10 @@ static NSUInteger const MaximumNumberOfTouches = 2;
strongSelf.singleTapTouch = nil;
[strongSelf sdl_cancelSingleTapTimer];
if ([strongSelf.touchEventDelegate respondsToSelector:@selector(touchManager:didReceiveSingleTapForView:atPoint:)]) {
- UIView *hitView = (self.hitTester != nil) ? [self.hitTester viewForPoint:point] : nil;
- [strongSelf.touchEventDelegate touchManager:strongSelf didReceiveSingleTapForView:hitView atPoint:point];
+ dispatch_async(dispatch_get_main_queue(), ^{
+ UIView *hitView = (self.hitTester != nil) ? [self.hitTester viewForPoint:point] : nil;
+ [strongSelf.touchEventDelegate touchManager:strongSelf didReceiveSingleTapForView:hitView atPoint:point];
+ });
}
});
}
diff --git a/SmartDeviceLink/SDLUploadFileOperation.m b/SmartDeviceLink/SDLUploadFileOperation.m
index fbeac9431..a084bd16a 100644
--- a/SmartDeviceLink/SDLUploadFileOperation.m
+++ b/SmartDeviceLink/SDLUploadFileOperation.m
@@ -121,7 +121,7 @@ NS_ASSUME_NONNULL_BEGIN
currentOffset += dataSize;
__weak typeof(self) weakself = self;
- [self.connectionManager sendManagerRequest:putFile withResponseHandler:^(__kindof SDLRPCRequest *_Nullable request, __kindof SDLRPCResponse *_Nullable response, NSError *_Nullable error) {
+ [self.connectionManager sendConnectionManagerRequest:putFile withResponseHandler:^(__kindof SDLRPCRequest *_Nullable request, __kindof SDLRPCResponse *_Nullable response, NSError *_Nullable error) {
typeof(weakself) strongself = weakself;
// Check if the upload process has been cancelled by another packet. If so, stop the upload process.
diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLAsynchronousRPCRequestOperationSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLAsynchronousRPCRequestOperationSpec.m
new file mode 100644
index 000000000..eed4efa81
--- /dev/null
+++ b/SmartDeviceLinkTests/DevAPISpecs/SDLAsynchronousRPCRequestOperationSpec.m
@@ -0,0 +1,105 @@
+#import <Quick/Quick.h>
+#import <Nimble/Nimble.h>
+
+#import "SDLAddCommand.h"
+#import "SDLAsynchronousRPCRequestOperation.h"
+#import "SDLSpecUtilities.h"
+
+#import "TestMultipleRequestsConnectionManager.h"
+#import "TestRequestProgressResponse.h"
+
+QuickSpecBegin(SDLAsynchronousRPCRequestOperationSpec)
+
+describe(@"sending asynchronous requests", ^{
+ __block TestMultipleRequestsConnectionManager *testConnectionManager = nil;
+ __block SDLAsynchronousRPCRequestOperation *testOperation = nil;
+ __block NSOperationQueue *testOperationQueue = nil;
+
+ __block NSMutableArray<SDLAddCommand *> *sendRequests = nil;
+ __block NSMutableDictionary<NSNumber<SDLInt> *, TestRequestProgressResponse *> *testProgressResponses;
+ __block NSMutableArray<SDLRPCResponse *> *resultResponses = [NSMutableArray array];
+
+ beforeEach(^{
+ testOperation = nil;
+ testConnectionManager = [[TestMultipleRequestsConnectionManager alloc] init];
+
+ sendRequests = [NSMutableArray array];
+ testProgressResponses = [NSMutableDictionary dictionary];
+
+ testOperationQueue = [[NSOperationQueue alloc] init];
+ testOperationQueue.name = @"com.sdl.asynchronousRPCOp.testqueue";
+ testOperationQueue.maxConcurrentOperationCount = 1;
+ });
+
+ context(@"where all requests succeed", ^{
+ beforeEach(^{
+ for (int i = 0; i < 3; i++) {
+ SDLAddCommand *addCommand = [[SDLAddCommand alloc] init];
+ addCommand.correlationID = @(i);
+ [sendRequests addObject:addCommand];
+
+ testConnectionManager.responses[addCommand.correlationID] = [SDLSpecUtilities addCommandRPCResponseWithCorrelationId:addCommand.correlationID];
+ testProgressResponses[addCommand.correlationID] = [[TestRequestProgressResponse alloc] initWithCorrelationId:addCommand.correlationID percentComplete:((float)(i+1)/3.0) error:nil];
+ }
+ });
+
+ it(@"should correctly send all requests", ^{
+ testOperation = [[SDLAsynchronousRPCRequestOperation alloc] initWithConnectionManager:testConnectionManager requests:sendRequests.copy progressHandler:^(__kindof SDLRPCRequest * _Nonnull request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error, float percentComplete) {
+ TestRequestProgressResponse *progressResponse = testProgressResponses[request.correlationID];
+
+ expect(progressResponse.percentComplete).to(beCloseTo(percentComplete));
+ expect(response).toNot(beNil());
+ expect(error).to(beNil());
+
+ [resultResponses addObject:response];
+ } completionHandler:^(BOOL success) {
+ expect(resultResponses).to(haveCount(3));
+ expect(success).to(beTruthy());
+ }];
+
+ [testOperationQueue addOperation:testOperation];
+ [NSThread sleepForTimeInterval:0.5];
+ });
+ });
+
+ context(@"where not all requests succeed", ^{
+ __block NSError *testError = [NSError errorWithDomain:@"com.test" code:-3 userInfo:nil];
+
+ beforeEach(^{
+ for (int i = 0; i < 3; i++) {
+ SDLAddCommand *addCommand = [[SDLAddCommand alloc] init];
+ addCommand.correlationID = @(i);
+ [sendRequests addObject:addCommand];
+
+ NSError *error = (i == 1) ? testError : nil;
+
+ testConnectionManager.responses[addCommand.correlationID] = [SDLSpecUtilities addCommandRPCResponseWithCorrelationId:addCommand.correlationID];
+ testProgressResponses[addCommand.correlationID] = [[TestRequestProgressResponse alloc] initWithCorrelationId:addCommand.correlationID percentComplete:((float)(i+1)/3.0) error:error];
+ }
+ });
+
+ it(@"should pass along the error", ^{
+ testOperation = [[SDLAsynchronousRPCRequestOperation alloc] initWithConnectionManager:testConnectionManager requests:sendRequests.copy progressHandler:^(__kindof SDLRPCRequest * _Nonnull request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error, float percentComplete) {
+ TestRequestProgressResponse *progressResponse = testProgressResponses[request.correlationID];
+
+ expect(progressResponse.percentComplete).to(beCloseTo(percentComplete));
+ expect(response).toNot(beNil());
+ if (![request.correlationID isEqualToNumber:@1]) {
+ expect(error).to(beNil());
+ } else {
+ expect(error.code).to(equal(testError.code));
+ }
+
+ [resultResponses addObject:response];
+ } completionHandler:^(BOOL success) {
+ expect(resultResponses).to(haveCount(3));
+ expect(success).to(beFalsy());
+ }];
+
+ [testOperationQueue addOperation:testOperation];
+ [NSThread sleepForTimeInterval:0.5];
+ });
+ });
+});
+
+QuickSpecEnd
diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLFileManagerSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLFileManagerSpec.m
index f68b8b1e5..c87fe59c5 100644
--- a/SmartDeviceLinkTests/DevAPISpecs/SDLFileManagerSpec.m
+++ b/SmartDeviceLinkTests/DevAPISpecs/SDLFileManagerSpec.m
@@ -15,7 +15,7 @@
#import "SDLRPCResponse.h"
#import "TestConnectionManager.h"
#import "TestMultipleFilesConnectionManager.h"
-#import "TestProgressResponse.h"
+#import "TestFileProgressResponse.h"
#import "TestResponse.h"
typedef NSString SDLFileManagerState;
@@ -712,6 +712,7 @@ describe(@"SDLFileManager uploading/deleting multiple files", ^{
testConnectionManager.responses = testConnectionManagerResponses;
});
+ // TODO: This should use itBehavesLike
afterEach(^{
waitUntilTimeout(10, ^(void (^done)(void)){
[testFileManager uploadFiles:testSDLFiles completionHandler:^(NSError * _Nullable error) {
@@ -1039,30 +1040,30 @@ describe(@"SDLFileManager uploading/deleting multiple files", ^{
[expectedSuccessfulFileNames addObject:testFileName];
testFileManagerResponses[testFileName] = [[TestResponse alloc] initWithResponse:successfulResponse error:nil];
- testTotalBytesUploaded += testSDLFile.fileSize;
- testFileManagerProgressResponses[testFileName] = [[TestProgressResponse alloc] initWithFileName:testFileName testUploadPercentage:testTotalBytesUploaded / testTotalBytesToUpload error:nil];
- }
- expectedSpaceLeft = @(testSpaceAvailable);
- testConnectionManager.responses = testFileManagerResponses;
- });
+ testTotalBytesUploaded += testSDLFile.fileSize;
+ testFileManagerProgressResponses[testFileName] = [[TestFileProgressResponse alloc] initWithFileName:testFileName testUploadPercentage:testTotalBytesUploaded / testTotalBytesToUpload error:nil];
+ }
+ expectedSpaceLeft = @(testSpaceAvailable);
+ testConnectionManager.responses = testFileManagerResponses;
});
+ });
- afterEach(^{
- waitUntilTimeout(10, ^(void (^done)(void)){
- [testFileManager uploadFiles:testSDLFiles progressHandler:^BOOL(SDLFileName * _Nonnull fileName, float uploadPercentage, NSError * _Nullable error) {
- TestProgressResponse *testProgressResponse = testFileManagerProgressResponses[fileName];
- expect(fileName).to(equal(testProgressResponse.testFileName));
- expect(uploadPercentage).to(equal(testProgressResponse.testUploadPercentage));
- expect(error).to(testProgressResponse.testError == nil ? beNil() : equal(testProgressResponse.testError));
- return YES;
- } completionHandler:^(NSError * _Nullable error) {
- expect(error).to(beNil());
- expect(testFileManager.bytesAvailable).to(equal(expectedSpaceLeft));
- done();
- }];
- });
+ afterEach(^{
+ waitUntilTimeout(10, ^(void (^done)(void)){
+ [testFileManager uploadFiles:testSDLFiles progressHandler:^BOOL(SDLFileName * _Nonnull fileName, float uploadPercentage, NSError * _Nullable error) {
+ TestFileProgressResponse *testProgressResponse = testFileManagerProgressResponses[fileName];
+ expect(fileName).to(equal(testProgressResponse.testFileName));
+ expect(uploadPercentage).to(equal(testProgressResponse.testUploadPercentage));
+ expect(error).to(testProgressResponse.testError == nil ? beNil() : equal(testProgressResponse.testError));
+ return YES;
+ } completionHandler:^(NSError * _Nullable error) {
+ expect(error).to(beNil());
+ expect(testFileManager.bytesAvailable).to(equal(expectedSpaceLeft));
+ done();
+ }];
});
});
+ });
describe(@"When uploading artworks", ^{
__block NSMutableArray<SDLArtwork *> *testArtworks = nil;
@@ -1185,14 +1186,14 @@ describe(@"SDLFileManager uploading/deleting multiple files", ^{
}
testResponses[testFileName] = [[TestResponse alloc] initWithResponse:successfulResponse error:nil];
- testProgressResponses[testFileName] = [[TestProgressResponse alloc] initWithFileName:testFileName testUploadPercentage:0.0 error:nil];
+ testProgressResponses[testFileName] = [[TestFileProgressResponse alloc] initWithFileName:testFileName testUploadPercentage:0.0 error:nil];
}
testConnectionManager.responses = testResponses;
waitUntilTimeout(10, ^(void (^done)(void)){
[testFileManager uploadFiles:testSDLFiles progressHandler:^(NSString * _Nonnull fileName, float uploadPercentage, NSError * _Nullable error) {
// Once operations are canceled, the order in which the operations complete is random, so the upload percentage and the error message can vary. This means we can not test the error message or upload percentage it will be different every test run.
- TestProgressResponse *testProgressResponse = testProgressResponses[fileName];
+ TestFileProgressResponse *testProgressResponse = testProgressResponses[fileName];
expect(fileName).to(equal(testProgressResponse.testFileName));
NSString *cancelFileName = [NSString stringWithFormat:@"%@%d", testFileNameBase, testCancelIndex];
@@ -1268,7 +1269,7 @@ describe(@"SDLFileManager uploading/deleting multiple files", ^{
}
testResponses[testFileName] = [[TestResponse alloc] initWithResponse:successfulResponse error:nil];
- testProgressResponses[testFileName] = [[TestProgressResponse alloc] initWithFileName:testFileName testUploadPercentage:0.0 error:nil];
+ testProgressResponses[testFileName] = [[TestFileProgressResponse alloc] initWithFileName:testFileName testUploadPercentage:0.0 error:nil];
}
for(int i = 0; i < testOtherFileCount; i += 1) {
@@ -1287,7 +1288,7 @@ describe(@"SDLFileManager uploading/deleting multiple files", ^{
waitUntilTimeout(10, ^(void (^done)(void)){
[testFileManager uploadFiles:testSDLFiles progressHandler:^(NSString * _Nonnull fileName, float uploadPercentage, NSError * _Nullable error) {
// Once operations are canceled, the order in which the operations complete is random, so the upload percentage and the error message can vary. This means we can not test the error message or upload percentage it will be different every test run.
- TestProgressResponse *testProgressResponse = testProgressResponses[fileName];
+ TestFileProgressResponse *testProgressResponse = testProgressResponses[fileName];
expect(fileName).to(equal(testProgressResponse.testFileName));
NSString *cancelFileName = [NSString stringWithFormat:@"%@%d", testFileNameBase, testCancelIndex];
diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLLifecycleManagerSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLLifecycleManagerSpec.m
index 54053e58c..9cf114aeb 100644
--- a/SmartDeviceLinkTests/DevAPISpecs/SDLLifecycleManagerSpec.m
+++ b/SmartDeviceLinkTests/DevAPISpecs/SDLLifecycleManagerSpec.m
@@ -42,15 +42,12 @@ QuickConfigurationBegin(SendingRPCsConfiguration)
+ (void)configure:(Configuration *)configuration {
sharedExamples(@"unable to send an RPC", ^(QCKDSLSharedExampleContext exampleContext) {
it(@"cannot publicly send RPCs", ^{
- __block NSError *testError = nil;
SDLLifecycleManager *testManager = exampleContext()[@"manager"];
SDLShow *testShow = [[SDLShow alloc] initWithMainField1:@"test" mainField2:nil alignment:nil];
[testManager sendRequest:testShow withResponseHandler:^(__kindof SDLRPCRequest * _Nullable request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error) {
- testError = error;
+ expect(error).to(equal([NSError sdl_lifecycle_notReadyError]));
}];
-
- expect(testError).to(equal([NSError sdl_lifecycle_notReadyError]));
});
});
}
@@ -327,8 +324,8 @@ describe(@"a lifecycle manager", ^{
[testManager.lifecycleStateMachine setToState:SDLLifecycleStateReady fromOldState:nil callEnterTransition:YES];
- expect(@(readyHandlerSuccess)).to(equal(@YES));
- expect(readyHandlerError).to(beNil());
+ expect(@(readyHandlerSuccess)).toEventually(equal(@YES));
+ expect(readyHandlerError).toEventually(beNil());
});
});
@@ -341,10 +338,10 @@ describe(@"a lifecycle manager", ^{
[testManager.lifecycleStateMachine setToState:SDLLifecycleStateReady fromOldState:nil callEnterTransition:YES];
- expect(@(readyHandlerSuccess)).to(equal(@YES));
- expect(readyHandlerError).toNot(beNil());
- expect(@(readyHandlerError.code)).to(equal(@(SDLManagerErrorRegistrationSuccessWithWarning)));
- expect(readyHandlerError.userInfo[NSLocalizedFailureReasonErrorKey]).to(match(response.info));
+ expect(@(readyHandlerSuccess)).toEventually(equal(@YES));
+ expect(readyHandlerError).toEventuallyNot(beNil());
+ expect(@(readyHandlerError.code)).toEventually(equal(@(SDLManagerErrorRegistrationSuccessWithWarning)));
+ expect(readyHandlerError.userInfo[NSLocalizedFailureReasonErrorKey]).toEventually(match(response.info));
});
});
diff --git a/SmartDeviceLinkTests/DevAPISpecs/SDLSequentialRPCRequestOperationSpec.m b/SmartDeviceLinkTests/DevAPISpecs/SDLSequentialRPCRequestOperationSpec.m
new file mode 100644
index 000000000..0c121d22c
--- /dev/null
+++ b/SmartDeviceLinkTests/DevAPISpecs/SDLSequentialRPCRequestOperationSpec.m
@@ -0,0 +1,136 @@
+#import <Quick/Quick.h>
+#import <Nimble/Nimble.h>
+
+#import "SDLAddCommand.h"
+#import "SDLAddCommandResponse.h"
+#import "SDLRPCResponse.h"
+#import "SDLSequentialRPCRequestOperation.h"
+#import "SDLSpecUtilities.h"
+
+#import "TestMultipleRequestsConnectionManager.h"
+#import "TestRequestProgressResponse.h"
+
+
+QuickSpecBegin(SDLSequentialRPCRequestOperationSpec)
+
+describe(@"Sending sequential requests", ^{
+ __block TestMultipleRequestsConnectionManager *testConnectionManager = nil;
+ __block SDLSequentialRPCRequestOperation *testOperation = nil;
+ __block NSOperationQueue *testOperationQueue = nil;
+
+ __block NSMutableArray<SDLAddCommand *> *sendRequests = nil;
+ __block NSMutableDictionary<NSNumber<SDLInt> *, TestRequestProgressResponse *> *testProgressResponses;
+ __block NSMutableArray<SDLRPCResponse *> *resultResponses = [NSMutableArray array];
+
+ beforeEach(^{
+ testOperation = nil;
+ testConnectionManager = [[TestMultipleRequestsConnectionManager alloc] init];
+
+ sendRequests = [NSMutableArray array];
+ testProgressResponses = [NSMutableDictionary dictionary];
+
+ testOperationQueue = [[NSOperationQueue alloc] init];
+ testOperationQueue.name = @"com.sdl.sequentialrpcop.testqueue";
+ testOperationQueue.maxConcurrentOperationCount = 1;
+ });
+
+ context(@"where all requests succeed", ^{
+ beforeEach(^{
+ for (int i = 0; i < 3; i++) {
+ SDLAddCommand *addCommand = [[SDLAddCommand alloc] init];
+ addCommand.correlationID = @(i);
+ [sendRequests addObject:addCommand];
+
+ testConnectionManager.responses[addCommand.correlationID] = [SDLSpecUtilities addCommandRPCResponseWithCorrelationId:addCommand.correlationID];
+ testProgressResponses[addCommand.correlationID] = [[TestRequestProgressResponse alloc] initWithCorrelationId:addCommand.correlationID percentComplete:((float)(i+1)/3.0) error:nil];
+ }
+ });
+
+ context(@"where the requests are continued", ^{
+ it(@"should correctly send all requests", ^{
+ testOperation = [[SDLSequentialRPCRequestOperation alloc] initWithConnectionManager:testConnectionManager requests:sendRequests.copy progressHandler:^BOOL(__kindof SDLRPCRequest * _Nonnull request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error, float percentComplete) {
+ TestRequestProgressResponse *progressResponse = testProgressResponses[request.correlationID];
+
+ expect(progressResponse.percentComplete).to(beCloseTo(percentComplete));
+ expect(response).toNot(beNil());
+ expect(error).to(beNil());
+
+ [resultResponses addObject:response];
+
+ return YES;
+ } completionHandler:^(BOOL success) {
+ expect(resultResponses).to(haveCount(3));
+ expect(success).to(beTruthy());
+ }];
+
+ [testOperationQueue addOperation:testOperation];
+ [NSThread sleepForTimeInterval:0.5];
+ });
+ });
+
+ context(@"where the requests are canceled", ^{
+ it(@"should only send the one before cancellation", ^{
+ testOperation = [[SDLSequentialRPCRequestOperation alloc] initWithConnectionManager:testConnectionManager requests:sendRequests.copy progressHandler:^BOOL(__kindof SDLRPCRequest * _Nonnull request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error, float percentComplete) {
+ TestRequestProgressResponse *progressResponse = testProgressResponses[request.correlationID];
+
+ expect(progressResponse.percentComplete).to(beCloseTo(percentComplete));
+ expect(response).toNot(beNil());
+ expect(error).to(beNil());
+
+ [resultResponses addObject:response];
+
+ return NO;
+ } completionHandler:^(BOOL success) {
+ expect(resultResponses).to(haveCount(1));
+ expect(success).to(beFalsy());
+ }];
+
+ [testOperationQueue addOperation:testOperation];
+ [NSThread sleepForTimeInterval:0.5];
+ });
+ });
+ });
+
+ context(@"where not all requests succeed", ^{
+ __block NSError *testError = [NSError errorWithDomain:@"com.test" code:-3 userInfo:nil];
+
+ beforeEach(^{
+ for (int i = 0; i < 3; i++) {
+ SDLAddCommand *addCommand = [[SDLAddCommand alloc] init];
+ addCommand.correlationID = @(i);
+ [sendRequests addObject:addCommand];
+
+ NSError *error = (i == 1) ? testError : nil;
+
+ testConnectionManager.responses[addCommand.correlationID] = [SDLSpecUtilities addCommandRPCResponseWithCorrelationId:addCommand.correlationID];
+ testProgressResponses[addCommand.correlationID] = [[TestRequestProgressResponse alloc] initWithCorrelationId:addCommand.correlationID percentComplete:((float)(i+1)/3.0) error:error];
+ }
+ });
+
+ it(@"should pass along the error", ^{
+ testOperation = [[SDLSequentialRPCRequestOperation alloc] initWithConnectionManager:testConnectionManager requests:sendRequests.copy progressHandler:^BOOL(__kindof SDLRPCRequest * _Nonnull request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error, float percentComplete) {
+ TestRequestProgressResponse *progressResponse = testProgressResponses[request.correlationID];
+
+ expect(progressResponse.percentComplete).to(beCloseTo(percentComplete));
+ expect(response).toNot(beNil());
+ if (![request.correlationID isEqualToNumber:@1]) {
+ expect(error).to(beNil());
+ } else {
+ expect(error.code).to(equal(testError.code));
+ }
+
+ [resultResponses addObject:response];
+
+ return YES;
+ } completionHandler:^(BOOL success) {
+ expect(resultResponses).to(haveCount(3));
+ expect(success).to(beFalsy());
+ }];
+
+ [testOperationQueue addOperation:testOperation];
+ [NSThread sleepForTimeInterval:0.5];
+ });
+ });
+});
+
+QuickSpecEnd
diff --git a/SmartDeviceLinkTests/ProxySpecs/SDLHapticManagerSpec.m b/SmartDeviceLinkTests/ProxySpecs/SDLHapticManagerSpec.m
index 8413e6abf..c7c339ac3 100644
--- a/SmartDeviceLinkTests/ProxySpecs/SDLHapticManagerSpec.m
+++ b/SmartDeviceLinkTests/ProxySpecs/SDLHapticManagerSpec.m
@@ -49,7 +49,7 @@ describe(@"the haptic manager", ^{
uiWindow.rootViewController = uiViewController;
- OCMExpect([[sdlLifecycleManager stub] sendManagerRequest:[OCMArg checkWithBlock:^BOOL(id value){
+ OCMExpect([[sdlLifecycleManager stub] sendConnectionManagerRequest:[OCMArg checkWithBlock:^BOOL(id value){
BOOL isFirstArg = [value isKindOfClass:[SDLSendHapticData class]];
if(isFirstArg) {
sentHapticRequest = value;
diff --git a/SmartDeviceLinkTests/TestProgressResponse.h b/SmartDeviceLinkTests/TestFileProgressResponse.h
index a98ccc5c4..f7b28ad52 100644
--- a/SmartDeviceLinkTests/TestProgressResponse.h
+++ b/SmartDeviceLinkTests/TestFileProgressResponse.h
@@ -1,5 +1,5 @@
//
-// TestProgressResponse.h
+// TestFileProgressResponse.h
// SmartDeviceLink-iOS
//
// Created by Nicole on 8/17/17.
@@ -8,7 +8,7 @@
#import <Foundation/Foundation.h>
-@interface TestProgressResponse : NSObject
+@interface TestFileProgressResponse : NSObject
@property (strong, nonatomic) NSString *testFileName;
@property (nonatomic) float testUploadPercentage;
diff --git a/SmartDeviceLinkTests/TestProgressResponse.m b/SmartDeviceLinkTests/TestFileProgressResponse.m
index 089d297db..6711fcb1f 100644
--- a/SmartDeviceLinkTests/TestProgressResponse.m
+++ b/SmartDeviceLinkTests/TestFileProgressResponse.m
@@ -6,9 +6,9 @@
// Copyright © 2017 smartdevicelink. All rights reserved.
//
-#import "TestProgressResponse.h"
+#import "TestFileProgressResponse.h"
-@implementation TestProgressResponse
+@implementation TestFileProgressResponse
- (instancetype)initWithFileName:(NSString *)testFileName testUploadPercentage:(float)testUploadPercentage error:(NSError *)testError {
self = [super init];
diff --git a/SmartDeviceLinkTests/TestMultipleFilesConnectionManager.m b/SmartDeviceLinkTests/TestMultipleFilesConnectionManager.m
index abaa09d31..7eb43733f 100644
--- a/SmartDeviceLinkTests/TestMultipleFilesConnectionManager.m
+++ b/SmartDeviceLinkTests/TestMultipleFilesConnectionManager.m
@@ -18,8 +18,8 @@ NS_ASSUME_NONNULL_BEGIN
@implementation TestMultipleFilesConnectionManager
-- (void)sendManagerRequest:(__kindof SDLRPCRequest *)request withResponseHandler:(nullable SDLResponseHandler)handler {
- [super sendManagerRequest:request withResponseHandler:handler];
+- (void)sendConnectionRequest:(__kindof SDLRPCRequest *)request withResponseHandler:(nullable SDLResponseHandler)handler {
+ [super sendConnectionRequest:request withResponseHandler:handler];
if ([[request name] isEqualToString:SDLNamePutFile]) {
SDLPutFile *putfileRequest = (SDLPutFile *)request;
diff --git a/SmartDeviceLinkTests/TestUtilities/SDLSpecUtilities.h b/SmartDeviceLinkTests/TestUtilities/SDLSpecUtilities.h
new file mode 100644
index 000000000..a42c2b561
--- /dev/null
+++ b/SmartDeviceLinkTests/TestUtilities/SDLSpecUtilities.h
@@ -0,0 +1,20 @@
+//
+// SDLSpecUtilities.h
+// SmartDeviceLinkTests
+//
+// Created by Joel Fischer on 2/8/18.
+// Copyright © 2018 smartdevicelink. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+#import "NSNumber+NumberType.h"
+
+@class SDLAddCommandResponse;
+@class TestResponse;
+
+@interface SDLSpecUtilities : NSObject
+
++ (TestResponse *)addCommandRPCResponseWithCorrelationId:(NSNumber<SDLInt> *)correlationId;
+
+@end
diff --git a/SmartDeviceLinkTests/TestUtilities/SDLSpecUtilities.m b/SmartDeviceLinkTests/TestUtilities/SDLSpecUtilities.m
new file mode 100644
index 000000000..68259990d
--- /dev/null
+++ b/SmartDeviceLinkTests/TestUtilities/SDLSpecUtilities.m
@@ -0,0 +1,28 @@
+//
+// SDLSpecUtilities.m
+// SmartDeviceLinkTests
+//
+// Created by Joel Fischer on 2/8/18.
+// Copyright © 2018 smartdevicelink. All rights reserved.
+//
+
+#import "SDLSpecUtilities.h"
+
+#import "NSNumber+NumberType.h"
+#import "SDLAddCommandResponse.h"
+
+#import "TestResponse.h"
+
+@implementation SDLSpecUtilities
+
++ (TestResponse *)addCommandRPCResponseWithCorrelationId:(NSNumber<SDLInt> *)correlationId {
+ SDLAddCommandResponse *response = [[SDLAddCommandResponse alloc] init];
+ response.success = @YES;
+ response.correlationID = correlationId;
+
+ TestResponse *testResponse = [[TestResponse alloc] initWithResponse:response error:nil];
+
+ return testResponse;
+}
+
+@end
diff --git a/SmartDeviceLinkTests/TestUtilities/TestConnectionManager.m b/SmartDeviceLinkTests/TestUtilities/TestConnectionManager.m
index bd9bd939d..adb494fb6 100644
--- a/SmartDeviceLinkTests/TestUtilities/TestConnectionManager.m
+++ b/SmartDeviceLinkTests/TestUtilities/TestConnectionManager.m
@@ -19,18 +19,22 @@ NS_ASSUME_NONNULL_BEGIN
if (!self) {
return nil;
}
-
+
_receivedRequests = [NSMutableArray<__kindof SDLRPCRequest *> array];
return self;
}
-- (void)sendManagerRequest:(__kindof SDLRPCRequest *)request withResponseHandler:(nullable SDLResponseHandler)handler {
+- (void)sendConnectionRequest:(__kindof SDLRPCRequest *)request withResponseHandler:(nullable SDLResponseHandler)handler {
self.lastRequestBlock = handler;
request.correlationID = [self test_nextCorrelationID];
[self.receivedRequests addObject:request];
}
+- (void)sendConnectionManagerRequest:(__kindof SDLRPCRequest *)request withResponseHandler:(nullable SDLResponseHandler)handler {
+ [self sendConnectionRequest:request withResponseHandler:handler];
+}
+
- (void)respondToLastRequestWithResponse:(__kindof SDLRPCResponse *)response {
[self respondToLastRequestWithResponse:response error:nil];
}
diff --git a/SmartDeviceLinkTests/TestUtilities/TestMultipleRequestsConnectionManager.h b/SmartDeviceLinkTests/TestUtilities/TestMultipleRequestsConnectionManager.h
new file mode 100644
index 000000000..cf0c8bfc7
--- /dev/null
+++ b/SmartDeviceLinkTests/TestUtilities/TestMultipleRequestsConnectionManager.h
@@ -0,0 +1,20 @@
+//
+// TestMultipleRequestsConnectionManager.h
+// SmartDeviceLinkTests
+//
+// Created by Joel Fischer on 2/8/18.
+// Copyright © 2018 smartdevicelink. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+#import "TestConnectionManager.h"
+
+@interface TestMultipleRequestsConnectionManager : TestConnectionManager
+
+/**
+ * A response and error to pass into the last request's block
+ */
+@property (copy, nonatomic) NSMutableDictionary *responses;
+
+@end
diff --git a/SmartDeviceLinkTests/TestUtilities/TestMultipleRequestsConnectionManager.m b/SmartDeviceLinkTests/TestUtilities/TestMultipleRequestsConnectionManager.m
new file mode 100644
index 000000000..8c6cc8b06
--- /dev/null
+++ b/SmartDeviceLinkTests/TestUtilities/TestMultipleRequestsConnectionManager.m
@@ -0,0 +1,39 @@
+//
+// TestMultipleRequestsConnectionManager.m
+// SmartDeviceLinkTests
+//
+// Created by Joel Fischer on 2/8/18.
+// Copyright © 2018 smartdevicelink. All rights reserved.
+//
+
+#import "TestMultipleRequestsConnectionManager.h"
+
+#import "SDLAddCommand.h"
+#import "SDLAddCommandResponse.h"
+#import "SDLNames.h"
+#import "TestResponse.h"
+
+@implementation TestMultipleRequestsConnectionManager
+
+- (instancetype)init {
+ self = [super init];
+ if (!self) { return nil; }
+
+ _responses = [[NSMutableDictionary alloc] init];
+
+ return self;
+}
+
+- (void)sendConnectionRequest:(__kindof SDLRPCRequest *)request withResponseHandler:(SDLResponseHandler)handler {
+ [super sendConnectionRequest:request withResponseHandler:handler];
+
+ NSAssert([request.name isEqualToString:SDLNameAddCommand], @"The TestMultipleRequestsConnectionManager is only setup for SDLAddCommand");
+
+ SDLAddCommand *addCommand = (SDLAddCommand *)request;
+ TestResponse *response = self.responses[addCommand.correlationID];
+
+ if (response == nil || handler == nil) { return; }
+ handler(request, response.testResponse, response.testError);
+}
+
+@end
diff --git a/SmartDeviceLinkTests/TestUtilities/TestRequestProgressResponse.h b/SmartDeviceLinkTests/TestUtilities/TestRequestProgressResponse.h
new file mode 100644
index 000000000..2b823398b
--- /dev/null
+++ b/SmartDeviceLinkTests/TestUtilities/TestRequestProgressResponse.h
@@ -0,0 +1,21 @@
+//
+// TestRequestProgressResponse.h
+// SmartDeviceLinkTests
+//
+// Created by Joel Fischer on 2/8/18.
+// Copyright © 2018 smartdevicelink. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+#import "NSNumber+NumberType.h"
+
+@interface TestRequestProgressResponse : NSObject
+
+@property (strong, nonatomic) NSNumber<SDLInt> *correlationId;
+@property (nonatomic) float percentComplete;
+@property (strong, nonatomic) NSError *error;
+
+- (instancetype)initWithCorrelationId:(NSNumber<SDLInt> *)correlationId percentComplete:(float)percentComplete error:(NSError *)error;
+
+@end
diff --git a/SmartDeviceLinkTests/TestUtilities/TestRequestProgressResponse.m b/SmartDeviceLinkTests/TestUtilities/TestRequestProgressResponse.m
new file mode 100644
index 000000000..64afa7107
--- /dev/null
+++ b/SmartDeviceLinkTests/TestUtilities/TestRequestProgressResponse.m
@@ -0,0 +1,24 @@
+//
+// TestRequestProgressResponse.m
+// SmartDeviceLinkTests
+//
+// Created by Joel Fischer on 2/8/18.
+// Copyright © 2018 smartdevicelink. All rights reserved.
+//
+
+#import "TestRequestProgressResponse.h"
+
+@implementation TestRequestProgressResponse
+
+- (instancetype)initWithCorrelationId:(NSNumber<SDLInt> *)correlationId percentComplete:(float)percentComplete error:(NSError *)error {
+ self = [super init];
+ if (!self) { return nil; }
+
+ _correlationId = correlationId;
+ _percentComplete = percentComplete;
+ _error = error;
+
+ return self;
+}
+
+@end
diff --git a/SmartDeviceLink_Example/Classes/ProxyManager.m b/SmartDeviceLink_Example/Classes/ProxyManager.m
index 03e0d0f96..862d2dd86 100644
--- a/SmartDeviceLink_Example/Classes/ProxyManager.m
+++ b/SmartDeviceLink_Example/Classes/ProxyManager.m
@@ -20,12 +20,6 @@ typedef NS_ENUM(NSUInteger, SDLHMIFirstState) {
SDLHMIFirstStateFull
};
-typedef NS_ENUM(NSUInteger, SDLHMIInitialShowState) {
- SDLHMIInitialShowStateNone,
- SDLHMIInitialShowStateDataAvailable,
- SDLHMIInitialShowStateShown
-};
-
NS_ASSUME_NONNULL_BEGIN
@@ -33,7 +27,6 @@ NS_ASSUME_NONNULL_BEGIN
// Describes the first time the HMI state goes non-none and full.
@property (assign, nonatomic) SDLHMIFirstState firstTimeState;
-@property (assign, nonatomic) SDLHMIInitialShowState initialShowState;
@property (assign, nonatomic) BOOL areImagesVisible;
@end
@@ -61,32 +54,30 @@ NS_ASSUME_NONNULL_BEGIN
_state = ProxyStateStopped;
_firstTimeState = SDLHMIFirstStateNone;
- _initialShowState = SDLHMIInitialShowStateNone;
- _areImagesVisible = true;
-
+ _areImagesVisible = YES;
+
return self;
}
- (void)startIAP {
- [self sdlex_updateProxyState:ProxyStateSearchingForConnection];
// Check for previous instance of sdlManager
if (self.sdlManager) { return; }
+ [self sdlex_updateProxyState:ProxyStateSearchingForConnection];
SDLLifecycleConfiguration *lifecycleConfig = [self.class sdlex_setLifecycleConfigurationPropertiesOnConfiguration:[SDLLifecycleConfiguration defaultConfigurationWithAppName:SDLAppName appId:SDLAppId]];
-
- SDLConfiguration *config = [SDLConfiguration configurationWithLifecycle:lifecycleConfig lockScreen:[SDLLockScreenConfiguration enabledConfiguration] logging:[SDLLogConfiguration debugConfiguration]];
- self.sdlManager = [[SDLManager alloc] initWithConfiguration:config delegate:self];
-
- [self startManager];
+ [self sdlex_startWithLifecycleConfiguration:lifecycleConfig];
}
- (void)startTCP {
- [self sdlex_updateProxyState:ProxyStateSearchingForConnection];
// Check for previous instance of sdlManager
if (self.sdlManager) { return; }
+ [self sdlex_updateProxyState:ProxyStateSearchingForConnection];
SDLLifecycleConfiguration *lifecycleConfig = [self.class sdlex_setLifecycleConfigurationPropertiesOnConfiguration:[SDLLifecycleConfiguration debugConfigurationWithAppName:SDLAppName appId:SDLAppId ipAddress:[Preferences sharedPreferences].ipAddress port:[Preferences sharedPreferences].port]];
- SDLConfiguration *config = [SDLConfiguration configurationWithLifecycle:lifecycleConfig lockScreen:[SDLLockScreenConfiguration enabledConfiguration] logging:[SDLLogConfiguration debugConfiguration]];
- self.sdlManager = [[SDLManager alloc] initWithConfiguration:config delegate:self];
+ [self sdlex_startWithLifecycleConfiguration:lifecycleConfig];
+}
+- (void)sdlex_startWithLifecycleConfiguration:(SDLLifecycleConfiguration *)lifecycleConfiguration {
+ SDLConfiguration *config = [SDLConfiguration configurationWithLifecycle:lifecycleConfiguration lockScreen:[SDLLockScreenConfiguration enabledConfiguration] logging:[self.class sdlex_logConfiguration]];
+ self.sdlManager = [[SDLManager alloc] initWithConfiguration:config delegate:self];
[self startManager];
}
@@ -100,49 +91,46 @@ NS_ASSUME_NONNULL_BEGIN
}
[weakSelf sdlex_updateProxyState:ProxyStateConnected];
-
[weakSelf sdlex_setupPermissionsCallbacks];
-
- if ([weakSelf.sdlManager.hmiLevel isEqualToEnum:SDLHMILevelFull]) {
- [weakSelf sdlex_showInitialData];
- }
}];
}
- (void)reset {
[self sdlex_updateProxyState:ProxyStateStopped];
[self.sdlManager stop];
- // Remove reference
self.sdlManager = nil;
}
-
#pragma mark - Helpers
- (void)sdlex_showInitialData {
- if ((self.initialShowState != SDLHMIInitialShowStateDataAvailable) || ![self.sdlManager.hmiLevel isEqualToEnum:SDLHMILevelFull]) {
- return;
- }
-
- SDLSetDisplayLayout *displayLayout = [[SDLSetDisplayLayout alloc] initWithLayout:SDLPredefinedLayoutNonMedia];
- [self.sdlManager sendRequest:displayLayout];
-
- self.initialShowState = SDLHMIInitialShowStateShown;
- [self sdlex_showWithManager:self.sdlManager];
+ SDLSetDisplayLayout *displayLayout = [[SDLSetDisplayLayout alloc] initWithLayout:SDLPredefinedLayoutMedia];
+ SDLAddCommand *speakNameAddCommand = [self.class sdlex_speakNameCommandWithManager:self.sdlManager];
+ SDLAddCommand *performInteractionChoiceSetAddCommand = [self.class sdlex_interactionSetCommandWithManager:self.sdlManager];
+ SDLAddCommand *getVehicleDataAddCommand = [self.class sdlex_vehicleDataCommandWithManager:self.sdlManager];
+ SDLCreateInteractionChoiceSet *createInteractionChoiceSet = [self.class sdlex_createOnlyChoiceInteractionSet];
+
+ [self.sdlManager sendSequentialRequests:@[displayLayout, speakNameAddCommand, performInteractionChoiceSetAddCommand, getVehicleDataAddCommand, createInteractionChoiceSet] progressHandler:^BOOL(__kindof SDLRPCRequest * _Nonnull request, __kindof SDLRPCResponse * _Nullable response, NSError * _Nullable error, float percentComplete) {
+ SDLLogD(@"Show initial data RPC. Request: %@. Response: %@. Percentage complete: %f, error: %@", request, response, percentComplete, error);
+ return YES;
+ } completionHandler:^(BOOL success) {
+ !success ? SDLLogW(@"Some show initial data RPCs were not sent successfully") : SDLLogD(@"All show initial data RPCs were sent successfully");
+ }];
}
-- (void)sdlex_showWithManager:(SDLManager *)manager {
+- (void)sdlex_updateShowWithManager:(SDLManager *)manager {
NSString *mainField1Text = isTextOn ? @"Smart Device Link" : @"";
NSString *mainField2Text = isTextOn ? @"Example App" : @"";
SDLShow* show = [[SDLShow alloc] initWithMainField1:mainField1Text mainField2:mainField2Text alignment:SDLTextAlignmentCenter];
[self sdlex_prepareSoftButtonsWithImages:self.areImagesVisible completionHandler:^(NSArray<SDLSoftButton *> * _Nonnull softButtons) {
+ if (softButtons.count == 0) { return; }
show.softButtons = softButtons;
[manager sendRequest:show];
}];
[self sdlex_prepareMainGraphicImageWithImages:self.areImagesVisible completionHandler:^(SDLImage * _Nullable sdlImage) {
- if (sdlImage == nil) { return ;}
+ if (sdlImage == nil) { return; }
show.graphic = sdlImage;
[manager sendRequest:show];
}];
@@ -188,6 +176,7 @@ NS_ASSUME_NONNULL_BEGIN
SDLLogFileModule *sdlExampleModule = [SDLLogFileModule moduleWithName:@"SDL Example" files:[NSSet setWithArray:@[@"ProxyManager"]]];
logConfig.modules = [logConfig.modules setByAddingObject:sdlExampleModule];
logConfig.targets = [logConfig.targets setByAddingObject:[SDLLogTargetFile logger]];
+ logConfig.globalLogLevel = SDLLogLevelVerbose;
return logConfig;
}
@@ -293,14 +282,7 @@ NS_ASSUME_NONNULL_BEGIN
}
+ (void)sdlex_sendPerformOnlyChoiceInteractionWithManager:(SDLManager *)manager {
- SDLPerformInteraction *performOnlyChoiceInteraction = [[SDLPerformInteraction alloc] init];
- performOnlyChoiceInteraction.initialText = @"Choose the only one! You have 5 seconds...";
- performOnlyChoiceInteraction.initialPrompt = [SDLTTSChunk textChunksFromString:@"Choose it"];
- performOnlyChoiceInteraction.interactionMode = SDLInteractionModeBoth;
- performOnlyChoiceInteraction.interactionChoiceSetIDList = @[@0];
- performOnlyChoiceInteraction.helpPrompt = [SDLTTSChunk textChunksFromString:@"Do it"];
- performOnlyChoiceInteraction.timeoutPrompt = [SDLTTSChunk textChunksFromString:@"Too late"];
- performOnlyChoiceInteraction.timeout = @5000;
+ SDLPerformInteraction *performOnlyChoiceInteraction = [[SDLPerformInteraction alloc] initWithInitialPrompt:@"Choose an item from the list" initialText:@"Choose the only option! You have 5 seconds..." interactionChoiceSetIDList:@[@0] helpPrompt:@"Select an item from the list" timeoutPrompt:@"The list is closing" interactionMode:SDLInteractionModeBoth timeout:5000 vrHelp:nil];
performOnlyChoiceInteraction.interactionLayout = SDLLayoutModeListOnly;
[manager sendRequest:performOnlyChoiceInteraction withResponseHandler:^(__kindof SDLRPCRequest * _Nullable request, __kindof SDLPerformInteractionResponse * _Nullable response, NSError * _Nullable error) {
@@ -318,9 +300,8 @@ NS_ASSUME_NONNULL_BEGIN
}
- (void)sdlex_prepareSoftButtonsWithImages:(BOOL)areImagesVisible completionHandler:(void (^)(NSArray<SDLSoftButton *> *softButtons))completionHandler {
- // Save each soft button in a specific index position in the array. Once all buttons have been return all the buttons. This is done to prevent flickering that can occur if the UI is updated everytime a button is created.
- SDLSoftButton *button = [[SDLSoftButton alloc] init];
- NSMutableArray<SDLSoftButton *> *softButtons = [[NSMutableArray alloc] initWithObjects:button, button, button, button, nil];
+ // Save each soft button to a specific index position in the array. Once all buttons have been created, return the array of buttons. This is done to prevent flickering that can occur if the UI is updated everytime a button is created.
+ NSMutableArray<SDLSoftButton *> *softButtons = [[NSMutableArray alloc] initWithObjects:[[SDLSoftButton alloc] init], [[SDLSoftButton alloc] init], [[SDLSoftButton alloc] init], [[SDLSoftButton alloc] init], nil];
dispatch_group_t dataDispatchGroup = dispatch_group_create();
dispatch_group_enter(dataDispatchGroup);
@@ -373,13 +354,12 @@ NS_ASSUME_NONNULL_BEGIN
}];
}
-static BOOL isHexagonOn = true;
+static BOOL isHexagonOn = YES;
- (void)sdlex_prepareSoftButton2WithManager:(SDLManager *)manager isImageVisible:(BOOL)imageVisible completionHandler:(void (^)(SDLSoftButton * button))completionHandler {
SDLSoftButton* softButton = [[SDLSoftButton alloc] initWithHandler:^(SDLOnButtonPress * _Nullable buttonPressNotification, SDLOnButtonEvent * _Nullable buttonEventNotification) {
if (buttonPressNotification == nil) { return; }
-
isHexagonOn = !isHexagonOn;
- [self sdlex_showWithManager:manager];
+ [self sdlex_updateShowWithManager:manager];
}];
softButton.softButtonID = @200;
@@ -397,18 +377,15 @@ static BOOL isHexagonOn = true;
}];
}
-static BOOL isTextOn = true;
+static BOOL isTextOn = YES;
- (SDLSoftButton *)sdlex_prepareSoftButton3WithManager:(SDLManager *)manager {
SDLSoftButton* softButton = [[SDLSoftButton alloc] initWithHandler:^(SDLOnButtonPress * _Nullable buttonPressNotification, SDLOnButtonEvent * _Nullable buttonEventNotification) {
- if (buttonPressNotification == nil) {
- return;
- }
+ if (buttonPressNotification == nil) { return; }
isTextOn = !isTextOn;
- [self sdlex_showWithManager:manager];
-
- SDLLogD(@"Text visibility soft button press fired");
+ [self sdlex_updateShowWithManager:manager];
}];
+
softButton.softButtonID = @300;
softButton.text = isTextOn ? @"➖Text" : @"➕Text";
softButton.type = SDLSoftButtonTypeText;
@@ -419,11 +396,8 @@ static BOOL isTextOn = true;
- (SDLSoftButton *)sdlex_prepareSoftButton4WithManager:(SDLManager *)manager areImagesVisible:(BOOL)imageVisible {
SDLSoftButton* softButton = [[SDLSoftButton alloc] initWithHandler:^(SDLOnButtonPress * _Nullable buttonPressNotification, SDLOnButtonEvent * _Nullable buttonEventNotification) {
if (buttonPressNotification == nil) { return; }
-
- self.areImagesVisible = !self.areImagesVisible;
- [self sdlex_showWithManager:manager];
-
- SDLLogD(@"Image visibility soft button press fired %d", isHexagonOn);
+ self.areImagesVisible = !imageVisible;
+ [self sdlex_updateShowWithManager:manager];
}];
softButton.text = imageVisible ? @"➖Icons" : @"➕Icons";
@@ -455,26 +429,26 @@ static BOOL isTextOn = true;
#pragma mark - Files / Artwork
+ (SDLArtwork *)sdlex_softButton1Artwork {
- return [[SDLArtwork alloc] initWithImage:[UIImage imageNamed:@"star_softbutton_icon"] persistent:false asImageFormat:SDLArtworkImageFormatPNG];
+ return [SDLArtwork artworkWithImage:[UIImage imageNamed:@"star_softbutton_icon"] asImageFormat:SDLArtworkImageFormatPNG];
}
+ (SDLArtwork *)sdlex_softButton2OnArtwork {
- return [[SDLArtwork alloc] initWithImage:[UIImage imageNamed:@"hexagon_on_softbutton_icon"] persistent:false asImageFormat:SDLArtworkImageFormatPNG];
+ return [SDLArtwork artworkWithImage:[UIImage imageNamed:@"hexagon_on_softbutton_icon"] asImageFormat:SDLArtworkImageFormatPNG];
}
+ (SDLArtwork *)sdlex_softButton2OffArtwork {
- return [[SDLArtwork alloc] initWithImage:[UIImage imageNamed:@"hexagon_off_softbutton_icon"] persistent:false asImageFormat:SDLArtworkImageFormatPNG];
+ return [SDLArtwork artworkWithImage:[UIImage imageNamed:@"hexagon_off_softbutton_icon"] asImageFormat:SDLArtworkImageFormatPNG];
}
+ (SDLArtwork *)sdlex_mainGraphicArtwork {
- return [[SDLArtwork alloc] initWithImage:[UIImage imageNamed:@"sdl_logo_green"] persistent:false asImageFormat:SDLArtworkImageFormatPNG];
+ return [SDLArtwork artworkWithImage:[UIImage imageNamed:@"sdl_logo_green"] asImageFormat:SDLArtworkImageFormatPNG];
}
+ (SDLArtwork *)sdlex_mainGraphicBlank {
UIGraphicsBeginImageContextWithOptions(CGSizeMake(5, 5), NO, 0.0);
UIImage *blankImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
- return [[SDLArtwork alloc] initWithImage:blankImage persistent:false asImageFormat:SDLArtworkImageFormatPNG];
+ return [SDLArtwork artworkWithImage:blankImage asImageFormat:SDLArtworkImageFormatPNG];
}
#pragma mark - SDLManagerDelegate
@@ -482,7 +456,6 @@ static BOOL isTextOn = true;
- (void)managerDidDisconnect {
// Reset our state
self.firstTimeState = SDLHMIFirstStateNone;
- self.initialShowState = SDLHMIInitialShowStateNone;
[self sdlex_updateProxyState:ProxyStateStopped];
if (ShouldRestartOnDisconnect) {
[self startManager];
@@ -493,28 +466,14 @@ static BOOL isTextOn = true;
if (![newLevel isEqualToEnum:SDLHMILevelNone] && (self.firstTimeState == SDLHMIFirstStateNone)) {
// This is our first time in a non-NONE state
self.firstTimeState = SDLHMIFirstStateNonNone;
- [self sdlex_prepareRemoteSystem];
+ [self sdlex_showInitialData];
}
if ([newLevel isEqualToEnum:SDLHMILevelFull] && (self.firstTimeState != SDLHMIFirstStateFull)) {
// This is our first time in a FULL state
self.firstTimeState = SDLHMIFirstStateFull;
+ [self sdlex_updateShowWithManager:self.sdlManager];
}
-
- if ([newLevel isEqualToEnum:SDLHMILevelFull]) {
- // We're always going to try to show the initial state, because if we've already shown it, it won't be shown, and we need to guard against some possible weird states
- [self sdlex_showInitialData];
- }
-}
-
-- (void)sdlex_prepareRemoteSystem {
- [self.sdlManager sendRequest:[self.class sdlex_speakNameCommandWithManager:self.sdlManager]];
- [self.sdlManager sendRequest:[self.class sdlex_interactionSetCommandWithManager:self.sdlManager]];
- [self.sdlManager sendRequest:[self.class sdlex_vehicleDataCommandWithManager:self.sdlManager]];
-
- [self.sdlManager sendRequest:[self.class sdlex_createOnlyChoiceInteractionSet] withResponseHandler:nil];
- self.initialShowState = SDLHMIInitialShowStateDataAvailable;
- [self sdlex_showInitialData];
}
@end