diff --git a/AppSyncRealTimeClient.podspec b/AppSyncRealTimeClient.podspec index f61acdee..fab850dd 100644 --- a/AppSyncRealTimeClient.podspec +++ b/AppSyncRealTimeClient.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = 'AppSyncRealTimeClient' - s.version = '1.0.1' + s.version = '1.0.2' s.summary = 'Amazon Web Services AppSync RealTime Client for iOS.' s.description = 'AppSync RealTime Client provides subscription connections to AppSync websocket endpoints' @@ -17,5 +17,4 @@ Pod::Spec.new do |s| s.source_files = 'AppSyncRealTimeClient/**/*.swift' s.dependency 'Starscream', '~> 3.0.2' - - end + end \ No newline at end of file diff --git a/AppSyncRealTimeClient.xcodeproj/project.pbxproj b/AppSyncRealTimeClient.xcodeproj/project.pbxproj index 22d0bbd7..e51809cb 100644 --- a/AppSyncRealTimeClient.xcodeproj/project.pbxproj +++ b/AppSyncRealTimeClient.xcodeproj/project.pbxproj @@ -40,7 +40,31 @@ 217F39F12406EA4000F1A0B3 /* MockConnectionProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 217F39EB2406EA3F00F1A0B3 /* MockConnectionProvider.swift */; }; 217F39F22406EA4000F1A0B3 /* RealtimeConnectionProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 217F39ED2406EA4000F1A0B3 /* RealtimeConnectionProviderTests.swift */; }; 217F39F32406EA4000F1A0B3 /* RealtimeGatewayURLInterceptorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 217F39EF2406EA4000F1A0B3 /* RealtimeGatewayURLInterceptorTests.swift */; }; + 21D38B412409AFBD00EC2A8D /* AppSyncRealTimeClientIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21D38B402409AFBD00EC2A8D /* AppSyncRealTimeClientIntegrationTests.swift */; }; + 21D38B432409AFBD00EC2A8D /* AppSyncRealTimeClient.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 217F398F2405D9D500F1A0B3 /* AppSyncRealTimeClient.framework */; }; + 21D38B4C2409B6C000EC2A8D /* amplifyconfiguration.json in Resources */ = {isa = PBXBuildFile; fileRef = 21D38B4B2409B6C000EC2A8D /* amplifyconfiguration.json */; }; + 21D38B4E2409B8B200EC2A8D /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = 21D38B4D2409B8B200EC2A8D /* README.md */; }; + 21D38B562409B93F00EC2A8D /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21D38B552409B93F00EC2A8D /* AppDelegate.swift */; }; + 21D38B582409B93F00EC2A8D /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21D38B572409B93F00EC2A8D /* SceneDelegate.swift */; }; + 21D38B5A2409B93F00EC2A8D /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21D38B592409B93F00EC2A8D /* ContentView.swift */; }; + 21D38B5C2409B94100EC2A8D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 21D38B5B2409B94100EC2A8D /* Assets.xcassets */; }; + 21D38B5F2409B94100EC2A8D /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 21D38B5E2409B94100EC2A8D /* Preview Assets.xcassets */; }; + 21D38B622409B94100EC2A8D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 21D38B602409B94100EC2A8D /* LaunchScreen.storyboard */; }; + 21D38B692409B95B00EC2A8D /* amplifyconfiguration.json in Resources */ = {isa = PBXBuildFile; fileRef = 21D38B4B2409B6C000EC2A8D /* amplifyconfiguration.json */; }; + 21D38B6D240A262800EC2A8D /* AppSyncJSONHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21D38B6C240A262800EC2A8D /* AppSyncJSONHelper.swift */; }; + 21D38B7C240A2A1300EC2A8D /* OIDCAuthInterceptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21D38B78240A2A1300EC2A8D /* OIDCAuthInterceptor.swift */; }; + 21D38B83240A392B00EC2A8D /* APIKeyAuthInterceptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21D38B82240A392B00EC2A8D /* APIKeyAuthInterceptor.swift */; }; + 21D38B89240A39E400EC2A8D /* OIDCAuthInterceptorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21D38B85240A39E400EC2A8D /* OIDCAuthInterceptorTests.swift */; }; + 21D38B8B240A39E400EC2A8D /* AppSyncJSONHelperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21D38B87240A39E400EC2A8D /* AppSyncJSONHelperTests.swift */; }; + 21D38B8C240A39E400EC2A8D /* APIKeyAuthInterceptorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21D38B88240A39E400EC2A8D /* APIKeyAuthInterceptorTests.swift */; }; + 21D38B8E240A3C2300EC2A8D /* ConnectionProviderFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21D38B8D240A3C2300EC2A8D /* ConnectionProviderFactory.swift */; }; + 21D38B94240C4A2A00EC2A8D /* OIDCAuthProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21D38B93240C4A2A00EC2A8D /* OIDCAuthProvider.swift */; }; + 21D38B97240C4DCF00EC2A8D /* Error+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21D38B96240C4DCF00EC2A8D /* Error+Extension.swift */; }; + 21D38B99240C4E1C00EC2A8D /* ConfigurationHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21D38B98240C4E1C00EC2A8D /* ConfigurationHelper.swift */; }; + 21D38B9D240C540D00EC2A8D /* TestCommonConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21D38B9C240C540D00EC2A8D /* TestCommonConstants.swift */; }; 259F9EB83B9F67D0413A6D4C /* Pods_AppSyncRealTimeClient.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 81AFCBED5A97E398A4BD9D06 /* Pods_AppSyncRealTimeClient.framework */; }; + 450019BB151D701382536BD0 /* Pods_HostApp.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 29CDD85F44666C7241232D29 /* Pods_HostApp.framework */; }; + 4A3EAC9B20D96D81CC3A7EF4 /* Pods_HostApp_AppSyncRealTimeClientIntegrationTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43D112B03D6D38F84995D6FA /* Pods_HostApp_AppSyncRealTimeClientIntegrationTests.framework */; }; DCFE701B5D1380E566694A48 /* Pods_AppSyncRealTimeClient_AppSyncRealTimeClientTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3E662A46AB2C93EE316F784C /* Pods_AppSyncRealTimeClient_AppSyncRealTimeClientTests.framework */; }; /* End PBXBuildFile section */ @@ -52,9 +76,24 @@ remoteGlobalIDString = 217F398E2405D9D500F1A0B3; remoteInfo = AppSyncRealTimeClient; }; + 21D38B442409AFBD00EC2A8D /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 217F39862405D9D500F1A0B3 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 217F398E2405D9D500F1A0B3; + remoteInfo = AppSyncRealTimeClient; + }; + 21D38B672409B95000EC2A8D /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 217F39862405D9D500F1A0B3 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 21D38B522409B93F00EC2A8D; + remoteInfo = HostApp; + }; /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + 18D6E56CE03BAC33493CC19B /* Pods-HostApp.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HostApp.debug.xcconfig"; path = "Target Support Files/Pods-HostApp/Pods-HostApp.debug.xcconfig"; sourceTree = ""; }; 217F398F2405D9D500F1A0B3 /* AppSyncRealTimeClient.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AppSyncRealTimeClient.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 217F39932405D9D500F1A0B3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 217F39982405D9D500F1A0B3 /* AppSyncRealTimeClientTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AppSyncRealTimeClientTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -91,12 +130,41 @@ 217F39EB2406EA3F00F1A0B3 /* MockConnectionProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockConnectionProvider.swift; sourceTree = ""; }; 217F39ED2406EA4000F1A0B3 /* RealtimeConnectionProviderTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealtimeConnectionProviderTests.swift; sourceTree = ""; }; 217F39EF2406EA4000F1A0B3 /* RealtimeGatewayURLInterceptorTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealtimeGatewayURLInterceptorTests.swift; sourceTree = ""; }; + 21D38B3E2409AFBD00EC2A8D /* AppSyncRealTimeClientIntegrationTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AppSyncRealTimeClientIntegrationTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 21D38B402409AFBD00EC2A8D /* AppSyncRealTimeClientIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSyncRealTimeClientIntegrationTests.swift; sourceTree = ""; }; + 21D38B422409AFBD00EC2A8D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 21D38B4B2409B6C000EC2A8D /* amplifyconfiguration.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = amplifyconfiguration.json; sourceTree = ""; }; + 21D38B4D2409B8B200EC2A8D /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; + 21D38B532409B93F00EC2A8D /* HostApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = HostApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 21D38B552409B93F00EC2A8D /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 21D38B572409B93F00EC2A8D /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; + 21D38B592409B93F00EC2A8D /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + 21D38B5B2409B94100EC2A8D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 21D38B5E2409B94100EC2A8D /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 21D38B612409B94100EC2A8D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 21D38B632409B94100EC2A8D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 21D38B6C240A262800EC2A8D /* AppSyncJSONHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppSyncJSONHelper.swift; sourceTree = ""; }; + 21D38B78240A2A1300EC2A8D /* OIDCAuthInterceptor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OIDCAuthInterceptor.swift; sourceTree = ""; }; + 21D38B82240A392B00EC2A8D /* APIKeyAuthInterceptor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = APIKeyAuthInterceptor.swift; sourceTree = ""; }; + 21D38B85240A39E400EC2A8D /* OIDCAuthInterceptorTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OIDCAuthInterceptorTests.swift; sourceTree = ""; }; + 21D38B87240A39E400EC2A8D /* AppSyncJSONHelperTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppSyncJSONHelperTests.swift; sourceTree = ""; }; + 21D38B88240A39E400EC2A8D /* APIKeyAuthInterceptorTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = APIKeyAuthInterceptorTests.swift; sourceTree = ""; }; + 21D38B8D240A3C2300EC2A8D /* ConnectionProviderFactory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConnectionProviderFactory.swift; sourceTree = ""; }; + 21D38B93240C4A2A00EC2A8D /* OIDCAuthProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OIDCAuthProvider.swift; sourceTree = ""; }; + 21D38B96240C4DCF00EC2A8D /* Error+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Error+Extension.swift"; sourceTree = ""; }; + 21D38B98240C4E1C00EC2A8D /* ConfigurationHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationHelper.swift; sourceTree = ""; }; + 21D38B9C240C540D00EC2A8D /* TestCommonConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestCommonConstants.swift; sourceTree = ""; }; + 29CDD85F44666C7241232D29 /* Pods_HostApp.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_HostApp.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 356411189EAD2C776D250FB7 /* Pods-HostApp.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HostApp.release.xcconfig"; path = "Target Support Files/Pods-HostApp/Pods-HostApp.release.xcconfig"; sourceTree = ""; }; 3E662A46AB2C93EE316F784C /* Pods_AppSyncRealTimeClient_AppSyncRealTimeClientTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AppSyncRealTimeClient_AppSyncRealTimeClientTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 43D112B03D6D38F84995D6FA /* Pods_HostApp_AppSyncRealTimeClientIntegrationTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_HostApp_AppSyncRealTimeClientIntegrationTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 7D4F451B830A5837CE5274A3 /* Pods-AppSyncRealTimeClient-AppSyncRealTimeClientTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AppSyncRealTimeClient-AppSyncRealTimeClientTests.release.xcconfig"; path = "Target Support Files/Pods-AppSyncRealTimeClient-AppSyncRealTimeClientTests/Pods-AppSyncRealTimeClient-AppSyncRealTimeClientTests.release.xcconfig"; sourceTree = ""; }; 81AFCBED5A97E398A4BD9D06 /* Pods_AppSyncRealTimeClient.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AppSyncRealTimeClient.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 93AA1C96F1B8D428CCC24CF0 /* Pods-AppSyncRealTimeClient-AppSyncRealTimeClientTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AppSyncRealTimeClient-AppSyncRealTimeClientTests.debug.xcconfig"; path = "Target Support Files/Pods-AppSyncRealTimeClient-AppSyncRealTimeClientTests/Pods-AppSyncRealTimeClient-AppSyncRealTimeClientTests.debug.xcconfig"; sourceTree = ""; }; + A843B4589131E0D2DB13ADC7 /* Pods-HostApp-AppSyncRealTimeClientIntegrationTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HostApp-AppSyncRealTimeClientIntegrationTests.debug.xcconfig"; path = "Target Support Files/Pods-HostApp-AppSyncRealTimeClientIntegrationTests/Pods-HostApp-AppSyncRealTimeClientIntegrationTests.debug.xcconfig"; sourceTree = ""; }; B07A87133C24AA78AF59093C /* Pods-AppSyncRealTimeClient.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AppSyncRealTimeClient.debug.xcconfig"; path = "Target Support Files/Pods-AppSyncRealTimeClient/Pods-AppSyncRealTimeClient.debug.xcconfig"; sourceTree = ""; }; C73EA796F50FB0196FDF4865 /* Pods-AppSyncRealTimeClient.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AppSyncRealTimeClient.release.xcconfig"; path = "Target Support Files/Pods-AppSyncRealTimeClient/Pods-AppSyncRealTimeClient.release.xcconfig"; sourceTree = ""; }; + E65A19C826699DA299A49284 /* Pods-HostApp-AppSyncRealTimeClientIntegrationTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-HostApp-AppSyncRealTimeClientIntegrationTests.release.xcconfig"; path = "Target Support Files/Pods-HostApp-AppSyncRealTimeClientIntegrationTests/Pods-HostApp-AppSyncRealTimeClientIntegrationTests.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -117,6 +185,23 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 21D38B3B2409AFBD00EC2A8D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 21D38B432409AFBD00EC2A8D /* AppSyncRealTimeClient.framework in Frameworks */, + 4A3EAC9B20D96D81CC3A7EF4 /* Pods_HostApp_AppSyncRealTimeClientIntegrationTests.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 21D38B502409B93F00EC2A8D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 450019BB151D701382536BD0 /* Pods_HostApp.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -125,6 +210,8 @@ children = ( 217F39912405D9D500F1A0B3 /* AppSyncRealTimeClient */, 217F399C2405D9D500F1A0B3 /* AppSyncRealTimeClientTests */, + 21D38B3F2409AFBD00EC2A8D /* AppSyncRealTimeClientIntegrationTests */, + 21D38B542409B93F00EC2A8D /* HostApp */, 217F39902405D9D500F1A0B3 /* Products */, D146D6DDE2251CCA7A4ECD7F /* Pods */, CC47BA7DC6033B1626BAD959 /* Frameworks */, @@ -136,6 +223,8 @@ children = ( 217F398F2405D9D500F1A0B3 /* AppSyncRealTimeClient.framework */, 217F39982405D9D500F1A0B3 /* AppSyncRealTimeClientTests.xctest */, + 21D38B3E2409AFBD00EC2A8D /* AppSyncRealTimeClientIntegrationTests.xctest */, + 21D38B532409B93F00EC2A8D /* HostApp.app */, ); name = Products; sourceTree = ""; @@ -146,6 +235,7 @@ 217F39B82406E98300F1A0B3 /* Connection */, 217F39A92406E98300F1A0B3 /* ConnectionProvider */, 217F39932405D9D500F1A0B3 /* Info.plist */, + 21D38B6E240A272F00EC2A8D /* Interceptor */, 217F39C62406E98400F1A0B3 /* Support */, 217F39C12406E98400F1A0B3 /* Websocket */, ); @@ -158,6 +248,7 @@ 217F39E82406EA3F00F1A0B3 /* Connection */, 217F39EC2406EA4000F1A0B3 /* ConnectionProvider */, 217F399F2405D9D500F1A0B3 /* Info.plist */, + 21D38B84240A39E400EC2A8D /* Interceptor */, 217F39EA2406EA3F00F1A0B3 /* Mocks */, 217F39EE2406EA4000F1A0B3 /* Support */, ); @@ -172,6 +263,7 @@ 217F39AD2406E98300F1A0B3 /* AppSyncMessage+Encodable.swift */, 217F39B02406E98300F1A0B3 /* AppsyncRealtimeConnection */, 217F39AA2406E98300F1A0B3 /* AppSyncResponse.swift */, + 21D38B8D240A3C2300EC2A8D /* ConnectionProviderFactory.swift */, 217F39B72406E98300F1A0B3 /* ConnectionProvider.swift */, 217F39AC2406E98300F1A0B3 /* ConnectionProviderError.swift */, 217F39AB2406E98300F1A0B3 /* InterceptableConnection.swift */, @@ -235,9 +327,10 @@ 217F39C62406E98400F1A0B3 /* Support */ = { isa = PBXGroup; children = ( + 21D38B6C240A262800EC2A8D /* AppSyncJSONHelper.swift */, 217F39C92406E98400F1A0B3 /* AppSyncJSONValue.swift */, 217F39C72406E98400F1A0B3 /* AppSyncLogger.swift */, - 217F39C82406E98400F1A0B3 /* RealtimeGatewayURLInterceptor.swift */, + 21D38B93240C4A2A00EC2A8D /* OIDCAuthProvider.swift */, 217F39CB2406E98400F1A0B3 /* SubscriptionConnectionType.swift */, 217F39CA2406E98400F1A0B3 /* SubscriptionConstants.swift */, ); @@ -276,11 +369,77 @@ path = Support; sourceTree = ""; }; + 21D38B3F2409AFBD00EC2A8D /* AppSyncRealTimeClientIntegrationTests */ = { + isa = PBXGroup; + children = ( + 21D38B4B2409B6C000EC2A8D /* amplifyconfiguration.json */, + 21D38B402409AFBD00EC2A8D /* AppSyncRealTimeClientIntegrationTests.swift */, + 21D38B422409AFBD00EC2A8D /* Info.plist */, + 21D38B4D2409B8B200EC2A8D /* README.md */, + 21D38B95240C4DC200EC2A8D /* Support */, + ); + path = AppSyncRealTimeClientIntegrationTests; + sourceTree = ""; + }; + 21D38B542409B93F00EC2A8D /* HostApp */ = { + isa = PBXGroup; + children = ( + 21D38B552409B93F00EC2A8D /* AppDelegate.swift */, + 21D38B572409B93F00EC2A8D /* SceneDelegate.swift */, + 21D38B592409B93F00EC2A8D /* ContentView.swift */, + 21D38B5B2409B94100EC2A8D /* Assets.xcassets */, + 21D38B602409B94100EC2A8D /* LaunchScreen.storyboard */, + 21D38B632409B94100EC2A8D /* Info.plist */, + 21D38B5D2409B94100EC2A8D /* Preview Content */, + ); + path = HostApp; + sourceTree = ""; + }; + 21D38B5D2409B94100EC2A8D /* Preview Content */ = { + isa = PBXGroup; + children = ( + 21D38B5E2409B94100EC2A8D /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + 21D38B6E240A272F00EC2A8D /* Interceptor */ = { + isa = PBXGroup; + children = ( + 21D38B82240A392B00EC2A8D /* APIKeyAuthInterceptor.swift */, + 21D38B78240A2A1300EC2A8D /* OIDCAuthInterceptor.swift */, + 217F39C82406E98400F1A0B3 /* RealtimeGatewayURLInterceptor.swift */, + ); + path = Interceptor; + sourceTree = ""; + }; + 21D38B84240A39E400EC2A8D /* Interceptor */ = { + isa = PBXGroup; + children = ( + 21D38B85240A39E400EC2A8D /* OIDCAuthInterceptorTests.swift */, + 21D38B87240A39E400EC2A8D /* AppSyncJSONHelperTests.swift */, + 21D38B88240A39E400EC2A8D /* APIKeyAuthInterceptorTests.swift */, + ); + path = Interceptor; + sourceTree = ""; + }; + 21D38B95240C4DC200EC2A8D /* Support */ = { + isa = PBXGroup; + children = ( + 21D38B96240C4DCF00EC2A8D /* Error+Extension.swift */, + 21D38B98240C4E1C00EC2A8D /* ConfigurationHelper.swift */, + 21D38B9C240C540D00EC2A8D /* TestCommonConstants.swift */, + ); + path = Support; + sourceTree = ""; + }; CC47BA7DC6033B1626BAD959 /* Frameworks */ = { isa = PBXGroup; children = ( 81AFCBED5A97E398A4BD9D06 /* Pods_AppSyncRealTimeClient.framework */, 3E662A46AB2C93EE316F784C /* Pods_AppSyncRealTimeClient_AppSyncRealTimeClientTests.framework */, + 29CDD85F44666C7241232D29 /* Pods_HostApp.framework */, + 43D112B03D6D38F84995D6FA /* Pods_HostApp_AppSyncRealTimeClientIntegrationTests.framework */, ); name = Frameworks; sourceTree = ""; @@ -292,6 +451,10 @@ C73EA796F50FB0196FDF4865 /* Pods-AppSyncRealTimeClient.release.xcconfig */, 93AA1C96F1B8D428CCC24CF0 /* Pods-AppSyncRealTimeClient-AppSyncRealTimeClientTests.debug.xcconfig */, 7D4F451B830A5837CE5274A3 /* Pods-AppSyncRealTimeClient-AppSyncRealTimeClientTests.release.xcconfig */, + 18D6E56CE03BAC33493CC19B /* Pods-HostApp.debug.xcconfig */, + 356411189EAD2C776D250FB7 /* Pods-HostApp.release.xcconfig */, + A843B4589131E0D2DB13ADC7 /* Pods-HostApp-AppSyncRealTimeClientIntegrationTests.debug.xcconfig */, + E65A19C826699DA299A49284 /* Pods-HostApp-AppSyncRealTimeClientIntegrationTests.release.xcconfig */, ); path = Pods; sourceTree = ""; @@ -348,6 +511,45 @@ productReference = 217F39982405D9D500F1A0B3 /* AppSyncRealTimeClientTests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; + 21D38B3D2409AFBD00EC2A8D /* AppSyncRealTimeClientIntegrationTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 21D38B482409AFBD00EC2A8D /* Build configuration list for PBXNativeTarget "AppSyncRealTimeClientIntegrationTests" */; + buildPhases = ( + 6996A5923AF9E7D35CA85369 /* [CP] Check Pods Manifest.lock */, + 21D38B3A2409AFBD00EC2A8D /* Sources */, + 21D38B3B2409AFBD00EC2A8D /* Frameworks */, + 21D38B3C2409AFBD00EC2A8D /* Resources */, + D3A1E54210CC83949895F543 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 21D38B452409AFBD00EC2A8D /* PBXTargetDependency */, + 21D38B682409B95000EC2A8D /* PBXTargetDependency */, + ); + name = AppSyncRealTimeClientIntegrationTests; + productName = AppSyncRealTimeClientIntegrationTests; + productReference = 21D38B3E2409AFBD00EC2A8D /* AppSyncRealTimeClientIntegrationTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 21D38B522409B93F00EC2A8D /* HostApp */ = { + isa = PBXNativeTarget; + buildConfigurationList = 21D38B642409B94100EC2A8D /* Build configuration list for PBXNativeTarget "HostApp" */; + buildPhases = ( + B4AC537B815B7AF42413C854 /* [CP] Check Pods Manifest.lock */, + 21D38B4F2409B93F00EC2A8D /* Sources */, + 21D38B502409B93F00EC2A8D /* Frameworks */, + 21D38B512409B93F00EC2A8D /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = HostApp; + productName = HostApp; + productReference = 21D38B532409B93F00EC2A8D /* HostApp.app */; + productType = "com.apple.product-type.application"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -364,6 +566,13 @@ 217F39972405D9D500F1A0B3 = { CreatedOnToolsVersion = 11.3.1; }; + 21D38B3D2409AFBD00EC2A8D = { + CreatedOnToolsVersion = 11.3.1; + TestTargetID = 21D38B522409B93F00EC2A8D; + }; + 21D38B522409B93F00EC2A8D = { + CreatedOnToolsVersion = 11.3.1; + }; }; }; buildConfigurationList = 217F39892405D9D500F1A0B3 /* Build configuration list for PBXProject "AppSyncRealTimeClient" */; @@ -381,6 +590,8 @@ targets = ( 217F398E2405D9D500F1A0B3 /* AppSyncRealTimeClient */, 217F39972405D9D500F1A0B3 /* AppSyncRealTimeClientTests */, + 21D38B3D2409AFBD00EC2A8D /* AppSyncRealTimeClientIntegrationTests */, + 21D38B522409B93F00EC2A8D /* HostApp */, ); }; /* End PBXProject section */ @@ -400,6 +611,26 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 21D38B3C2409AFBD00EC2A8D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 21D38B4E2409B8B200EC2A8D /* README.md in Resources */, + 21D38B4C2409B6C000EC2A8D /* amplifyconfiguration.json in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 21D38B512409B93F00EC2A8D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 21D38B692409B95B00EC2A8D /* amplifyconfiguration.json in Resources */, + 21D38B622409B94100EC2A8D /* LaunchScreen.storyboard in Resources */, + 21D38B5F2409B94100EC2A8D /* Preview Assets.xcassets in Resources */, + 21D38B5C2409B94100EC2A8D /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ @@ -425,6 +656,28 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; + 6996A5923AF9E7D35CA85369 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-HostApp-AppSyncRealTimeClientIntegrationTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; 9CB3A44F02BD117B9687BF03 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -442,6 +695,45 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-AppSyncRealTimeClient-AppSyncRealTimeClientTests/Pods-AppSyncRealTimeClient-AppSyncRealTimeClientTests-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; + B4AC537B815B7AF42413C854 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-HostApp-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + D3A1E54210CC83949895F543 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-HostApp-AppSyncRealTimeClientIntegrationTests/Pods-HostApp-AppSyncRealTimeClientIntegrationTests-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-HostApp-AppSyncRealTimeClientIntegrationTests/Pods-HostApp-AppSyncRealTimeClientIntegrationTests-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-HostApp-AppSyncRealTimeClientIntegrationTests/Pods-HostApp-AppSyncRealTimeClientIntegrationTests-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; F78B4AC87B22D691D620684A /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -471,10 +763,13 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 21D38B7C240A2A1300EC2A8D /* OIDCAuthInterceptor.swift in Sources */, 217F39DB2406E98400F1A0B3 /* AppSyncSubscriptionConnection+DataHandler.swift in Sources */, + 21D38B94240C4A2A00EC2A8D /* OIDCAuthProvider.swift in Sources */, 217F39DF2406E98400F1A0B3 /* SubscriptionItem.swift in Sources */, 217F39CF2406E98400F1A0B3 /* AppSyncMessage+Encodable.swift in Sources */, 217F39CD2406E98400F1A0B3 /* InterceptableConnection.swift in Sources */, + 21D38B8E240A3C2300EC2A8D /* ConnectionProviderFactory.swift in Sources */, 217F39E02406E98400F1A0B3 /* AppSyncWebsocketProvider.swift in Sources */, 217F39D32406E98400F1A0B3 /* RealtimeConnectionProvider.swift in Sources */, 217F39D12406E98400F1A0B3 /* AppSyncConnectionRequest.swift in Sources */, @@ -488,12 +783,14 @@ 217F39D02406E98400F1A0B3 /* AppSyncMessage.swift in Sources */, 217F39E32406E98400F1A0B3 /* AppSyncLogger.swift in Sources */, 217F39D82406E98400F1A0B3 /* ConnectionProvider.swift in Sources */, + 21D38B83240A392B00EC2A8D /* APIKeyAuthInterceptor.swift in Sources */, 217F39D92406E98400F1A0B3 /* SubscriptionConnection.swift in Sources */, 217F39DA2406E98400F1A0B3 /* RetryableConnection.swift in Sources */, 217F39CC2406E98400F1A0B3 /* AppSyncResponse.swift in Sources */, 217F39E72406E98400F1A0B3 /* SubscriptionConnectionType.swift in Sources */, 217F39D42406E98400F1A0B3 /* RealtimeConnectionProvider+Websocket.swift in Sources */, 217F39DC2406E98400F1A0B3 /* AppSyncSubscriptionConnection.swift in Sources */, + 21D38B6D240A262800EC2A8D /* AppSyncJSONHelper.swift in Sources */, 217F39CE2406E98400F1A0B3 /* ConnectionProviderError.swift in Sources */, 217F39E12406E98400F1A0B3 /* StarscreamAdapter.swift in Sources */, 217F39D72406E98400F1A0B3 /* RealtimeConnectionProvider+ConnectionInterceptable.swift in Sources */, @@ -506,13 +803,37 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 21D38B89240A39E400EC2A8D /* OIDCAuthInterceptorTests.swift in Sources */, 217F39F22406EA4000F1A0B3 /* RealtimeConnectionProviderTests.swift in Sources */, 217F39F02406EA4000F1A0B3 /* AppSyncSubscriptionConnectionTests.swift in Sources */, + 21D38B8C240A39E400EC2A8D /* APIKeyAuthInterceptorTests.swift in Sources */, + 21D38B8B240A39E400EC2A8D /* AppSyncJSONHelperTests.swift in Sources */, 217F39F32406EA4000F1A0B3 /* RealtimeGatewayURLInterceptorTests.swift in Sources */, 217F39F12406EA4000F1A0B3 /* MockConnectionProvider.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; + 21D38B3A2409AFBD00EC2A8D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 21D38B99240C4E1C00EC2A8D /* ConfigurationHelper.swift in Sources */, + 21D38B97240C4DCF00EC2A8D /* Error+Extension.swift in Sources */, + 21D38B9D240C540D00EC2A8D /* TestCommonConstants.swift in Sources */, + 21D38B412409AFBD00EC2A8D /* AppSyncRealTimeClientIntegrationTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 21D38B4F2409B93F00EC2A8D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 21D38B562409B93F00EC2A8D /* AppDelegate.swift in Sources */, + 21D38B582409B93F00EC2A8D /* SceneDelegate.swift in Sources */, + 21D38B5A2409B93F00EC2A8D /* ContentView.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ @@ -521,8 +842,29 @@ target = 217F398E2405D9D500F1A0B3 /* AppSyncRealTimeClient */; targetProxy = 217F399A2405D9D500F1A0B3 /* PBXContainerItemProxy */; }; + 21D38B452409AFBD00EC2A8D /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 217F398E2405D9D500F1A0B3 /* AppSyncRealTimeClient */; + targetProxy = 21D38B442409AFBD00EC2A8D /* PBXContainerItemProxy */; + }; + 21D38B682409B95000EC2A8D /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 21D38B522409B93F00EC2A8D /* HostApp */; + targetProxy = 21D38B672409B95000EC2A8D /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ +/* Begin PBXVariantGroup section */ + 21D38B602409B94100EC2A8D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 21D38B612409B94100EC2A8D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + /* Begin XCBuildConfiguration section */ 217F39A12405D9D500F1A0B3 /* Debug */ = { isa = XCBuildConfiguration; @@ -575,7 +917,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.2; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -632,7 +974,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.2; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; @@ -736,6 +1078,92 @@ }; name = Release; }; + 21D38B462409AFBD00EC2A8D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = A843B4589131E0D2DB13ADC7 /* Pods-HostApp-AppSyncRealTimeClientIntegrationTests.debug.xcconfig */; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = W3DRXD72QU; + INFOPLIST_FILE = AppSyncRealTimeClientIntegrationTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 13.2; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.amazonaws.AppSyncRealTimeClientIntegrationTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/HostApp.app/HostApp"; + }; + name = Debug; + }; + 21D38B472409AFBD00EC2A8D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = E65A19C826699DA299A49284 /* Pods-HostApp-AppSyncRealTimeClientIntegrationTests.release.xcconfig */; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = W3DRXD72QU; + INFOPLIST_FILE = AppSyncRealTimeClientIntegrationTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 13.2; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.amazonaws.AppSyncRealTimeClientIntegrationTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/HostApp.app/HostApp"; + }; + name = Release; + }; + 21D38B652409B94100EC2A8D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 18D6E56CE03BAC33493CC19B /* Pods-HostApp.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "\"HostApp/Preview Content\""; + DEVELOPMENT_TEAM = W3DRXD72QU; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = HostApp/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 13.2; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.amazonaws.HostApp; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 21D38B662409B94100EC2A8D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 356411189EAD2C776D250FB7 /* Pods-HostApp.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "\"HostApp/Preview Content\""; + DEVELOPMENT_TEAM = W3DRXD72QU; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = HostApp/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 13.2; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.amazonaws.HostApp; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -766,6 +1194,24 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 21D38B482409AFBD00EC2A8D /* Build configuration list for PBXNativeTarget "AppSyncRealTimeClientIntegrationTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 21D38B462409AFBD00EC2A8D /* Debug */, + 21D38B472409AFBD00EC2A8D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 21D38B642409B94100EC2A8D /* Build configuration list for PBXNativeTarget "HostApp" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 21D38B652409B94100EC2A8D /* Debug */, + 21D38B662409B94100EC2A8D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ }; rootObject = 217F39862405D9D500F1A0B3 /* Project object */; diff --git a/AppSyncRealTimeClient.xcodeproj/xcshareddata/xcschemes/AppSyncRealTimeClientIntegrationTests.xcscheme b/AppSyncRealTimeClient.xcodeproj/xcshareddata/xcschemes/AppSyncRealTimeClientIntegrationTests.xcscheme new file mode 100644 index 00000000..e7239c88 --- /dev/null +++ b/AppSyncRealTimeClient.xcodeproj/xcshareddata/xcschemes/AppSyncRealTimeClientIntegrationTests.xcscheme @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/AppSyncRealTimeClient.xcodeproj/xcshareddata/xcschemes/HostApp.xcscheme b/AppSyncRealTimeClient.xcodeproj/xcshareddata/xcschemes/HostApp.xcscheme new file mode 100644 index 00000000..b19980d8 --- /dev/null +++ b/AppSyncRealTimeClient.xcodeproj/xcshareddata/xcschemes/HostApp.xcscheme @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/AppSyncRealTimeClient/ConnectionProvider/ConnectionProviderFactory.swift b/AppSyncRealTimeClient/ConnectionProvider/ConnectionProviderFactory.swift new file mode 100644 index 00000000..14934cfb --- /dev/null +++ b/AppSyncRealTimeClient/ConnectionProvider/ConnectionProviderFactory.swift @@ -0,0 +1,37 @@ +// +// Copyright 2018-2020 Amazon.com, +// Inc. or its affiliates. All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import Foundation + +/// Create connection providers to connect to the websocket endpoint of the AppSync endpoint. +public struct ConnectionProviderFactory { + + public static func createConnectionProvider(for url: URL, + authInterceptor: AuthInterceptor, + connectionType: SubscriptionConnectionType) -> ConnectionProvider { + let provider = ConnectionProviderFactory.createConnectionProvider(for: url, connectionType: connectionType) + + if let messageInterceptable = provider as? MessageInterceptable { + messageInterceptable.addInterceptor(authInterceptor) + } + if let connectionInterceptable = provider as? ConnectionInterceptable { + connectionInterceptable.addInterceptor(RealtimeGatewayURLInterceptor()) + connectionInterceptable.addInterceptor(authInterceptor) + } + + return provider + } + + static func createConnectionProvider(for url: URL, connectionType: SubscriptionConnectionType) -> ConnectionProvider { + switch connectionType { + case .appSyncRealtime: + let websocketProvider = StarscreamAdapter() + let connectionProvider = RealtimeConnectionProvider(for: url, websocket: websocketProvider) + return connectionProvider + } + } +} diff --git a/AppSyncRealTimeClient/Interceptor/APIKeyAuthInterceptor.swift b/AppSyncRealTimeClient/Interceptor/APIKeyAuthInterceptor.swift new file mode 100644 index 00000000..8e6de564 --- /dev/null +++ b/AppSyncRealTimeClient/Interceptor/APIKeyAuthInterceptor.swift @@ -0,0 +1,99 @@ +// +// Copyright 2018-2020 Amazon.com, +// Inc. or its affiliates. All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import Foundation + +/// Auth interceptor for API Key based authentication +public class APIKeyAuthInterceptor: AuthInterceptor { + + let apiKey: String + + public init(_ apiKey: String) { + self.apiKey = apiKey + } + + /// Intercept the connection and adds header, payload query to the request url. + /// + /// The value of header should be the base64 string of the following: + /// * "host": : this is the host for the AppSync endpoint + /// * "x-amz-date": : UTC timestamp in the following ISO 8601 format: YYYYMMDD'T'HHMMSS'Z' + /// * "x-api-key": : Api key configured for AppSync API + /// The value of payload is {} + /// - Parameter request: Signed request + public func interceptConnection(_ request: AppSyncConnectionRequest, + for endpoint: URL) -> AppSyncConnectionRequest { + let host = endpoint.host! + let authHeader = APIKeyAuthenticationHeader(apiKey: apiKey, host: host) + let base64Auth = AppSyncJSONHelper.base64AuthenticationBlob(authHeader) + + let payloadData = SubscriptionConstants.emptyPayload.data(using: .utf8) + let payloadBase64 = payloadData?.base64EncodedString() + + guard var urlComponents = URLComponents(url: request.url, resolvingAgainstBaseURL: false) else { + return request + } + let headerQuery = URLQueryItem(name: RealtimeProviderConstants.header, value: base64Auth) + let payloadQuery = URLQueryItem(name: RealtimeProviderConstants.payload, value: payloadBase64) + urlComponents.queryItems = [headerQuery, payloadQuery] + guard let url = urlComponents.url else { + return request + } + let signedRequest = AppSyncConnectionRequest(url: url) + return signedRequest + } + + public func interceptMessage(_ message: AppSyncMessage, for endpoint: URL) -> AppSyncMessage { + let host = endpoint.host! + switch message.messageType { + case .subscribe: + let authHeader = APIKeyAuthenticationHeader(apiKey: apiKey, host: host) + var payload = message.payload ?? AppSyncMessage.Payload() + payload.authHeader = authHeader + + let signedMessage = AppSyncMessage(id: message.id, + payload: payload, + type: message.messageType) + return signedMessage + default: + AppSyncLogger.debug("Message type does not need signing - \(message.messageType)") + } + return message + } +} + +/// Authentication header for API key based auth +private class APIKeyAuthenticationHeader: AuthenticationHeader { + static let ISO8601DateFormat: String = "yyyyMMdd'T'HHmmss'Z'" + let date: String? + let apiKey: String + + var formatter: DateFormatter = { + var formatter = DateFormatter() + formatter.timeZone = TimeZone(secondsFromGMT: 0) + formatter.locale = Locale(identifier: "en_US_POSIX") + formatter.dateFormat = ISO8601DateFormat + return formatter + }() + + init(apiKey: String, host: String) { + self.date = formatter.string(from: Date()) + self.apiKey = apiKey + super.init(host: host) + } + + private enum CodingKeys: String, CodingKey { + case date = "x-amz-date" + case apiKey = "x-api-key" + } + + override func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(date, forKey: .date) + try container.encode(apiKey, forKey: .apiKey) + try super.encode(to: encoder) + } +} diff --git a/AppSyncRealTimeClient/Interceptor/OIDCAuthInterceptor.swift b/AppSyncRealTimeClient/Interceptor/OIDCAuthInterceptor.swift new file mode 100644 index 00000000..245e4830 --- /dev/null +++ b/AppSyncRealTimeClient/Interceptor/OIDCAuthInterceptor.swift @@ -0,0 +1,91 @@ +// +// Copyright 2018-2020 Amazon.com, +// Inc. or its affiliates. All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import Foundation + +public class OIDCAuthInterceptor: AuthInterceptor { + + let authProvider: OIDCAuthProvider + + public init (_ authProvider: OIDCAuthProvider) { + self.authProvider = authProvider + } + + public func interceptMessage(_ message: AppSyncMessage, for endpoint: URL) -> AppSyncMessage { + let host = endpoint.host! + let jwtToken: String + switch authProvider.getLatestAuthToken() { + case .success(let token): + jwtToken = token + case .failure: + return message + } + switch message.messageType { + case .subscribe: + let authHeader = UserPoolsAuthenticationHeader(token: jwtToken, host: host) + var payload = message.payload ?? AppSyncMessage.Payload() + payload.authHeader = authHeader + + let signedMessage = AppSyncMessage(id: message.id, + payload: payload, + type: message.messageType) + return signedMessage + default: + AppSyncLogger.debug("Message type does not need signing - \(message.messageType)") + } + return message + } + + public func interceptConnection(_ request: AppSyncConnectionRequest, for endpoint: URL) -> AppSyncConnectionRequest { + let host = endpoint.host! + let jwtToken: String + switch authProvider.getLatestAuthToken() { + case .success(let token): + jwtToken = token + case .failure: + return request + } + + let authHeader = UserPoolsAuthenticationHeader(token: jwtToken, host: host) + let base64Auth = AppSyncJSONHelper.base64AuthenticationBlob(authHeader) + + let payloadData = SubscriptionConstants.emptyPayload.data(using: .utf8) + let payloadBase64 = payloadData?.base64EncodedString() + + guard var urlComponents = URLComponents(url: request.url, resolvingAgainstBaseURL: false) else { + return request + } + let headerQuery = URLQueryItem(name: RealtimeProviderConstants.header, value: base64Auth) + let payloadQuery = URLQueryItem(name: RealtimeProviderConstants.payload, value: payloadBase64) + urlComponents.queryItems = [headerQuery, payloadQuery] + guard let url = urlComponents.url else { + return request + } + let signedRequest = AppSyncConnectionRequest(url: url) + return signedRequest + } +} + +/// Authentication header for user pool based auth +private class UserPoolsAuthenticationHeader: AuthenticationHeader { + let authorization: String + + init(token: String, host: String) { + self.authorization = token + super.init(host: host) + } + + private enum CodingKeys: String, CodingKey { + case authorization = "Authorization" + } + + override func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(authorization, forKey: .authorization) + try super.encode(to: encoder) + } +} diff --git a/AppSyncRealTimeClient/Support/RealtimeGatewayURLInterceptor.swift b/AppSyncRealTimeClient/Interceptor/RealtimeGatewayURLInterceptor.swift similarity index 85% rename from AppSyncRealTimeClient/Support/RealtimeGatewayURLInterceptor.swift rename to AppSyncRealTimeClient/Interceptor/RealtimeGatewayURLInterceptor.swift index 9344abb6..f4703470 100644 --- a/AppSyncRealTimeClient/Support/RealtimeGatewayURLInterceptor.swift +++ b/AppSyncRealTimeClient/Interceptor/RealtimeGatewayURLInterceptor.swift @@ -8,13 +8,9 @@ import Foundation /// Connection interceptor for real time connection provider -public class RealtimeGatewayURLInterceptor: ConnectionInterceptor { +class RealtimeGatewayURLInterceptor: ConnectionInterceptor { - public init() { - - } - - public func interceptConnection(_ request: AppSyncConnectionRequest, + func interceptConnection(_ request: AppSyncConnectionRequest, for endpoint: URL) -> AppSyncConnectionRequest { guard let host = endpoint.host else { return request diff --git a/AppSyncRealTimeClient/Support/AppSyncJSONHelper.swift b/AppSyncRealTimeClient/Support/AppSyncJSONHelper.swift new file mode 100644 index 00000000..b72184d9 --- /dev/null +++ b/AppSyncRealTimeClient/Support/AppSyncJSONHelper.swift @@ -0,0 +1,23 @@ +// +// Copyright 2018-2020 Amazon.com, +// Inc. or its affiliates. All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import Foundation + +public struct AppSyncJSONHelper { + + public static func base64AuthenticationBlob(_ header: AuthenticationHeader ) -> String { + let jsonEncoder = JSONEncoder() + do { + let jsonHeader = try jsonEncoder.encode(header) + AppSyncLogger.verbose("Header - \(String(describing: String(data: jsonHeader, encoding: .utf8)))") + return jsonHeader.base64EncodedString() + } catch { + AppSyncLogger.error(error.localizedDescription) + } + return "" + } +} diff --git a/AppSyncRealTimeClient/Support/OIDCAuthProvider.swift b/AppSyncRealTimeClient/Support/OIDCAuthProvider.swift new file mode 100644 index 00000000..6eda03c6 --- /dev/null +++ b/AppSyncRealTimeClient/Support/OIDCAuthProvider.swift @@ -0,0 +1,10 @@ +// +// Copyright 2018-2020 Amazon.com, +// Inc. or its affiliates. All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +public protocol OIDCAuthProvider { + func getLatestAuthToken() -> Result +} diff --git a/AppSyncRealTimeClientIntegrationTests/AppSyncRealTimeClientIntegrationTests.swift b/AppSyncRealTimeClientIntegrationTests/AppSyncRealTimeClientIntegrationTests.swift new file mode 100644 index 00000000..8d9a8c70 --- /dev/null +++ b/AppSyncRealTimeClientIntegrationTests/AppSyncRealTimeClientIntegrationTests.swift @@ -0,0 +1,87 @@ +// +// Copyright 2018-2020 Amazon.com, +// Inc. or its affiliates. All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import XCTest +@testable import AppSyncRealTimeClient + +class AppSyncRealTimeClientIntegrationTests: XCTestCase { + + var url: URL! + var apiKey: String! + + override func setUp() { + do { + let json = try ConfigurationHelper.retrieve(forResource: "amplifyconfiguration") + if let data = json as? [String: Any], + let api = data["api"] as? [String: Any], + let plugins = api["plugins"] as? [String: Any], + let awsAPIPlugin = plugins["awsAPIPlugin"] as? [String: Any], + let apiNameOptional = awsAPIPlugin.first, + let apiName = apiNameOptional.value as? [String: Any], + let endpoint = apiName["endpoint"] as? String, + let apiKey = apiName["apiKey"] as? String { + + url = URL(string: endpoint) + self.apiKey = apiKey + } else { + throw "Could not retrieve endpoint" + } + + } catch { + print("Error \(error)") + } + } + + /// Simple integration test against an AppSync service provisioned with a simple Todo model generated by the + /// GraphQL Transform on the `model` directive. + /// + /// - Given: A subscription connection on an AppSync endpoint with Todo model provisioned + /// - When: + /// - Subscribe to the `onCreateTodo` + /// - Then: + /// - Webosocket connection and subscription connection is established. + /// + func testSubscribeWithSubscriptionConnection() { + let subscribeSuccess = expectation(description: "subscribe successfully") + let authInterceptor = APIKeyAuthInterceptor(apiKey) + let connectionProvider = ConnectionProviderFactory.createConnectionProvider(for: url, + authInterceptor: authInterceptor, + connectionType: .appSyncRealtime) + + let subscriptionConnection = AppSyncSubscriptionConnection(provider: connectionProvider) + let requestString = """ + subscription onCreate { + onCreateTodo{ + id + description + name + } + } + """ + _ = subscriptionConnection.subscribe(requestString: requestString, variables: nil) { (event, item) in + + switch event { + case .connection(let subscriptionConnectionEvent): + switch subscriptionConnectionEvent { + case .connecting: + break + case .connected: + subscribeSuccess.fulfill() + case .disconnected: + break + } + case .data(let data): + print("Got data back \(data)") + case .failed(let error): + XCTFail("Got error \(error)") + } + } + + wait(for: [subscribeSuccess], timeout: TestCommonConstants.networkTimeout) + } +} + diff --git a/AppSyncRealTimeClientIntegrationTests/Info.plist b/AppSyncRealTimeClientIntegrationTests/Info.plist new file mode 100644 index 00000000..64d65ca4 --- /dev/null +++ b/AppSyncRealTimeClientIntegrationTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/AppSyncRealTimeClientIntegrationTests/README.md b/AppSyncRealTimeClientIntegrationTests/README.md new file mode 100644 index 00000000..d9b1e0d7 --- /dev/null +++ b/AppSyncRealTimeClientIntegrationTests/README.md @@ -0,0 +1,34 @@ +## AppSync RealTime GraphQL Service + +The following steps demonstrate how to set up a GraphQL endpoint with AppSync. The auth configured will be API key. The set up is used to run the integration tests. + + +### Set-up + +1. `amplify-init` + +2. `amplify add api` + + +```perl +? Please select from one of the below mentioned services: `GraphQL` +? Provide API name: `` +? Choose the default authorization type for the API `API key` +? Enter a description for the API key: +? After how many days from now the API key should expire (1-365): `365` +? Do you want to configure advanced settings for the GraphQL API `No, I am done` +? Do you have an annotated GraphQL schema? `Yes` +? Provide your schema file path: `schema.graphql` +``` +When asked to provide the schema, create the `schema.graphql` file +``` +type Todo @model { + id: ID! + name: String! + description: String +} +``` + +3. `amplify push` + +4. Copy `amplifyconfiguration.json` over to this integration test project. This file is already set up to be copied to the HostApp bundle and loaded when the test is run. diff --git a/AppSyncRealTimeClientIntegrationTests/Support/ConfigurationHelper.swift b/AppSyncRealTimeClientIntegrationTests/Support/ConfigurationHelper.swift new file mode 100644 index 00000000..88c183a5 --- /dev/null +++ b/AppSyncRealTimeClientIntegrationTests/Support/ConfigurationHelper.swift @@ -0,0 +1,21 @@ +// +// Copyright 2018-2020 Amazon.com, +// Inc. or its affiliates. All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import Foundation + +class ConfigurationHelper { + static func retrieve(forResource: String) throws -> Any { + guard let path = Bundle.main.path(forResource: forResource, ofType: "json") else { + throw "Could not retrieve configuration file: \(forResource)" + } + + let url = URL(fileURLWithPath: path) + let data = try Data(contentsOf: url) + let json = try JSONSerialization.jsonObject(with: data) + return json + } +} diff --git a/AppSyncRealTimeClientIntegrationTests/Support/Error+Extension.swift b/AppSyncRealTimeClientIntegrationTests/Support/Error+Extension.swift new file mode 100644 index 00000000..a5e85801 --- /dev/null +++ b/AppSyncRealTimeClientIntegrationTests/Support/Error+Extension.swift @@ -0,0 +1,8 @@ +// +// Copyright 2018-2020 Amazon.com, +// Inc. or its affiliates. All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +extension String: Error { } diff --git a/AppSyncRealTimeClientIntegrationTests/Support/TestCommonConstants.swift b/AppSyncRealTimeClientIntegrationTests/Support/TestCommonConstants.swift new file mode 100644 index 00000000..d6798e97 --- /dev/null +++ b/AppSyncRealTimeClientIntegrationTests/Support/TestCommonConstants.swift @@ -0,0 +1,12 @@ +// +// Copyright 2018-2020 Amazon.com, +// Inc. or its affiliates. All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import Foundation + +class TestCommonConstants { + static let networkTimeout = TimeInterval(180) +} diff --git a/AppSyncRealTimeClientTests/ConnectionProvider/RealtimeConnectionProviderTests.swift b/AppSyncRealTimeClientTests/ConnectionProvider/RealtimeConnectionProviderTests.swift index 2095414d..8ba64787 100644 --- a/AppSyncRealTimeClientTests/ConnectionProvider/RealtimeConnectionProviderTests.swift +++ b/AppSyncRealTimeClientTests/ConnectionProvider/RealtimeConnectionProviderTests.swift @@ -28,5 +28,4 @@ class RealtimeConnectionProviderTests: XCTestCase { // Put the code you want to measure the time of here. } } - } diff --git a/AppSyncRealTimeClientTests/Interceptor/APIKeyAuthInterceptorTests.swift b/AppSyncRealTimeClientTests/Interceptor/APIKeyAuthInterceptorTests.swift new file mode 100644 index 00000000..969e845a --- /dev/null +++ b/AppSyncRealTimeClientTests/Interceptor/APIKeyAuthInterceptorTests.swift @@ -0,0 +1,39 @@ +// +// Copyright 2018-2020 Amazon.com, +// Inc. or its affiliates. All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import XCTest +import AppSyncRealTimeClient + +class APIKeyAuthInterceptorTests: XCTestCase { + + var apiKeyAuthInterceptor: APIKeyAuthInterceptor! + + override func setUp() { + apiKeyAuthInterceptor = APIKeyAuthInterceptor("mock_api_key") + } + + func testInterceptRequest() { + let url = URL(string: "http://xxxc.appsync-api.ap-southeast-2.amazonaws.com/sd")! + let request = AppSyncConnectionRequest(url: url) + let signedRequest = apiKeyAuthInterceptor.interceptConnection(request, for: url) + + guard let queries = URLComponents(url: signedRequest.url, resolvingAgainstBaseURL: true)?.queryItems else { + assertionFailure("Query parameters should not be nil") + return + } + XCTAssertTrue(queries.contains{ $0.name == "header"}, "Should contain the header query") + XCTAssertTrue(queries.contains{ $0.name == "payload"}, "Should contain the payload query") + } + + func testInterceptMessage() { + let message = AppSyncMessage(type: .subscribe("start")) + let url = URL(string: "http://xxxc.appsync-api.ap-southeast-2.amazonaws.com/sd")! + let signedMessage = apiKeyAuthInterceptor.interceptMessage(message, for: url) + XCTAssertNotNil(signedMessage.payload?.authHeader) + + } +} diff --git a/AppSyncRealTimeClientTests/Interceptor/AppSyncJSONHelperTests.swift b/AppSyncRealTimeClientTests/Interceptor/AppSyncJSONHelperTests.swift new file mode 100644 index 00000000..5a5a007d --- /dev/null +++ b/AppSyncRealTimeClientTests/Interceptor/AppSyncJSONHelperTests.swift @@ -0,0 +1,54 @@ +// +// Copyright 2018-2020 Amazon.com, +// Inc. or its affiliates. All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import XCTest +@testable import AppSyncRealTimeClient + +class AppSyncJSONHelperTests: XCTestCase { + + /// Test if we get the right base 64 value + /// + /// - Given: A valid auth header + /// - When: + /// - I invoke the method `base64AuthenticationBlob` + /// - Then: + /// - I should get a valid base64 value + /// + func testJSONParsing() { + let authHeader = AuthenticationHeader(host: "http://asd.com") + let result = AppSyncJSONHelper.base64AuthenticationBlob(authHeader) + XCTAssertNotNil(result, "Result should not be nil") + XCTAssertEqual(result, "eyJob3N0IjoiaHR0cDpcL1wvYXNkLmNvbSJ9", "Base 64 encoded result should match") + } + + /// Test to check invalid json returns empty string + /// + /// - Given: An invalid auth header + /// - When: + /// - I invoke the method `base64AuthenticationBlob` + /// - Then: + /// - I should get back an empty string + /// + func testInValidJSONParsing() { + let authHeader = MockInvalidHeader(host: "http://asd.com") + let result = AppSyncJSONHelper.base64AuthenticationBlob(authHeader) + XCTAssertNotNil(result, "Result should not be nil") + XCTAssertEqual(result, "", "Base 64 encoded result should be empty") + } +} + + +private class MockInvalidHeader: AuthenticationHeader { + + private enum CodingKeys: String, CodingKey { + case apiKey = "x-api-key" + } + + override func encode(to encoder: Encoder) throws { + throw EncodingError.invalidValue("Error", EncodingError.Context(codingPath: [], debugDescription: "")) + } +} diff --git a/AppSyncRealTimeClientTests/Interceptor/OIDCAuthInterceptorTests.swift b/AppSyncRealTimeClientTests/Interceptor/OIDCAuthInterceptorTests.swift new file mode 100644 index 00000000..fa7e2275 --- /dev/null +++ b/AppSyncRealTimeClientTests/Interceptor/OIDCAuthInterceptorTests.swift @@ -0,0 +1,45 @@ +// +// Copyright 2018-2020 Amazon.com, +// Inc. or its affiliates. All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import XCTest +import AppSyncRealTimeClient + +class OIDCAuthInterceptorTests: XCTestCase { + + var authInterceptor: OIDCAuthInterceptor! + + override func setUp() { + authInterceptor = OIDCAuthInterceptor(MockUserPoolsAuthProvider()) + } + + func testInterceptRequest() { + let url = URL(string: "http://xxxc.appsync-api.ap-southeast-2.amazonaws.com/sd")! + let request = AppSyncConnectionRequest(url: url) + let signedRequest = authInterceptor.interceptConnection(request, for: url) + + guard let queries = URLComponents(url: signedRequest.url, resolvingAgainstBaseURL: true)?.queryItems else { + assertionFailure("Query parameters should not be nil") + return + } + XCTAssertTrue(queries.contains{ $0.name == "header"}, "Should contain the header query") + XCTAssertTrue(queries.contains{ $0.name == "payload"}, "Should contain the payload query") + } + + func testInterceptMessage() { + let message = AppSyncMessage(type: .subscribe("start")) + let url = URL(string: "http://xxxc.appsync-api.ap-southeast-2.amazonaws.com/sd")! + let signedMessage = authInterceptor.interceptMessage(message, for: url) + XCTAssertNotNil(signedMessage.payload?.authHeader) + + } +} + +class MockUserPoolsAuthProvider: OIDCAuthProvider { + func getLatestAuthToken() -> Result { + return .success("jwtToken") + } +} diff --git a/AppSyncRealTimeClientTests/Support/RealtimeGatewayURLInterceptorTests.swift b/AppSyncRealTimeClientTests/Support/RealtimeGatewayURLInterceptorTests.swift index 20a41a3d..467fd83b 100644 --- a/AppSyncRealTimeClientTests/Support/RealtimeGatewayURLInterceptorTests.swift +++ b/AppSyncRealTimeClientTests/Support/RealtimeGatewayURLInterceptorTests.swift @@ -6,7 +6,7 @@ // import XCTest -import AppSyncRealTimeClient +@testable import AppSyncRealTimeClient class RealtimeGatewayURLInterceptorTests: XCTestCase { diff --git a/CHANGELOG.md b/CHANGELOG.md index d8f04cd0..9da3bee9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # AppSync RealTime Client for iOS +## 1.0.2 + +Add Interceptors and interceptor protocols ## 1.0.1 diff --git a/HostApp/AppDelegate.swift b/HostApp/AppDelegate.swift new file mode 100644 index 00000000..932a0c40 --- /dev/null +++ b/HostApp/AppDelegate.swift @@ -0,0 +1,36 @@ +// +// Copyright 2018-2020 Amazon.com, +// Inc. or its affiliates. All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import UIKit + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + // Override point for customization after application launch. + return true + } + + // MARK: UISceneSession Lifecycle + + func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { + // Called when a new scene session is being created. + // Use this method to select a configuration to create the new scene with. + return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) + } + + func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { + // Called when the user discards a scene session. + // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. + // Use this method to release any resources that were specific to the discarded scenes, as they will not return. + } + + +} + diff --git a/HostApp/Assets.xcassets/AppIcon.appiconset/Contents.json b/HostApp/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..d8db8d65 --- /dev/null +++ b/HostApp/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/HostApp/Assets.xcassets/Contents.json b/HostApp/Assets.xcassets/Contents.json new file mode 100644 index 00000000..da4a164c --- /dev/null +++ b/HostApp/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/HostApp/Base.lproj/LaunchScreen.storyboard b/HostApp/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 00000000..865e9329 --- /dev/null +++ b/HostApp/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/HostApp/ContentView.swift b/HostApp/ContentView.swift new file mode 100644 index 00000000..db6c7d2c --- /dev/null +++ b/HostApp/ContentView.swift @@ -0,0 +1,20 @@ +// +// Copyright 2018-2020 Amazon.com, +// Inc. or its affiliates. All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import SwiftUI + +struct ContentView: View { + var body: some View { + Text("Hello, World!") + } +} + +struct ContentView_Previews: PreviewProvider { + static var previews: some View { + ContentView() + } +} diff --git a/HostApp/Info.plist b/HostApp/Info.plist new file mode 100644 index 00000000..9742bf0f --- /dev/null +++ b/HostApp/Info.plist @@ -0,0 +1,60 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + $(PRODUCT_MODULE_NAME).SceneDelegate + + + + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/HostApp/Preview Content/Preview Assets.xcassets/Contents.json b/HostApp/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 00000000..da4a164c --- /dev/null +++ b/HostApp/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/HostApp/SceneDelegate.swift b/HostApp/SceneDelegate.swift new file mode 100644 index 00000000..94cacac2 --- /dev/null +++ b/HostApp/SceneDelegate.swift @@ -0,0 +1,63 @@ +// +// Copyright 2018-2020 Amazon.com, +// Inc. or its affiliates. All Rights Reserved. +// +// SPDX-License-Identifier: Apache-2.0 +// + +import UIKit +import SwiftUI + +class SceneDelegate: UIResponder, UIWindowSceneDelegate { + + var window: UIWindow? + + + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { + // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. + // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. + // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). + + // Create the SwiftUI view that provides the window contents. + let contentView = ContentView() + + // Use a UIHostingController as window root view controller. + if let windowScene = scene as? UIWindowScene { + let window = UIWindow(windowScene: windowScene) + window.rootViewController = UIHostingController(rootView: contentView) + self.window = window + window.makeKeyAndVisible() + } + } + + func sceneDidDisconnect(_ scene: UIScene) { + // Called as the scene is being released by the system. + // This occurs shortly after the scene enters the background, or when its session is discarded. + // Release any resources associated with this scene that can be re-created the next time the scene connects. + // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead). + } + + func sceneDidBecomeActive(_ scene: UIScene) { + // Called when the scene has moved from an inactive state to an active state. + // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. + } + + func sceneWillResignActive(_ scene: UIScene) { + // Called when the scene will move from an active state to an inactive state. + // This may occur due to temporary interruptions (ex. an incoming phone call). + } + + func sceneWillEnterForeground(_ scene: UIScene) { + // Called as the scene transitions from the background to the foreground. + // Use this method to undo the changes made on entering the background. + } + + func sceneDidEnterBackground(_ scene: UIScene) { + // Called as the scene transitions from the foreground to the background. + // Use this method to save data, release shared resources, and store enough scene-specific state information + // to restore the scene back to its current state. + } + + +} + diff --git a/Podfile b/Podfile index d1f59e1e..42c99084 100644 --- a/Podfile +++ b/Podfile @@ -13,3 +13,10 @@ target 'AppSyncRealTimeClient' do end end + +target "HostApp" do + use_frameworks! + target "AppSyncRealTimeClientIntegrationTests" do + inherit! :complete + end +end