Compare commits
2 Commits
main
...
feat/conne
Author | SHA1 | Date |
---|---|---|
![]() |
fc84acdb84 | |
![]() |
9685d376f0 |
|
@ -1,12 +1,10 @@
|
||||||
# .circleci/config.yml
|
# .circleci/config.yml
|
||||||
|
|
||||||
version: 2.1
|
version: 2.1
|
||||||
orbs:
|
|
||||||
aws-cli: circleci/aws-cli@3.1.4
|
|
||||||
|
|
||||||
defaults: &defaults
|
defaults: &defaults
|
||||||
macos:
|
macos:
|
||||||
xcode: '14.2.0'
|
xcode: "11.2.1"
|
||||||
working_directory: ~/aws-appsync-realtime-client-ios
|
working_directory: ~/aws-appsync-realtime-client-ios
|
||||||
environment:
|
environment:
|
||||||
BUNDLE_PATH: vendor/bundle
|
BUNDLE_PATH: vendor/bundle
|
||||||
|
@ -49,8 +47,8 @@ commands:
|
||||||
steps:
|
steps:
|
||||||
- restore_cache:
|
- restore_cache:
|
||||||
keys:
|
keys:
|
||||||
- app-{{ .Environment.CACHE_VERSION }}-gems-{{ checksum "~/aws-appsync-realtime-client-ios/Gemfile.lock" }}
|
- v1-gems-{{ checksum "~/aws-appsync-realtime-client-ios/Gemfile.lock" }}
|
||||||
- app-{{ .Environment.CACHE_VERSION }}-gems-
|
- v1-gems-
|
||||||
|
|
||||||
check_bundle:
|
check_bundle:
|
||||||
steps:
|
steps:
|
||||||
|
@ -96,12 +94,12 @@ jobs:
|
||||||
- restore_gems
|
- restore_gems
|
||||||
- run:
|
- run:
|
||||||
name: Bundle install
|
name: Bundle install
|
||||||
command: bundle config set --local path 'vendor/bundle' && bundle install
|
command: bundle check --path $BUNDLE_PATH || bundle install --path $BUNDLE_PATH
|
||||||
environment:
|
environment:
|
||||||
BUNDLE_JOBS: 4
|
BUNDLE_JOBS: 4
|
||||||
BUNDLE_RETRY: 3
|
BUNDLE_RETRY: 3
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: app-{{ .Environment.CACHE_VERSION }}-gems-{{ checksum "~/aws-appsync-realtime-client-ios/Gemfile.lock" }}
|
key: v1-gems-{{ checksum "~/aws-appsync-realtime-client-ios/Gemfile.lock" }}
|
||||||
paths:
|
paths:
|
||||||
- vendor/bundle
|
- vendor/bundle
|
||||||
build_test_appsync_realtime_client:
|
build_test_appsync_realtime_client:
|
||||||
|
@ -109,15 +107,15 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- *restore_repo
|
- *restore_repo
|
||||||
- pre_start_simulator
|
- pre_start_simulator
|
||||||
|
- run: pod install
|
||||||
- restore_gems
|
- restore_gems
|
||||||
- run: bundle exec pod install
|
|
||||||
- check_bundle
|
- check_bundle
|
||||||
- run:
|
- run:
|
||||||
name: Build AppSyncRealTimeClient
|
name: Build AppSyncRealTimeClient
|
||||||
command: xcodebuild build-for-testing -workspace AppSyncRealTimeClient.xcworkspace -scheme AppSyncRealTimeClient -sdk iphonesimulator -destination "${destination}" | xcpretty
|
command: xcodebuild build-for-testing -workspace AppSyncRealTimeClient.xcworkspace -scheme AppSyncRealTimeClient -sdk iphonesimulator -destination "${destination}" | xcpretty
|
||||||
- run:
|
- run:
|
||||||
name: Test AppSyncRealTimeClient
|
name: Test AppSyncRealTimeClient
|
||||||
command: xcodebuild test -workspace AppSyncRealTimeClient.xcworkspace -scheme AppSyncRealTimeClient -sdk iphonesimulator -destination "${destination}" | xcpretty --simple --color --report junit
|
command: xcodebuild test -enableThreadSanitizer NO -workspace AppSyncRealTimeClient.xcworkspace -scheme AppSyncRealTimeClient -sdk iphonesimulator -destination "${destination}" | xcpretty --simple --color --report junit
|
||||||
- store_test_results:
|
- store_test_results:
|
||||||
path: build/reports
|
path: build/reports
|
||||||
deploy:
|
deploy:
|
||||||
|
@ -126,10 +124,6 @@ jobs:
|
||||||
steps:
|
steps:
|
||||||
- *restore_repo
|
- *restore_repo
|
||||||
- restore_gems
|
- restore_gems
|
||||||
- aws-cli/setup:
|
|
||||||
role-arn: $AWS_OIDC_ROLE_ARN
|
|
||||||
role-session-name: "${CIRCLE_WORKFLOW_JOB_ID}.deploy"
|
|
||||||
session-duration: '900'
|
|
||||||
- check_bundle
|
- check_bundle
|
||||||
- run:
|
- run:
|
||||||
name: Release pods
|
name: Release pods
|
||||||
|
@ -145,7 +139,6 @@ workflows:
|
||||||
requires:
|
requires:
|
||||||
- install_gems
|
- install_gems
|
||||||
- deploy:
|
- deploy:
|
||||||
context: amplify-swift-aws-oidc
|
|
||||||
filters:
|
filters:
|
||||||
branches:
|
branches:
|
||||||
only:
|
only:
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
* @aws-amplify/amplify-ios
|
|
|
@ -43,8 +43,6 @@ credentials-mc.json
|
||||||
|
|
||||||
amplify
|
amplify
|
||||||
build/
|
build/
|
||||||
.build/
|
|
||||||
.vscode/
|
|
||||||
dist/
|
dist/
|
||||||
node_modules/
|
node_modules/
|
||||||
aws-exports.js
|
aws-exports.js
|
||||||
|
|
|
@ -10,11 +10,13 @@
|
||||||
--elseposition same-line
|
--elseposition same-line
|
||||||
|
|
||||||
--enable fileHeader
|
--enable fileHeader
|
||||||
--header "//\n// Copyright Amazon.com Inc. or its affiliates.\n// All Rights Reserved.\n//\n// SPDX-License-Identifier: Apache-2.0\n//"
|
--header "//\n// Copyright 2018-{created.year} Amazon.com,\n// Inc. or its affiliates. All Rights Reserved.\n//\n// SPDX-License-Identifier: Apache-2.0\n//"
|
||||||
|
|
||||||
--disable hoistPatternLet
|
--disable hoistPatternLet
|
||||||
|
--patternlet inline
|
||||||
|
|
||||||
--disable indent
|
--disable indent
|
||||||
|
--ifdef outdent
|
||||||
--indent 4
|
--indent 4
|
||||||
--indentcase false
|
--indentcase false
|
||||||
--xcodeindentation disabled
|
--xcodeindentation disabled
|
||||||
|
@ -43,6 +45,7 @@
|
||||||
--semicolons never
|
--semicolons never
|
||||||
|
|
||||||
--disable sortedImports
|
--disable sortedImports
|
||||||
|
--importgrouping testable-bottom
|
||||||
|
|
||||||
--enable spaceAroundOperators
|
--enable spaceAroundOperators
|
||||||
--operatorfunc spaced
|
--operatorfunc spaced
|
||||||
|
@ -51,11 +54,13 @@
|
||||||
--trailingclosures
|
--trailingclosures
|
||||||
|
|
||||||
--disable trailingCommas
|
--disable trailingCommas
|
||||||
|
--commas inline
|
||||||
|
|
||||||
--enable trailingSpace
|
--enable trailingSpace
|
||||||
--trimwhitespace always
|
--trimwhitespace always
|
||||||
|
|
||||||
--disable unusedArguments
|
--disable unusedArguments
|
||||||
|
--stripunusedargs closure-only
|
||||||
|
|
||||||
--enable void
|
--enable void
|
||||||
--empty void
|
--empty void
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2020 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2020 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2020 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2020 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
Pod::Spec.new do |s|
|
Pod::Spec.new do |s|
|
||||||
|
|
||||||
s.name = 'AppSyncRealTimeClient'
|
s.name = 'AppSyncRealTimeClient'
|
||||||
s.version = '3.1.0'
|
s.version = '1.5.0'
|
||||||
s.summary = 'Amazon Web Services AppSync RealTime Client for iOS.'
|
s.summary = 'Amazon Web Services AppSync RealTime Client for iOS.'
|
||||||
|
|
||||||
s.description = 'AppSync RealTime Client provides subscription connections to AppSync websocket endpoints'
|
s.description = 'AppSync RealTime Client provides subscription connections to AppSync websocket endpoints'
|
||||||
|
@ -16,5 +16,5 @@ Pod::Spec.new do |s|
|
||||||
s.requires_arc = true
|
s.requires_arc = true
|
||||||
|
|
||||||
s.source_files = 'AppSyncRealTimeClient/**/*.swift'
|
s.source_files = 'AppSyncRealTimeClient/**/*.swift'
|
||||||
s.dependency 'Starscream', '~> 4.0.4'
|
s.dependency 'Starscream', '~> 3.1.1'
|
||||||
end
|
end
|
|
@ -3,18 +3,13 @@
|
||||||
archiveVersion = 1;
|
archiveVersion = 1;
|
||||||
classes = {
|
classes = {
|
||||||
};
|
};
|
||||||
objectVersion = 54;
|
objectVersion = 51;
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
0B59161BB37D32073E4FD61B /* Pods_AppSyncRTCSample.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7CF486070B34EFD15B4DB8FC /* Pods_AppSyncRTCSample.framework */; };
|
0B59161BB37D32073E4FD61B /* Pods_AppSyncRTCSample.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7CF486070B34EFD15B4DB8FC /* Pods_AppSyncRTCSample.framework */; };
|
||||||
2143D46727B5D9B40066B2F7 /* RealTimeConnectionProviderResponseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2143D46627B5D9B40066B2F7 /* RealTimeConnectionProviderResponseTests.swift */; };
|
|
||||||
2143D4B027BC49BE0066B2F7 /* AWSAppSyncRealTimeClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2143D4AF27BC49BE0066B2F7 /* AWSAppSyncRealTimeClient.swift */; };
|
|
||||||
2143D4B227BE40B30066B2F7 /* AppSyncRealTimeClientFailureTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2143D4B127BE40B30066B2F7 /* AppSyncRealTimeClientFailureTests.swift */; };
|
|
||||||
2151D5CA27C68C3C00F3C866 /* ConnectionProviderHandleErrorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2151D5C927C68C3C00F3C866 /* ConnectionProviderHandleErrorTests.swift */; };
|
|
||||||
2164E65D2639AD5600385027 /* StarscreamAdapterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2164E65C2639AD5600385027 /* StarscreamAdapterTests.swift */; };
|
2164E65D2639AD5600385027 /* StarscreamAdapterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2164E65C2639AD5600385027 /* StarscreamAdapterTests.swift */; };
|
||||||
2164E674263C58CE00385027 /* AppSyncRealTimeClientTestBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2164E673263C58CD00385027 /* AppSyncRealTimeClientTestBase.swift */; };
|
2164E674263C58CE00385027 /* AppSyncRealTimeClientTestBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2164E673263C58CD00385027 /* AppSyncRealTimeClientTestBase.swift */; };
|
||||||
217C74D227C45B6600AE054F /* AppSyncSubscriptionConnectionErrorHandlerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 217C74D127C45B6600AE054F /* AppSyncSubscriptionConnectionErrorHandlerTests.swift */; };
|
|
||||||
217F39992405D9D500F1A0B3 /* AppSyncRealTimeClient.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 217F398F2405D9D500F1A0B3 /* AppSyncRealTimeClient.framework */; };
|
217F39992405D9D500F1A0B3 /* AppSyncRealTimeClient.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 217F398F2405D9D500F1A0B3 /* AppSyncRealTimeClient.framework */; };
|
||||||
217F39CC2406E98400F1A0B3 /* AppSyncResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 217F39AA2406E98300F1A0B3 /* AppSyncResponse.swift */; };
|
217F39CC2406E98400F1A0B3 /* AppSyncResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 217F39AA2406E98300F1A0B3 /* AppSyncResponse.swift */; };
|
||||||
217F39CD2406E98400F1A0B3 /* InterceptableConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 217F39AB2406E98300F1A0B3 /* InterceptableConnection.swift */; };
|
217F39CD2406E98400F1A0B3 /* InterceptableConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 217F39AB2406E98300F1A0B3 /* InterceptableConnection.swift */; };
|
||||||
|
@ -47,11 +42,6 @@
|
||||||
217F39F12406EA4000F1A0B3 /* MockConnectionProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 217F39EB2406EA3F00F1A0B3 /* MockConnectionProvider.swift */; };
|
217F39F12406EA4000F1A0B3 /* MockConnectionProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 217F39EB2406EA3F00F1A0B3 /* MockConnectionProvider.swift */; };
|
||||||
217F39F22406EA4000F1A0B3 /* ConnectionProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 217F39ED2406EA4000F1A0B3 /* ConnectionProviderTests.swift */; };
|
217F39F22406EA4000F1A0B3 /* ConnectionProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 217F39ED2406EA4000F1A0B3 /* ConnectionProviderTests.swift */; };
|
||||||
217F39F32406EA4000F1A0B3 /* RealtimeGatewayURLInterceptorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 217F39EF2406EA4000F1A0B3 /* RealtimeGatewayURLInterceptorTests.swift */; };
|
217F39F32406EA4000F1A0B3 /* RealtimeGatewayURLInterceptorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 217F39EF2406EA4000F1A0B3 /* RealtimeGatewayURLInterceptorTests.swift */; };
|
||||||
219BFF3B27A38902000FC148 /* schema.graphql in Resources */ = {isa = PBXBuildFile; fileRef = 219BFF3A27A38902000FC148 /* schema.graphql */; };
|
|
||||||
219BFF4927A3B238000FC148 /* ConnectivityMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 219BFF4827A3B237000FC148 /* ConnectivityMonitor.swift */; };
|
|
||||||
219BFF4B27A3B24F000FC148 /* ConnectivityPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 219BFF4A27A3B24F000FC148 /* ConnectivityPath.swift */; };
|
|
||||||
219BFF6A27A9AC6E000FC148 /* ConnectivityMonitorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 219BFF6927A9AC6E000FC148 /* ConnectivityMonitorTests.swift */; };
|
|
||||||
219BFF6C27A9AF85000FC148 /* MockConnectivityMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 219BFF6B27A9AF85000FC148 /* MockConnectivityMonitor.swift */; };
|
|
||||||
21D38B412409AFBD00EC2A8D /* AppSyncRealTimeClientIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21D38B402409AFBD00EC2A8D /* AppSyncRealTimeClientIntegrationTests.swift */; };
|
21D38B412409AFBD00EC2A8D /* AppSyncRealTimeClientIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 21D38B402409AFBD00EC2A8D /* AppSyncRealTimeClientIntegrationTests.swift */; };
|
||||||
21D38B432409AFBD00EC2A8D /* AppSyncRealTimeClient.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 217F398F2405D9D500F1A0B3 /* AppSyncRealTimeClient.framework */; };
|
21D38B432409AFBD00EC2A8D /* AppSyncRealTimeClient.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 217F398F2405D9D500F1A0B3 /* AppSyncRealTimeClient.framework */; };
|
||||||
21D38B4C2409B6C000EC2A8D /* amplifyconfiguration.json in Resources */ = {isa = PBXBuildFile; fileRef = 21D38B4B2409B6C000EC2A8D /* amplifyconfiguration.json */; };
|
21D38B4C2409B6C000EC2A8D /* amplifyconfiguration.json in Resources */ = {isa = PBXBuildFile; fileRef = 21D38B4B2409B6C000EC2A8D /* amplifyconfiguration.json */; };
|
||||||
|
@ -76,22 +66,6 @@
|
||||||
259F9EB83B9F67D0413A6D4C /* Pods_AppSyncRealTimeClient.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 81AFCBED5A97E398A4BD9D06 /* Pods_AppSyncRealTimeClient.framework */; };
|
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 */; };
|
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 */; };
|
4A3EAC9B20D96D81CC3A7EF4 /* Pods_HostApp_AppSyncRealTimeClientIntegrationTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 43D112B03D6D38F84995D6FA /* Pods_HostApp_AppSyncRealTimeClientIntegrationTests.framework */; };
|
||||||
5C4E7C5E289082D3001800C1 /* OIDCAuthProviderAsync.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C4E7C5D289082D3001800C1 /* OIDCAuthProviderAsync.swift */; };
|
|
||||||
5C5609EE2821E6FC0002ACF5 /* InterceptableConnectionAsync.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C5609ED2821E6FC0002ACF5 /* InterceptableConnectionAsync.swift */; };
|
|
||||||
5C95D878289081F0008D66E9 /* OIDCAuthInterceptorAsync.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C95D877289081F0008D66E9 /* OIDCAuthInterceptorAsync.swift */; };
|
|
||||||
5CF3E96B283C33D40036EAD2 /* AppSyncRealTimeClientAsyncFailureTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CF3E968283C33D40036EAD2 /* AppSyncRealTimeClientAsyncFailureTests.swift */; };
|
|
||||||
5CF3E96D283C33D40036EAD2 /* AppSyncRealTimeClientAsyncIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CF3E96A283C33D40036EAD2 /* AppSyncRealTimeClientAsyncIntegrationTests.swift */; };
|
|
||||||
5CFF7233283C1971001D5471 /* RealtimeConnectionProviderAsync.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFF722E283C1971001D5471 /* RealtimeConnectionProviderAsync.swift */; };
|
|
||||||
5CFF7234283C1971001D5471 /* RealtimeConnectionProviderAsync+StaleConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFF722F283C1971001D5471 /* RealtimeConnectionProviderAsync+StaleConnection.swift */; };
|
|
||||||
5CFF7235283C1971001D5471 /* RealtimeConnectionProviderAsync+ConnectionInterceptableAsync.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFF7230283C1971001D5471 /* RealtimeConnectionProviderAsync+ConnectionInterceptableAsync.swift */; };
|
|
||||||
5CFF7236283C1971001D5471 /* RealtimeConnectionProviderAsync+Websocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFF7231283C1971001D5471 /* RealtimeConnectionProviderAsync+Websocket.swift */; };
|
|
||||||
5CFF7237283C1971001D5471 /* RealtimeConnectionProviderAsync+MessageInterceptableAsync.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFF7232283C1971001D5471 /* RealtimeConnectionProviderAsync+MessageInterceptableAsync.swift */; };
|
|
||||||
5CFF7239283C19CF001D5471 /* TaskQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFF7238283C19CF001D5471 /* TaskQueue.swift */; };
|
|
||||||
5CFF723D283C1AF5001D5471 /* ConnectionProviderAsyncTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFF723B283C1AF5001D5471 /* ConnectionProviderAsyncTests.swift */; };
|
|
||||||
5CFF723E283C1AF5001D5471 /* RealtimeConnectionProviderAsyncTestBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFF723C283C1AF5001D5471 /* RealtimeConnectionProviderAsyncTestBase.swift */; };
|
|
||||||
978409B82739C7BE002362A7 /* AppSyncURLHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 978409B72739C7BE002362A7 /* AppSyncURLHelper.swift */; };
|
|
||||||
978409BA2739C7E1002362A7 /* AppSyncURLHelperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 978409B92739C7E1002362A7 /* AppSyncURLHelperTests.swift */; };
|
|
||||||
B4AEC68C29E8A14F00D693CD /* InterceptableConnection+Default.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4AEC68B29E8A14F00D693CD /* InterceptableConnection+Default.swift */; };
|
|
||||||
DCFE701B5D1380E566694A48 /* Pods_AppSyncRealTimeClient_AppSyncRealTimeClientTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3E662A46AB2C93EE316F784C /* Pods_AppSyncRealTimeClient_AppSyncRealTimeClientTests.framework */; };
|
DCFE701B5D1380E566694A48 /* Pods_AppSyncRealTimeClient_AppSyncRealTimeClientTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3E662A46AB2C93EE316F784C /* Pods_AppSyncRealTimeClient_AppSyncRealTimeClientTests.framework */; };
|
||||||
FA67507824D3244A005A1345 /* MockWebsocketProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA67507724D3244A005A1345 /* MockWebsocketProvider.swift */; };
|
FA67507824D3244A005A1345 /* MockWebsocketProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA67507724D3244A005A1345 /* MockWebsocketProvider.swift */; };
|
||||||
FA67507B24D338CB005A1345 /* Error+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA67507A24D338C6005A1345 /* Error+Extension.swift */; };
|
FA67507B24D338CB005A1345 /* Error+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA67507A24D338C6005A1345 /* Error+Extension.swift */; };
|
||||||
|
@ -145,13 +119,8 @@
|
||||||
|
|
||||||
/* Begin PBXFileReference 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 = "<group>"; };
|
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 = "<group>"; };
|
||||||
2143D46627B5D9B40066B2F7 /* RealTimeConnectionProviderResponseTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RealTimeConnectionProviderResponseTests.swift; sourceTree = "<group>"; };
|
|
||||||
2143D4AF27BC49BE0066B2F7 /* AWSAppSyncRealTimeClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AWSAppSyncRealTimeClient.swift; sourceTree = "<group>"; };
|
|
||||||
2143D4B127BE40B30066B2F7 /* AppSyncRealTimeClientFailureTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSyncRealTimeClientFailureTests.swift; sourceTree = "<group>"; };
|
|
||||||
2151D5C927C68C3C00F3C866 /* ConnectionProviderHandleErrorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectionProviderHandleErrorTests.swift; sourceTree = "<group>"; };
|
|
||||||
2164E65C2639AD5600385027 /* StarscreamAdapterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StarscreamAdapterTests.swift; sourceTree = "<group>"; };
|
2164E65C2639AD5600385027 /* StarscreamAdapterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StarscreamAdapterTests.swift; sourceTree = "<group>"; };
|
||||||
2164E673263C58CD00385027 /* AppSyncRealTimeClientTestBase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSyncRealTimeClientTestBase.swift; sourceTree = "<group>"; };
|
2164E673263C58CD00385027 /* AppSyncRealTimeClientTestBase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSyncRealTimeClientTestBase.swift; sourceTree = "<group>"; };
|
||||||
217C74D127C45B6600AE054F /* AppSyncSubscriptionConnectionErrorHandlerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSyncSubscriptionConnectionErrorHandlerTests.swift; sourceTree = "<group>"; };
|
|
||||||
217F398F2405D9D500F1A0B3 /* AppSyncRealTimeClient.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = AppSyncRealTimeClient.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
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 = "<group>"; };
|
217F39932405D9D500F1A0B3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
217F39982405D9D500F1A0B3 /* AppSyncRealTimeClientTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AppSyncRealTimeClientTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
217F39982405D9D500F1A0B3 /* AppSyncRealTimeClientTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AppSyncRealTimeClientTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
@ -188,11 +157,6 @@
|
||||||
217F39EB2406EA3F00F1A0B3 /* MockConnectionProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockConnectionProvider.swift; sourceTree = "<group>"; };
|
217F39EB2406EA3F00F1A0B3 /* MockConnectionProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockConnectionProvider.swift; sourceTree = "<group>"; };
|
||||||
217F39ED2406EA4000F1A0B3 /* ConnectionProviderTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConnectionProviderTests.swift; sourceTree = "<group>"; };
|
217F39ED2406EA4000F1A0B3 /* ConnectionProviderTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConnectionProviderTests.swift; sourceTree = "<group>"; };
|
||||||
217F39EF2406EA4000F1A0B3 /* RealtimeGatewayURLInterceptorTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealtimeGatewayURLInterceptorTests.swift; sourceTree = "<group>"; };
|
217F39EF2406EA4000F1A0B3 /* RealtimeGatewayURLInterceptorTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealtimeGatewayURLInterceptorTests.swift; sourceTree = "<group>"; };
|
||||||
219BFF3A27A38902000FC148 /* schema.graphql */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = schema.graphql; sourceTree = "<group>"; };
|
|
||||||
219BFF4827A3B237000FC148 /* ConnectivityMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectivityMonitor.swift; sourceTree = "<group>"; };
|
|
||||||
219BFF4A27A3B24F000FC148 /* ConnectivityPath.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectivityPath.swift; sourceTree = "<group>"; };
|
|
||||||
219BFF6927A9AC6E000FC148 /* ConnectivityMonitorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectivityMonitorTests.swift; sourceTree = "<group>"; };
|
|
||||||
219BFF6B27A9AF85000FC148 /* MockConnectivityMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockConnectivityMonitor.swift; sourceTree = "<group>"; };
|
|
||||||
21D38B3E2409AFBD00EC2A8D /* AppSyncRealTimeClientIntegrationTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AppSyncRealTimeClientIntegrationTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
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 = "<group>"; };
|
21D38B402409AFBD00EC2A8D /* AppSyncRealTimeClientIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSyncRealTimeClientIntegrationTests.swift; sourceTree = "<group>"; };
|
||||||
21D38B422409AFBD00EC2A8D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
21D38B422409AFBD00EC2A8D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
|
@ -221,29 +185,13 @@
|
||||||
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 = "<group>"; };
|
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 = "<group>"; };
|
||||||
3E662A46AB2C93EE316F784C /* Pods_AppSyncRealTimeClient_AppSyncRealTimeClientTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AppSyncRealTimeClient_AppSyncRealTimeClientTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
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; };
|
43D112B03D6D38F84995D6FA /* Pods_HostApp_AppSyncRealTimeClientIntegrationTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_HostApp_AppSyncRealTimeClientIntegrationTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
5C4E7C5D289082D3001800C1 /* OIDCAuthProviderAsync.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OIDCAuthProviderAsync.swift; sourceTree = "<group>"; };
|
|
||||||
5C5609ED2821E6FC0002ACF5 /* InterceptableConnectionAsync.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InterceptableConnectionAsync.swift; sourceTree = "<group>"; };
|
|
||||||
5C95D877289081F0008D66E9 /* OIDCAuthInterceptorAsync.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OIDCAuthInterceptorAsync.swift; sourceTree = "<group>"; };
|
|
||||||
5CF3E968283C33D40036EAD2 /* AppSyncRealTimeClientAsyncFailureTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppSyncRealTimeClientAsyncFailureTests.swift; sourceTree = "<group>"; };
|
|
||||||
5CF3E96A283C33D40036EAD2 /* AppSyncRealTimeClientAsyncIntegrationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppSyncRealTimeClientAsyncIntegrationTests.swift; sourceTree = "<group>"; };
|
|
||||||
5CFF722E283C1971001D5471 /* RealtimeConnectionProviderAsync.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealtimeConnectionProviderAsync.swift; sourceTree = "<group>"; };
|
|
||||||
5CFF722F283C1971001D5471 /* RealtimeConnectionProviderAsync+StaleConnection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "RealtimeConnectionProviderAsync+StaleConnection.swift"; sourceTree = "<group>"; };
|
|
||||||
5CFF7230283C1971001D5471 /* RealtimeConnectionProviderAsync+ConnectionInterceptableAsync.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "RealtimeConnectionProviderAsync+ConnectionInterceptableAsync.swift"; sourceTree = "<group>"; };
|
|
||||||
5CFF7231283C1971001D5471 /* RealtimeConnectionProviderAsync+Websocket.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "RealtimeConnectionProviderAsync+Websocket.swift"; sourceTree = "<group>"; };
|
|
||||||
5CFF7232283C1971001D5471 /* RealtimeConnectionProviderAsync+MessageInterceptableAsync.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "RealtimeConnectionProviderAsync+MessageInterceptableAsync.swift"; sourceTree = "<group>"; };
|
|
||||||
5CFF7238283C19CF001D5471 /* TaskQueue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TaskQueue.swift; sourceTree = "<group>"; };
|
|
||||||
5CFF723B283C1AF5001D5471 /* ConnectionProviderAsyncTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConnectionProviderAsyncTests.swift; sourceTree = "<group>"; };
|
|
||||||
5CFF723C283C1AF5001D5471 /* RealtimeConnectionProviderAsyncTestBase.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealtimeConnectionProviderAsyncTestBase.swift; sourceTree = "<group>"; };
|
|
||||||
7CF486070B34EFD15B4DB8FC /* Pods_AppSyncRTCSample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AppSyncRTCSample.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
7CF486070B34EFD15B4DB8FC /* Pods_AppSyncRTCSample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AppSyncRTCSample.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 = "<group>"; };
|
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 = "<group>"; };
|
||||||
81AFCBED5A97E398A4BD9D06 /* Pods_AppSyncRealTimeClient.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AppSyncRealTimeClient.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
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 = "<group>"; };
|
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 = "<group>"; };
|
||||||
978409B72739C7BE002362A7 /* AppSyncURLHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppSyncURLHelper.swift; sourceTree = "<group>"; };
|
|
||||||
978409B92739C7E1002362A7 /* AppSyncURLHelperTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppSyncURLHelperTests.swift; sourceTree = "<group>"; };
|
|
||||||
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 = "<group>"; };
|
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 = "<group>"; };
|
||||||
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 = "<group>"; };
|
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 = "<group>"; };
|
||||||
B4A642C2C000E0D0B568A4A9 /* Pods-AppSyncRTCSample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AppSyncRTCSample.release.xcconfig"; path = "Target Support Files/Pods-AppSyncRTCSample/Pods-AppSyncRTCSample.release.xcconfig"; sourceTree = "<group>"; };
|
B4A642C2C000E0D0B568A4A9 /* Pods-AppSyncRTCSample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AppSyncRTCSample.release.xcconfig"; path = "Target Support Files/Pods-AppSyncRTCSample/Pods-AppSyncRTCSample.release.xcconfig"; sourceTree = "<group>"; };
|
||||||
B4AEC68B29E8A14F00D693CD /* InterceptableConnection+Default.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "InterceptableConnection+Default.swift"; sourceTree = "<group>"; };
|
|
||||||
BBF14AA5E9A4D036CA87B952 /* Pods-AppSyncRTCSample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AppSyncRTCSample.debug.xcconfig"; path = "Target Support Files/Pods-AppSyncRTCSample/Pods-AppSyncRTCSample.debug.xcconfig"; sourceTree = "<group>"; };
|
BBF14AA5E9A4D036CA87B952 /* Pods-AppSyncRTCSample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AppSyncRTCSample.debug.xcconfig"; path = "Target Support Files/Pods-AppSyncRTCSample/Pods-AppSyncRTCSample.debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
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 = "<group>"; };
|
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 = "<group>"; };
|
||||||
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 = "<group>"; };
|
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 = "<group>"; };
|
||||||
|
@ -311,14 +259,6 @@
|
||||||
/* End PBXFrameworksBuildPhase section */
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
/* Begin PBXGroup section */
|
||||||
2143D46527B5D9730066B2F7 /* AppSyncRealTimeConnection */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
2143D46627B5D9B40066B2F7 /* RealTimeConnectionProviderResponseTests.swift */,
|
|
||||||
);
|
|
||||||
path = AppSyncRealTimeConnection;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
217F39852405D9D500F1A0B3 = {
|
217F39852405D9D500F1A0B3 = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
@ -348,11 +288,9 @@
|
||||||
217F39912405D9D500F1A0B3 /* AppSyncRealTimeClient */ = {
|
217F39912405D9D500F1A0B3 /* AppSyncRealTimeClient */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
2143D4AF27BC49BE0066B2F7 /* AWSAppSyncRealTimeClient.swift */,
|
217F39932405D9D500F1A0B3 /* Info.plist */,
|
||||||
217F39B82406E98300F1A0B3 /* Connection */,
|
217F39B82406E98300F1A0B3 /* Connection */,
|
||||||
217F39A92406E98300F1A0B3 /* ConnectionProvider */,
|
217F39A92406E98300F1A0B3 /* ConnectionProvider */,
|
||||||
219BFF4727A3B223000FC148 /* ConnectivityMonitor */,
|
|
||||||
217F39932405D9D500F1A0B3 /* Info.plist */,
|
|
||||||
21D38B6E240A272F00EC2A8D /* Interceptor */,
|
21D38B6E240A272F00EC2A8D /* Interceptor */,
|
||||||
217F39C62406E98400F1A0B3 /* Support */,
|
217F39C62406E98400F1A0B3 /* Support */,
|
||||||
217F39C12406E98400F1A0B3 /* Websocket */,
|
217F39C12406E98400F1A0B3 /* Websocket */,
|
||||||
|
@ -365,7 +303,6 @@
|
||||||
children = (
|
children = (
|
||||||
217F39E82406EA3F00F1A0B3 /* Connection */,
|
217F39E82406EA3F00F1A0B3 /* Connection */,
|
||||||
217F39EC2406EA4000F1A0B3 /* ConnectionProvider */,
|
217F39EC2406EA4000F1A0B3 /* ConnectionProvider */,
|
||||||
219BFF6827A9AC4F000FC148 /* ConnectivityMonitor */,
|
|
||||||
217F399F2405D9D500F1A0B3 /* Info.plist */,
|
217F399F2405D9D500F1A0B3 /* Info.plist */,
|
||||||
21D38B84240A39E400EC2A8D /* Interceptor */,
|
21D38B84240A39E400EC2A8D /* Interceptor */,
|
||||||
217F39EA2406EA3F00F1A0B3 /* Mocks */,
|
217F39EA2406EA3F00F1A0B3 /* Mocks */,
|
||||||
|
@ -381,15 +318,12 @@
|
||||||
217F39AF2406E98300F1A0B3 /* AppSyncConnectionRequest.swift */,
|
217F39AF2406E98300F1A0B3 /* AppSyncConnectionRequest.swift */,
|
||||||
217F39AE2406E98300F1A0B3 /* AppSyncMessage.swift */,
|
217F39AE2406E98300F1A0B3 /* AppSyncMessage.swift */,
|
||||||
217F39AD2406E98300F1A0B3 /* AppSyncMessage+Encodable.swift */,
|
217F39AD2406E98300F1A0B3 /* AppSyncMessage+Encodable.swift */,
|
||||||
217F39B02406E98300F1A0B3 /* AppsyncRealtimeConnection */,
|
|
||||||
5CFF722D283C1971001D5471 /* AppsyncRealtimeConnectionAsync */,
|
|
||||||
217F39AA2406E98300F1A0B3 /* AppSyncResponse.swift */,
|
217F39AA2406E98300F1A0B3 /* AppSyncResponse.swift */,
|
||||||
217F39B72406E98300F1A0B3 /* ConnectionProvider.swift */,
|
217F39B72406E98300F1A0B3 /* ConnectionProvider.swift */,
|
||||||
217F39AC2406E98300F1A0B3 /* ConnectionProviderError.swift */,
|
217F39AC2406E98300F1A0B3 /* ConnectionProviderError.swift */,
|
||||||
21D38B8D240A3C2300EC2A8D /* ConnectionProviderFactory.swift */,
|
21D38B8D240A3C2300EC2A8D /* ConnectionProviderFactory.swift */,
|
||||||
217F39AB2406E98300F1A0B3 /* InterceptableConnection.swift */,
|
217F39AB2406E98300F1A0B3 /* InterceptableConnection.swift */,
|
||||||
B4AEC68B29E8A14F00D693CD /* InterceptableConnection+Default.swift */,
|
217F39B02406E98300F1A0B3 /* AppsyncRealtimeConnection */,
|
||||||
5C5609ED2821E6FC0002ACF5 /* InterceptableConnectionAsync.swift */,
|
|
||||||
);
|
);
|
||||||
path = ConnectionProvider;
|
path = ConnectionProvider;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -453,14 +387,11 @@
|
||||||
21D38B6C240A262800EC2A8D /* AppSyncJSONHelper.swift */,
|
21D38B6C240A262800EC2A8D /* AppSyncJSONHelper.swift */,
|
||||||
217F39C92406E98400F1A0B3 /* AppSyncJSONValue.swift */,
|
217F39C92406E98400F1A0B3 /* AppSyncJSONValue.swift */,
|
||||||
217F39C72406E98400F1A0B3 /* AppSyncLogger.swift */,
|
217F39C72406E98400F1A0B3 /* AppSyncLogger.swift */,
|
||||||
978409B72739C7BE002362A7 /* AppSyncURLHelper.swift */,
|
|
||||||
FA2EFABB2550CAC6007698C7 /* AtomicValue.swift */,
|
FA2EFABB2550CAC6007698C7 /* AtomicValue.swift */,
|
||||||
FA67508124D33A7A005A1345 /* CountdownTimer.swift */,
|
FA67508124D33A7A005A1345 /* CountdownTimer.swift */,
|
||||||
21D38B93240C4A2A00EC2A8D /* OIDCAuthProvider.swift */,
|
21D38B93240C4A2A00EC2A8D /* OIDCAuthProvider.swift */,
|
||||||
5C4E7C5D289082D3001800C1 /* OIDCAuthProviderAsync.swift */,
|
|
||||||
217F39CB2406E98400F1A0B3 /* SubscriptionConnectionType.swift */,
|
217F39CB2406E98400F1A0B3 /* SubscriptionConnectionType.swift */,
|
||||||
217F39CA2406E98400F1A0B3 /* SubscriptionConstants.swift */,
|
217F39CA2406E98400F1A0B3 /* SubscriptionConstants.swift */,
|
||||||
5CFF7238283C19CF001D5471 /* TaskQueue.swift */,
|
|
||||||
);
|
);
|
||||||
path = Support;
|
path = Support;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -468,7 +399,6 @@
|
||||||
217F39E82406EA3F00F1A0B3 /* Connection */ = {
|
217F39E82406EA3F00F1A0B3 /* Connection */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
217C74D127C45B6600AE054F /* AppSyncSubscriptionConnectionErrorHandlerTests.swift */,
|
|
||||||
217F39E92406EA3F00F1A0B3 /* AppSyncSubscriptionConnectionTests.swift */,
|
217F39E92406EA3F00F1A0B3 /* AppSyncSubscriptionConnectionTests.swift */,
|
||||||
);
|
);
|
||||||
path = Connection;
|
path = Connection;
|
||||||
|
@ -479,7 +409,6 @@
|
||||||
children = (
|
children = (
|
||||||
217F39EB2406EA3F00F1A0B3 /* MockConnectionProvider.swift */,
|
217F39EB2406EA3F00F1A0B3 /* MockConnectionProvider.swift */,
|
||||||
FA67507724D3244A005A1345 /* MockWebsocketProvider.swift */,
|
FA67507724D3244A005A1345 /* MockWebsocketProvider.swift */,
|
||||||
219BFF6B27A9AF85000FC148 /* MockConnectivityMonitor.swift */,
|
|
||||||
);
|
);
|
||||||
path = Mocks;
|
path = Mocks;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -487,9 +416,6 @@
|
||||||
217F39EC2406EA4000F1A0B3 /* ConnectionProvider */ = {
|
217F39EC2406EA4000F1A0B3 /* ConnectionProvider */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
5CFF723A283C1AA0001D5471 /* ConnectionProviderAsync */,
|
|
||||||
2143D46527B5D9730066B2F7 /* AppSyncRealTimeConnection */,
|
|
||||||
2151D5C927C68C3C00F3C866 /* ConnectionProviderHandleErrorTests.swift */,
|
|
||||||
FA67507F24D339B0005A1345 /* ConnectionProviderStaleConnectionTests.swift */,
|
FA67507F24D339B0005A1345 /* ConnectionProviderStaleConnectionTests.swift */,
|
||||||
217F39ED2406EA4000F1A0B3 /* ConnectionProviderTests.swift */,
|
217F39ED2406EA4000F1A0B3 /* ConnectionProviderTests.swift */,
|
||||||
FA67507C24D338FA005A1345 /* RealtimeConnectionProviderTestBase.swift */,
|
FA67507C24D338FA005A1345 /* RealtimeConnectionProviderTestBase.swift */,
|
||||||
|
@ -506,29 +432,10 @@
|
||||||
path = Support;
|
path = Support;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
219BFF4727A3B223000FC148 /* ConnectivityMonitor */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
219BFF4827A3B237000FC148 /* ConnectivityMonitor.swift */,
|
|
||||||
219BFF4A27A3B24F000FC148 /* ConnectivityPath.swift */,
|
|
||||||
);
|
|
||||||
path = ConnectivityMonitor;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
219BFF6827A9AC4F000FC148 /* ConnectivityMonitor */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
219BFF6927A9AC6E000FC148 /* ConnectivityMonitorTests.swift */,
|
|
||||||
);
|
|
||||||
path = ConnectivityMonitor;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
21D38B3F2409AFBD00EC2A8D /* AppSyncRealTimeClientIntegrationTests */ = {
|
21D38B3F2409AFBD00EC2A8D /* AppSyncRealTimeClientIntegrationTests */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
5CF3E967283C33D40036EAD2 /* AppSyncRealTimeClientAsync */,
|
|
||||||
21D38B4B2409B6C000EC2A8D /* amplifyconfiguration.json */,
|
21D38B4B2409B6C000EC2A8D /* amplifyconfiguration.json */,
|
||||||
2143D4B127BE40B30066B2F7 /* AppSyncRealTimeClientFailureTests.swift */,
|
|
||||||
21D38B402409AFBD00EC2A8D /* AppSyncRealTimeClientIntegrationTests.swift */,
|
21D38B402409AFBD00EC2A8D /* AppSyncRealTimeClientIntegrationTests.swift */,
|
||||||
2164E673263C58CD00385027 /* AppSyncRealTimeClientTestBase.swift */,
|
2164E673263C58CD00385027 /* AppSyncRealTimeClientTestBase.swift */,
|
||||||
21D38B422409AFBD00EC2A8D /* Info.plist */,
|
21D38B422409AFBD00EC2A8D /* Info.plist */,
|
||||||
|
@ -566,7 +473,6 @@
|
||||||
children = (
|
children = (
|
||||||
21D38B82240A392B00EC2A8D /* APIKeyAuthInterceptor.swift */,
|
21D38B82240A392B00EC2A8D /* APIKeyAuthInterceptor.swift */,
|
||||||
21D38B78240A2A1300EC2A8D /* OIDCAuthInterceptor.swift */,
|
21D38B78240A2A1300EC2A8D /* OIDCAuthInterceptor.swift */,
|
||||||
5C95D877289081F0008D66E9 /* OIDCAuthInterceptorAsync.swift */,
|
|
||||||
217F39C82406E98400F1A0B3 /* RealtimeGatewayURLInterceptor.swift */,
|
217F39C82406E98400F1A0B3 /* RealtimeGatewayURLInterceptor.swift */,
|
||||||
);
|
);
|
||||||
path = Interceptor;
|
path = Interceptor;
|
||||||
|
@ -577,7 +483,6 @@
|
||||||
children = (
|
children = (
|
||||||
21D38B88240A39E400EC2A8D /* APIKeyAuthInterceptorTests.swift */,
|
21D38B88240A39E400EC2A8D /* APIKeyAuthInterceptorTests.swift */,
|
||||||
21D38B87240A39E400EC2A8D /* AppSyncJSONHelperTests.swift */,
|
21D38B87240A39E400EC2A8D /* AppSyncJSONHelperTests.swift */,
|
||||||
978409B92739C7E1002362A7 /* AppSyncURLHelperTests.swift */,
|
|
||||||
21D38B85240A39E400EC2A8D /* OIDCAuthInterceptorTests.swift */,
|
21D38B85240A39E400EC2A8D /* OIDCAuthInterceptorTests.swift */,
|
||||||
);
|
);
|
||||||
path = Interceptor;
|
path = Interceptor;
|
||||||
|
@ -586,7 +491,6 @@
|
||||||
21D38B95240C4DC200EC2A8D /* Support */ = {
|
21D38B95240C4DC200EC2A8D /* Support */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
219BFF3A27A38902000FC148 /* schema.graphql */,
|
|
||||||
21D38B98240C4E1C00EC2A8D /* ConfigurationHelper.swift */,
|
21D38B98240C4E1C00EC2A8D /* ConfigurationHelper.swift */,
|
||||||
21D38B96240C4DCF00EC2A8D /* Error+Extension.swift */,
|
21D38B96240C4DCF00EC2A8D /* Error+Extension.swift */,
|
||||||
21D38B9C240C540D00EC2A8D /* TestCommonConstants.swift */,
|
21D38B9C240C540D00EC2A8D /* TestCommonConstants.swift */,
|
||||||
|
@ -594,36 +498,6 @@
|
||||||
path = Support;
|
path = Support;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
5CF3E967283C33D40036EAD2 /* AppSyncRealTimeClientAsync */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
5CF3E968283C33D40036EAD2 /* AppSyncRealTimeClientAsyncFailureTests.swift */,
|
|
||||||
5CF3E96A283C33D40036EAD2 /* AppSyncRealTimeClientAsyncIntegrationTests.swift */,
|
|
||||||
);
|
|
||||||
path = AppSyncRealTimeClientAsync;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
5CFF722D283C1971001D5471 /* AppsyncRealtimeConnectionAsync */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
5CFF722E283C1971001D5471 /* RealtimeConnectionProviderAsync.swift */,
|
|
||||||
5CFF7230283C1971001D5471 /* RealtimeConnectionProviderAsync+ConnectionInterceptableAsync.swift */,
|
|
||||||
5CFF7232283C1971001D5471 /* RealtimeConnectionProviderAsync+MessageInterceptableAsync.swift */,
|
|
||||||
5CFF722F283C1971001D5471 /* RealtimeConnectionProviderAsync+StaleConnection.swift */,
|
|
||||||
5CFF7231283C1971001D5471 /* RealtimeConnectionProviderAsync+Websocket.swift */,
|
|
||||||
);
|
|
||||||
path = AppsyncRealtimeConnectionAsync;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
5CFF723A283C1AA0001D5471 /* ConnectionProviderAsync */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
5CFF723B283C1AF5001D5471 /* ConnectionProviderAsyncTests.swift */,
|
|
||||||
5CFF723C283C1AF5001D5471 /* RealtimeConnectionProviderAsyncTestBase.swift */,
|
|
||||||
);
|
|
||||||
path = ConnectionProviderAsync;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
CC47BA7DC6033B1626BAD959 /* Frameworks */ = {
|
CC47BA7DC6033B1626BAD959 /* Frameworks */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
@ -873,7 +747,6 @@
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
21D38B4E2409B8B200EC2A8D /* README.md in Resources */,
|
21D38B4E2409B8B200EC2A8D /* README.md in Resources */,
|
||||||
219BFF3B27A38902000FC148 /* schema.graphql in Resources */,
|
|
||||||
21D38B4C2409B6C000EC2A8D /* amplifyconfiguration.json in Resources */,
|
21D38B4C2409B6C000EC2A8D /* amplifyconfiguration.json in Resources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
@ -1066,7 +939,6 @@
|
||||||
};
|
};
|
||||||
FA91B21124D308330017404D /* SwiftFormat */ = {
|
FA91B21124D308330017404D /* SwiftFormat */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
alwaysOutOfDate = 1;
|
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
);
|
);
|
||||||
|
@ -1085,7 +957,6 @@
|
||||||
};
|
};
|
||||||
FA91B21224D308550017404D /* SwiftLint */ = {
|
FA91B21224D308550017404D /* SwiftLint */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
alwaysOutOfDate = 1;
|
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
);
|
);
|
||||||
|
@ -1104,7 +975,6 @@
|
||||||
};
|
};
|
||||||
FA91B21324D308800017404D /* SwiftFormat */ = {
|
FA91B21324D308800017404D /* SwiftFormat */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
alwaysOutOfDate = 1;
|
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
);
|
);
|
||||||
|
@ -1123,7 +993,6 @@
|
||||||
};
|
};
|
||||||
FA91B21424D308B00017404D /* SwiftLint */ = {
|
FA91B21424D308B00017404D /* SwiftLint */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
alwaysOutOfDate = 1;
|
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
);
|
);
|
||||||
|
@ -1142,7 +1011,6 @@
|
||||||
};
|
};
|
||||||
FA91B21524D308C70017404D /* SwiftFormat */ = {
|
FA91B21524D308C70017404D /* SwiftFormat */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
alwaysOutOfDate = 1;
|
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
);
|
);
|
||||||
|
@ -1161,7 +1029,6 @@
|
||||||
};
|
};
|
||||||
FA91B21624D308D90017404D /* SwiftLint */ = {
|
FA91B21624D308D90017404D /* SwiftLint */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
alwaysOutOfDate = 1;
|
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
);
|
);
|
||||||
|
@ -1222,7 +1089,6 @@
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
21D38B7C240A2A1300EC2A8D /* OIDCAuthInterceptor.swift in Sources */,
|
21D38B7C240A2A1300EC2A8D /* OIDCAuthInterceptor.swift in Sources */,
|
||||||
5CFF7235283C1971001D5471 /* RealtimeConnectionProviderAsync+ConnectionInterceptableAsync.swift in Sources */,
|
|
||||||
217F39DB2406E98400F1A0B3 /* AppSyncSubscriptionConnection+DataHandler.swift in Sources */,
|
217F39DB2406E98400F1A0B3 /* AppSyncSubscriptionConnection+DataHandler.swift in Sources */,
|
||||||
21D38B94240C4A2A00EC2A8D /* OIDCAuthProvider.swift in Sources */,
|
21D38B94240C4A2A00EC2A8D /* OIDCAuthProvider.swift in Sources */,
|
||||||
217F39DF2406E98400F1A0B3 /* SubscriptionItem.swift in Sources */,
|
217F39DF2406E98400F1A0B3 /* SubscriptionItem.swift in Sources */,
|
||||||
|
@ -1231,45 +1097,32 @@
|
||||||
217F39CD2406E98400F1A0B3 /* InterceptableConnection.swift in Sources */,
|
217F39CD2406E98400F1A0B3 /* InterceptableConnection.swift in Sources */,
|
||||||
21D38B8E240A3C2300EC2A8D /* ConnectionProviderFactory.swift in Sources */,
|
21D38B8E240A3C2300EC2A8D /* ConnectionProviderFactory.swift in Sources */,
|
||||||
217F39E02406E98400F1A0B3 /* AppSyncWebsocketProvider.swift in Sources */,
|
217F39E02406E98400F1A0B3 /* AppSyncWebsocketProvider.swift in Sources */,
|
||||||
219BFF4927A3B238000FC148 /* ConnectivityMonitor.swift in Sources */,
|
|
||||||
FAB7E91224D2644E00DF1EA1 /* RealtimeConnectionProvider+StaleConnection.swift in Sources */,
|
FAB7E91224D2644E00DF1EA1 /* RealtimeConnectionProvider+StaleConnection.swift in Sources */,
|
||||||
217F39D32406E98400F1A0B3 /* RealtimeConnectionProvider.swift in Sources */,
|
217F39D32406E98400F1A0B3 /* RealtimeConnectionProvider.swift in Sources */,
|
||||||
B4AEC68C29E8A14F00D693CD /* InterceptableConnection+Default.swift in Sources */,
|
|
||||||
217F39D12406E98400F1A0B3 /* AppSyncConnectionRequest.swift in Sources */,
|
217F39D12406E98400F1A0B3 /* AppSyncConnectionRequest.swift in Sources */,
|
||||||
5C5609EE2821E6FC0002ACF5 /* InterceptableConnectionAsync.swift in Sources */,
|
|
||||||
217F39DD2406E98400F1A0B3 /* AppSyncSubscriptionConnection+Connection.swift in Sources */,
|
217F39DD2406E98400F1A0B3 /* AppSyncSubscriptionConnection+Connection.swift in Sources */,
|
||||||
217F39E52406E98400F1A0B3 /* AppSyncJSONValue.swift in Sources */,
|
217F39E52406E98400F1A0B3 /* AppSyncJSONValue.swift in Sources */,
|
||||||
217F39E42406E98400F1A0B3 /* RealtimeGatewayURLInterceptor.swift in Sources */,
|
217F39E42406E98400F1A0B3 /* RealtimeGatewayURLInterceptor.swift in Sources */,
|
||||||
2143D4B027BC49BE0066B2F7 /* AWSAppSyncRealTimeClient.swift in Sources */,
|
|
||||||
217F39E22406E98400F1A0B3 /* StarscreamAdapter+Delegate.swift in Sources */,
|
217F39E22406E98400F1A0B3 /* StarscreamAdapter+Delegate.swift in Sources */,
|
||||||
217F39D52406E98400F1A0B3 /* RealtimeConnectionProviderResponse.swift in Sources */,
|
217F39D52406E98400F1A0B3 /* RealtimeConnectionProviderResponse.swift in Sources */,
|
||||||
217F39D62406E98400F1A0B3 /* RealtimeConnectionProvider+MessageInterceptable.swift in Sources */,
|
217F39D62406E98400F1A0B3 /* RealtimeConnectionProvider+MessageInterceptable.swift in Sources */,
|
||||||
5CFF7233283C1971001D5471 /* RealtimeConnectionProviderAsync.swift in Sources */,
|
|
||||||
5C95D878289081F0008D66E9 /* OIDCAuthInterceptorAsync.swift in Sources */,
|
|
||||||
FA67508224D33A7A005A1345 /* CountdownTimer.swift in Sources */,
|
FA67508224D33A7A005A1345 /* CountdownTimer.swift in Sources */,
|
||||||
217F39E62406E98400F1A0B3 /* SubscriptionConstants.swift in Sources */,
|
217F39E62406E98400F1A0B3 /* SubscriptionConstants.swift in Sources */,
|
||||||
5CFF7234283C1971001D5471 /* RealtimeConnectionProviderAsync+StaleConnection.swift in Sources */,
|
|
||||||
217F39D02406E98400F1A0B3 /* AppSyncMessage.swift in Sources */,
|
217F39D02406E98400F1A0B3 /* AppSyncMessage.swift in Sources */,
|
||||||
217F39E32406E98400F1A0B3 /* AppSyncLogger.swift in Sources */,
|
217F39E32406E98400F1A0B3 /* AppSyncLogger.swift in Sources */,
|
||||||
217F39D82406E98400F1A0B3 /* ConnectionProvider.swift in Sources */,
|
217F39D82406E98400F1A0B3 /* ConnectionProvider.swift in Sources */,
|
||||||
21D38B83240A392B00EC2A8D /* APIKeyAuthInterceptor.swift in Sources */,
|
21D38B83240A392B00EC2A8D /* APIKeyAuthInterceptor.swift in Sources */,
|
||||||
217F39D92406E98400F1A0B3 /* SubscriptionConnection.swift in Sources */,
|
217F39D92406E98400F1A0B3 /* SubscriptionConnection.swift in Sources */,
|
||||||
5C4E7C5E289082D3001800C1 /* OIDCAuthProviderAsync.swift in Sources */,
|
|
||||||
217F39DA2406E98400F1A0B3 /* RetryableConnection.swift in Sources */,
|
217F39DA2406E98400F1A0B3 /* RetryableConnection.swift in Sources */,
|
||||||
978409B82739C7BE002362A7 /* AppSyncURLHelper.swift in Sources */,
|
|
||||||
217F39CC2406E98400F1A0B3 /* AppSyncResponse.swift in Sources */,
|
217F39CC2406E98400F1A0B3 /* AppSyncResponse.swift in Sources */,
|
||||||
5CFF7239283C19CF001D5471 /* TaskQueue.swift in Sources */,
|
|
||||||
217F39E72406E98400F1A0B3 /* SubscriptionConnectionType.swift in Sources */,
|
217F39E72406E98400F1A0B3 /* SubscriptionConnectionType.swift in Sources */,
|
||||||
217F39D42406E98400F1A0B3 /* RealtimeConnectionProvider+Websocket.swift in Sources */,
|
217F39D42406E98400F1A0B3 /* RealtimeConnectionProvider+Websocket.swift in Sources */,
|
||||||
217F39DC2406E98400F1A0B3 /* AppSyncSubscriptionConnection.swift in Sources */,
|
217F39DC2406E98400F1A0B3 /* AppSyncSubscriptionConnection.swift in Sources */,
|
||||||
5CFF7236283C1971001D5471 /* RealtimeConnectionProviderAsync+Websocket.swift in Sources */,
|
|
||||||
21D38B6D240A262800EC2A8D /* AppSyncJSONHelper.swift in Sources */,
|
21D38B6D240A262800EC2A8D /* AppSyncJSONHelper.swift in Sources */,
|
||||||
219BFF4B27A3B24F000FC148 /* ConnectivityPath.swift in Sources */,
|
|
||||||
217F39CE2406E98400F1A0B3 /* ConnectionProviderError.swift in Sources */,
|
217F39CE2406E98400F1A0B3 /* ConnectionProviderError.swift in Sources */,
|
||||||
217F39E12406E98400F1A0B3 /* StarscreamAdapter.swift in Sources */,
|
217F39E12406E98400F1A0B3 /* StarscreamAdapter.swift in Sources */,
|
||||||
217F39D72406E98400F1A0B3 /* RealtimeConnectionProvider+ConnectionInterceptable.swift in Sources */,
|
217F39D72406E98400F1A0B3 /* RealtimeConnectionProvider+ConnectionInterceptable.swift in Sources */,
|
||||||
217F39DE2406E98400F1A0B3 /* AppSyncSubscriptionConnection+ErrorHandler.swift in Sources */,
|
217F39DE2406E98400F1A0B3 /* AppSyncSubscriptionConnection+ErrorHandler.swift in Sources */,
|
||||||
5CFF7237283C1971001D5471 /* RealtimeConnectionProviderAsync+MessageInterceptableAsync.swift in Sources */,
|
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
@ -1278,22 +1131,14 @@
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
21D38B89240A39E400EC2A8D /* OIDCAuthInterceptorTests.swift in Sources */,
|
21D38B89240A39E400EC2A8D /* OIDCAuthInterceptorTests.swift in Sources */,
|
||||||
219BFF6C27A9AF85000FC148 /* MockConnectivityMonitor.swift in Sources */,
|
|
||||||
217F39F22406EA4000F1A0B3 /* ConnectionProviderTests.swift in Sources */,
|
217F39F22406EA4000F1A0B3 /* ConnectionProviderTests.swift in Sources */,
|
||||||
2143D46727B5D9B40066B2F7 /* RealTimeConnectionProviderResponseTests.swift in Sources */,
|
|
||||||
FA67507824D3244A005A1345 /* MockWebsocketProvider.swift in Sources */,
|
FA67507824D3244A005A1345 /* MockWebsocketProvider.swift in Sources */,
|
||||||
217F39F02406EA4000F1A0B3 /* AppSyncSubscriptionConnectionTests.swift in Sources */,
|
217F39F02406EA4000F1A0B3 /* AppSyncSubscriptionConnectionTests.swift in Sources */,
|
||||||
FA67507B24D338CB005A1345 /* Error+Extension.swift in Sources */,
|
FA67507B24D338CB005A1345 /* Error+Extension.swift in Sources */,
|
||||||
21D38B8C240A39E400EC2A8D /* APIKeyAuthInterceptorTests.swift in Sources */,
|
21D38B8C240A39E400EC2A8D /* APIKeyAuthInterceptorTests.swift in Sources */,
|
||||||
5CFF723E283C1AF5001D5471 /* RealtimeConnectionProviderAsyncTestBase.swift in Sources */,
|
|
||||||
5CFF723D283C1AF5001D5471 /* ConnectionProviderAsyncTests.swift in Sources */,
|
|
||||||
21D38B8B240A39E400EC2A8D /* AppSyncJSONHelperTests.swift in Sources */,
|
21D38B8B240A39E400EC2A8D /* AppSyncJSONHelperTests.swift in Sources */,
|
||||||
217F39F32406EA4000F1A0B3 /* RealtimeGatewayURLInterceptorTests.swift in Sources */,
|
217F39F32406EA4000F1A0B3 /* RealtimeGatewayURLInterceptorTests.swift in Sources */,
|
||||||
217C74D227C45B6600AE054F /* AppSyncSubscriptionConnectionErrorHandlerTests.swift in Sources */,
|
|
||||||
FA67507E24D33976005A1345 /* RealtimeConnectionProviderTestBase.swift in Sources */,
|
FA67507E24D33976005A1345 /* RealtimeConnectionProviderTestBase.swift in Sources */,
|
||||||
219BFF6A27A9AC6E000FC148 /* ConnectivityMonitorTests.swift in Sources */,
|
|
||||||
2151D5CA27C68C3C00F3C866 /* ConnectionProviderHandleErrorTests.swift in Sources */,
|
|
||||||
978409BA2739C7E1002362A7 /* AppSyncURLHelperTests.swift in Sources */,
|
|
||||||
FA67508424D33ACC005A1345 /* CountdownTimerTests.swift in Sources */,
|
FA67508424D33ACC005A1345 /* CountdownTimerTests.swift in Sources */,
|
||||||
FA67508024D339B0005A1345 /* ConnectionProviderStaleConnectionTests.swift in Sources */,
|
FA67508024D339B0005A1345 /* ConnectionProviderStaleConnectionTests.swift in Sources */,
|
||||||
217F39F12406EA4000F1A0B3 /* MockConnectionProvider.swift in Sources */,
|
217F39F12406EA4000F1A0B3 /* MockConnectionProvider.swift in Sources */,
|
||||||
|
@ -1305,12 +1150,9 @@
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
21D38B99240C4E1C00EC2A8D /* ConfigurationHelper.swift in Sources */,
|
21D38B99240C4E1C00EC2A8D /* ConfigurationHelper.swift in Sources */,
|
||||||
5CF3E96D283C33D40036EAD2 /* AppSyncRealTimeClientAsyncIntegrationTests.swift in Sources */,
|
|
||||||
2164E674263C58CE00385027 /* AppSyncRealTimeClientTestBase.swift in Sources */,
|
2164E674263C58CE00385027 /* AppSyncRealTimeClientTestBase.swift in Sources */,
|
||||||
21D38B97240C4DCF00EC2A8D /* Error+Extension.swift in Sources */,
|
21D38B97240C4DCF00EC2A8D /* Error+Extension.swift in Sources */,
|
||||||
2143D4B227BE40B30066B2F7 /* AppSyncRealTimeClientFailureTests.swift in Sources */,
|
|
||||||
21D38B9D240C540D00EC2A8D /* TestCommonConstants.swift in Sources */,
|
21D38B9D240C540D00EC2A8D /* TestCommonConstants.swift in Sources */,
|
||||||
5CF3E96B283C33D40036EAD2 /* AppSyncRealTimeClientAsyncFailureTests.swift in Sources */,
|
|
||||||
21D38B412409AFBD00EC2A8D /* AppSyncRealTimeClientIntegrationTests.swift in Sources */,
|
21D38B412409AFBD00EC2A8D /* AppSyncRealTimeClientIntegrationTests.swift in Sources */,
|
||||||
2164E65D2639AD5600385027 /* StarscreamAdapterTests.swift in Sources */,
|
2164E65D2639AD5600385027 /* StarscreamAdapterTests.swift in Sources */,
|
||||||
);
|
);
|
||||||
|
@ -1433,7 +1275,7 @@
|
||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 13.2;
|
||||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||||
MTL_FAST_MATH = YES;
|
MTL_FAST_MATH = YES;
|
||||||
ONLY_ACTIVE_ARCH = YES;
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
|
@ -1490,7 +1332,7 @@
|
||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 13.2;
|
||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
MTL_FAST_MATH = YES;
|
MTL_FAST_MATH = YES;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
|
|
|
@ -45,7 +45,6 @@
|
||||||
buildConfiguration = "Debug"
|
buildConfiguration = "Debug"
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
enableThreadSanitizer = "YES"
|
|
||||||
launchStyle = "0"
|
launchStyle = "0"
|
||||||
useCustomWorkingDirectory = "NO"
|
useCustomWorkingDirectory = "NO"
|
||||||
ignoresPersistentStateOnLaunch = "NO"
|
ignoresPersistentStateOnLaunch = "NO"
|
||||||
|
|
|
@ -1,44 +0,0 @@
|
||||||
//
|
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
|
||||||
// All Rights Reserved.
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
public enum AppSyncRealTimeClient {
|
|
||||||
|
|
||||||
static let lock: NSLocking = NSLock()
|
|
||||||
|
|
||||||
static var _logLevel = LogLevel.error // swiftlint:disable:this identifier_name
|
|
||||||
|
|
||||||
public static var logLevel: LogLevel {
|
|
||||||
get {
|
|
||||||
lock.lock()
|
|
||||||
defer {
|
|
||||||
lock.unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
return _logLevel
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
lock.lock()
|
|
||||||
defer {
|
|
||||||
lock.unlock()
|
|
||||||
}
|
|
||||||
|
|
||||||
_logLevel = newValue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public extension AppSyncRealTimeClient {
|
|
||||||
enum LogLevel: Int {
|
|
||||||
case error
|
|
||||||
case warn
|
|
||||||
case info
|
|
||||||
case debug
|
|
||||||
case verbose
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2021 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
@ -18,6 +18,7 @@ extension AppSyncSubscriptionConnection {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if connectionState == .connected {
|
if connectionState == .connected {
|
||||||
|
AppSyncLogger.debug("[AppSyncSubscriptionConnection] \(#function): connection is connected, start subscription.")
|
||||||
startSubscription()
|
startSubscription()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,9 +32,7 @@ extension AppSyncSubscriptionConnection {
|
||||||
else {
|
else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
AppSyncLogger.debug(
|
|
||||||
"[AppSyncSubscriptionConnection]: Connection connected, start subscription \(subscriptionItem.identifier)."
|
|
||||||
)
|
|
||||||
subscriptionState = .inProgress
|
subscriptionState = .inProgress
|
||||||
|
|
||||||
guard let payload = convertToPayload(
|
guard let payload = convertToPayload(
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2021 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
@ -16,6 +16,9 @@ extension AppSyncSubscriptionConnection {
|
||||||
}
|
}
|
||||||
|
|
||||||
guard response.id == subscriptionItem.identifier else {
|
guard response.id == subscriptionItem.identifier else {
|
||||||
|
AppSyncLogger.verbose("""
|
||||||
|
[AppSyncSubscriptionConnection] \(#function): \(subscriptionItem.identifier). Ignoring data event for \(response.id ?? "(null)")
|
||||||
|
""")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2021 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
@ -15,35 +15,13 @@ extension AppSyncSubscriptionConnection {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the error identifier is not for the this subscription
|
// If the error identifier is not for the this connection
|
||||||
// we return immediately without handling the error.
|
// we return immediately without handling the error.
|
||||||
if case let ConnectionProviderError.subscription(identifier, _) = error,
|
if case let ConnectionProviderError.subscription(identifier, _) = error,
|
||||||
identifier != subscriptionItem.identifier {
|
identifier != subscriptionItem.identifier {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if case let ConnectionProviderError.limitExceeded(identifier) = error {
|
|
||||||
// If the error identifier is not for the this subscription
|
|
||||||
// we return immediately without handling the error.
|
|
||||||
if let identifier = identifier, identifier != subscriptionItem.identifier {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Limit exceeded without an subscription identifier is an error for the entire connection
|
|
||||||
// that can be caused by multiple subscriptions trying to subscribe at the same time.
|
|
||||||
// Return the error on those subscriptions in-progress, and return immediately.
|
|
||||||
if identifier == nil {
|
|
||||||
if subscriptionState == .inProgress {
|
|
||||||
subscriptionState = .notSubscribed
|
|
||||||
AppSyncSubscriptionConnection.logExtendedErrorInfo(for: error)
|
|
||||||
subscriptionItem.subscriptionEventHandler(.failed(error), subscriptionItem)
|
|
||||||
connectionProvider?.removeListener(identifier: subscriptionItem.identifier)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AppSyncSubscriptionConnection.logExtendedErrorInfo(for: error)
|
AppSyncSubscriptionConnection.logExtendedErrorInfo(for: error)
|
||||||
|
|
||||||
subscriptionState = .notSubscribed
|
subscriptionState = .notSubscribed
|
||||||
|
@ -51,20 +29,17 @@ extension AppSyncSubscriptionConnection {
|
||||||
let connectionError = error as? ConnectionProviderError
|
let connectionError = error as? ConnectionProviderError
|
||||||
else {
|
else {
|
||||||
subscriptionItem.subscriptionEventHandler(.failed(error), subscriptionItem)
|
subscriptionItem.subscriptionEventHandler(.failed(error), subscriptionItem)
|
||||||
connectionProvider?.removeListener(identifier: subscriptionItem.identifier)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let retryAdvice = retryHandler.shouldRetryRequest(for: connectionError)
|
let retryAdvice = retryHandler.shouldRetryRequest(for: connectionError)
|
||||||
if retryAdvice.shouldRetry, let retryInterval = retryAdvice.retryInterval {
|
if retryAdvice.shouldRetry, let retryInterval = retryAdvice.retryInterval {
|
||||||
// swiftlint:disable:next line_length
|
|
||||||
AppSyncLogger.debug("[AppSyncSubscriptionConnection] Retrying subscription \(subscriptionItem.identifier) after \(retryInterval)")
|
AppSyncLogger.debug("[AppSyncSubscriptionConnection] Retrying subscription \(subscriptionItem.identifier) after \(retryInterval)")
|
||||||
DispatchQueue.global().asyncAfter(deadline: .now() + retryInterval) {
|
DispatchQueue.global().asyncAfter(deadline: .now() + retryInterval) {
|
||||||
self.connectionProvider?.connect()
|
self.connectionProvider?.connect()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
subscriptionItem.subscriptionEventHandler(.failed(error), subscriptionItem)
|
subscriptionItem.subscriptionEventHandler(.failed(error), subscriptionItem)
|
||||||
connectionProvider?.removeListener(identifier: subscriptionItem.identifier)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,10 +83,8 @@ extension AppSyncSubscriptionConnection {
|
||||||
additionalInfo=\(String(describing: errorPayload))
|
additionalInfo=\(String(describing: errorPayload))
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
case .unauthorized:
|
case .other:
|
||||||
AppSyncLogger.error("ConnectionProviderError.unauthorized")
|
AppSyncLogger.error("ConnectionProviderError.other")
|
||||||
case .unknown:
|
|
||||||
AppSyncLogger.error("ConnectionProviderError.unknown")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2021 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
@ -81,9 +81,7 @@ public class AppSyncSubscriptionConnection: SubscriptionConnection, RetryableCon
|
||||||
|
|
||||||
connectionProvider.addListener(identifier: subscriptionItem.identifier) { [weak self] event in
|
connectionProvider.addListener(identifier: subscriptionItem.identifier) { [weak self] event in
|
||||||
guard let self = self else {
|
guard let self = self else {
|
||||||
AppSyncLogger.debug(
|
AppSyncLogger.debug("[AppSyncSubscriptionConnection] \(#function): Self is nil, listener is not called.")
|
||||||
"[AppSyncSubscriptionConnection]: Subscription (Self) is nil, connection event is not handled."
|
|
||||||
)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
switch event {
|
switch event {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2020 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2020 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2020 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2020 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2020 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2020 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2020 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2020 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
@ -13,48 +13,10 @@ extension RealtimeConnectionProvider: ConnectionInterceptable {
|
||||||
connectionInterceptors.append(interceptor)
|
connectionInterceptors.append(interceptor)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func interceptConnection(
|
|
||||||
_ request: AppSyncConnectionRequest,
|
|
||||||
for endpoint: URL,
|
|
||||||
completion: @escaping (AppSyncConnectionRequest) -> Void
|
|
||||||
) {
|
|
||||||
chainInterceptors(
|
|
||||||
iterator: connectionInterceptors.makeIterator(),
|
|
||||||
request: request,
|
|
||||||
endpoint: endpoint,
|
|
||||||
completion: completion
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func chainInterceptors<I: IteratorProtocol>(
|
|
||||||
iterator: I,
|
|
||||||
request: AppSyncConnectionRequest,
|
|
||||||
endpoint: URL,
|
|
||||||
completion: @escaping (AppSyncConnectionRequest) -> Void
|
|
||||||
) where I.Element == ConnectionInterceptor {
|
|
||||||
|
|
||||||
var mutableIterator = iterator
|
|
||||||
guard let interceptor = mutableIterator.next() else {
|
|
||||||
completion(request)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
interceptor.interceptConnection(request, for: endpoint) { interceptedRequest in
|
|
||||||
self.chainInterceptors(
|
|
||||||
iterator: mutableIterator,
|
|
||||||
request: interceptedRequest,
|
|
||||||
endpoint: endpoint,
|
|
||||||
completion: completion
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Deprecated method
|
|
||||||
|
|
||||||
public func interceptConnection(
|
public func interceptConnection(
|
||||||
_ request: AppSyncConnectionRequest,
|
_ request: AppSyncConnectionRequest,
|
||||||
for endpoint: URL
|
for endpoint: URL
|
||||||
) -> AppSyncConnectionRequest {
|
) -> AppSyncConnectionRequest {
|
||||||
// This is added here for backward compatibility
|
|
||||||
let finalRequest = connectionInterceptors.reduce(request) { $1.interceptConnection($0, for: endpoint) }
|
let finalRequest = connectionInterceptors.reduce(request) { $1.interceptConnection($0, for: endpoint) }
|
||||||
return finalRequest
|
return finalRequest
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2020 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
@ -13,46 +13,7 @@ extension RealtimeConnectionProvider: MessageInterceptable {
|
||||||
messageInterceptors.append(interceptor)
|
messageInterceptors.append(interceptor)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func interceptMessage(
|
|
||||||
_ message: AppSyncMessage,
|
|
||||||
for endpoint: URL,
|
|
||||||
completion: @escaping (AppSyncMessage) -> Void
|
|
||||||
) {
|
|
||||||
|
|
||||||
chainInterceptors(
|
|
||||||
iterator: messageInterceptors.makeIterator(),
|
|
||||||
message: message,
|
|
||||||
endpoint: endpoint,
|
|
||||||
completion: completion
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private func chainInterceptors<I: IteratorProtocol>(
|
|
||||||
iterator: I,
|
|
||||||
message: AppSyncMessage,
|
|
||||||
endpoint: URL,
|
|
||||||
completion: @escaping (AppSyncMessage) -> Void
|
|
||||||
) where I.Element == MessageInterceptor {
|
|
||||||
|
|
||||||
var mutableIterator = iterator
|
|
||||||
guard let interceptor = mutableIterator.next() else {
|
|
||||||
completion(message)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
interceptor.interceptMessage(message, for: endpoint) { interceptedMessage in
|
|
||||||
self.chainInterceptors(
|
|
||||||
iterator: mutableIterator,
|
|
||||||
message: interceptedMessage,
|
|
||||||
endpoint: endpoint,
|
|
||||||
completion: completion
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Deprecated method
|
|
||||||
|
|
||||||
public func interceptMessage(_ message: AppSyncMessage, for endpoint: URL) -> AppSyncMessage {
|
public func interceptMessage(_ message: AppSyncMessage, for endpoint: URL) -> AppSyncMessage {
|
||||||
// This is added here for backward compatibility
|
|
||||||
let finalMessage = messageInterceptors.reduce(message) { $1.interceptMessage($0, for: endpoint) }
|
let finalMessage = messageInterceptors.reduce(message) { $1.interceptMessage($0, for: endpoint) }
|
||||||
return finalMessage
|
return finalMessage
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,60 +1,36 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2021 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
/// Consolidates usage and parameters passed to the `staleConnectionTimer` methods.
|
|
||||||
extension RealtimeConnectionProvider {
|
extension RealtimeConnectionProvider {
|
||||||
|
|
||||||
/// Start a stale connection timer, first invalidating and destroying any existing timer
|
/// Start a stale connection timer, first invalidating and destroying any existing timer
|
||||||
func startStaleConnectionTimer() {
|
func startStaleConnectionTimer() {
|
||||||
AppSyncLogger.debug(
|
AppSyncLogger.debug("[RealtimeConnectionProvider] Starting stale connection timer for \(staleConnectionTimeout.get())s")
|
||||||
"[RealtimeConnectionProvider] Starting stale connection timer for \(staleConnectionTimer.interval)s"
|
if staleConnectionTimer != nil {
|
||||||
)
|
stopStaleConnectionTimer()
|
||||||
|
}
|
||||||
staleConnectionTimer.start(interval: RealtimeConnectionProvider.staleConnectionTimeout) {
|
staleConnectionTimer = CountdownTimer(interval: staleConnectionTimeout.get()) {
|
||||||
self.disconnectStaleConnection()
|
self.disconnectStaleConnection()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reset the stale connection timer in response to receiving a message from the websocket
|
/// Stop and destroy any existing stale connection timer
|
||||||
func resetStaleConnectionTimer(interval: TimeInterval? = nil) {
|
func stopStaleConnectionTimer() {
|
||||||
AppSyncLogger.verbose("[RealtimeConnectionProvider] Resetting stale connection timer")
|
AppSyncLogger.debug("[RealtimeConnectionProvider] Stopping and destroying stale connection timer")
|
||||||
staleConnectionTimer.reset(interval: interval)
|
staleConnectionTimer?.invalidate()
|
||||||
|
staleConnectionTimer = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Stops the timer when disconnecting the websocket.
|
/// Reset the stale connection timer in response to receiving a message
|
||||||
func invalidateStaleConnectionTimer() {
|
func resetStaleConnectionTimer() {
|
||||||
staleConnectionTimer.invalidate()
|
AppSyncLogger.debug("[RealtimeConnectionProvider] Resetting stale connection timer")
|
||||||
}
|
staleConnectionTimer?.resetCountdown()
|
||||||
|
|
||||||
/// Handle updates from the ConnectivityMonitor
|
|
||||||
func handleConnectivityUpdates(connectivity: ConnectivityPath) {
|
|
||||||
connectionQueue.async {[weak self] in
|
|
||||||
guard let self = self else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
AppSyncLogger.debug(
|
|
||||||
"[RealtimeConnectionProvider] Status: \(self.status). Connectivity status: \(connectivity.status)"
|
|
||||||
)
|
|
||||||
if self.status == .connected && connectivity.status == .unsatisfied && !self.isStaleConnection {
|
|
||||||
AppSyncLogger.debug(
|
|
||||||
"[RealtimeConnectionProvider] Connetion is stale. Pending reconnect on connectivity."
|
|
||||||
)
|
|
||||||
self.isStaleConnection = true
|
|
||||||
|
|
||||||
} else if self.status == .connected && self.isStaleConnection && connectivity.status == .satisfied {
|
|
||||||
AppSyncLogger.debug(
|
|
||||||
"[RealtimeConnectionProvider] Connetion is stale. Disconnecting to begin reconnect."
|
|
||||||
)
|
|
||||||
self.staleConnectionTimer.invalidate()
|
|
||||||
self.disconnectStaleConnection()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fired when the stale connection timer expires
|
/// Fired when the stale connection timer expires
|
||||||
|
@ -65,9 +41,9 @@ extension RealtimeConnectionProvider {
|
||||||
}
|
}
|
||||||
AppSyncLogger.error("[RealtimeConnectionProvider] Realtime connection is stale, disconnecting.")
|
AppSyncLogger.error("[RealtimeConnectionProvider] Realtime connection is stale, disconnecting.")
|
||||||
self.status = .notConnected
|
self.status = .notConnected
|
||||||
self.isStaleConnection = false
|
|
||||||
self.websocket.disconnect()
|
self.websocket.disconnect()
|
||||||
self.updateCallback(event: .error(ConnectionProviderError.connection))
|
self.updateCallback(event: .error(ConnectionProviderError.connection))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2021 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
@ -53,12 +53,7 @@ extension RealtimeConnectionProvider: AppSyncWebsocketDelegate {
|
||||||
self?.handleConnectionAck(response: response)
|
self?.handleConnectionAck(response: response)
|
||||||
}
|
}
|
||||||
case .error:
|
case .error:
|
||||||
AppSyncLogger.verbose("[RealtimeConnectionProvider] received error")
|
AppSyncLogger.debug("[RealtimeConnectionProvider] received error")
|
||||||
connectionQueue.async { [weak self] in
|
|
||||||
self?.handleError(response: response)
|
|
||||||
}
|
|
||||||
case .connectionError:
|
|
||||||
AppSyncLogger.verbose("[RealtimeConnectionProvider] received error")
|
|
||||||
connectionQueue.async { [weak self] in
|
connectionQueue.async { [weak self] in
|
||||||
self?.handleError(response: response)
|
self?.handleError(response: response)
|
||||||
}
|
}
|
||||||
|
@ -67,7 +62,7 @@ extension RealtimeConnectionProvider: AppSyncWebsocketDelegate {
|
||||||
updateCallback(event: .data(appSyncResponse))
|
updateCallback(event: .data(appSyncResponse))
|
||||||
}
|
}
|
||||||
case .keepAlive:
|
case .keepAlive:
|
||||||
AppSyncLogger.verbose("[RealtimeConnectionProvider] received keepAlive")
|
AppSyncLogger.debug("[RealtimeConnectionProvider] received keepAlive")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,6 +80,16 @@ extension RealtimeConnectionProvider: AppSyncWebsocketDelegate {
|
||||||
status = .connected
|
status = .connected
|
||||||
updateCallback(event: .connection(status))
|
updateCallback(event: .connection(status))
|
||||||
|
|
||||||
|
if let overrideConnectionTimeoutInSeconds = overrideConnectionTimeoutInSeconds {
|
||||||
|
AppSyncLogger.debug(
|
||||||
|
"""
|
||||||
|
`overrideConnectionTimeoutInSeconds` exists: \(overrideConnectionTimeoutInSeconds) seconds.
|
||||||
|
Ignoring service `connectionTimeoutMs`.
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// If the service returns a connection timeout, use that instead of the default
|
// If the service returns a connection timeout, use that instead of the default
|
||||||
guard case let .number(value) = response.payload?["connectionTimeoutMs"] else {
|
guard case let .number(value) = response.payload?["connectionTimeoutMs"] else {
|
||||||
return
|
return
|
||||||
|
@ -92,7 +97,8 @@ extension RealtimeConnectionProvider: AppSyncWebsocketDelegate {
|
||||||
|
|
||||||
let interval = value / 1_000
|
let interval = value / 1_000
|
||||||
|
|
||||||
guard interval != staleConnectionTimer.interval else {
|
// Only use the service value if it is not equal to the one set already
|
||||||
|
guard interval != staleConnectionTimer?.interval else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,28 +108,40 @@ extension RealtimeConnectionProvider: AppSyncWebsocketDelegate {
|
||||||
instructions: \(interval)s
|
instructions: \(interval)s
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
resetStaleConnectionTimer(interval: interval)
|
staleConnectionTimeout.set(interval)
|
||||||
|
startStaleConnectionTimer()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resolves & dispatches errors from `response`.
|
/// Resolves & dispatches errors from `response`.
|
||||||
///
|
///
|
||||||
/// - Warning: This method must be invoked on the `connectionQueue`
|
/// - Warning: This method must be invoked on the `connectionQueue`
|
||||||
func handleError(response: RealtimeConnectionProviderResponse) {
|
private func handleError(response: RealtimeConnectionProviderResponse) {
|
||||||
// If we get an error while the connection was inProgress state,
|
// If we get an error in connection inprogress state, return back as connection error.
|
||||||
let error = response.toConnectionProviderError(connectionState: status)
|
guard status != .inProgress else {
|
||||||
if status == .inProgress {
|
|
||||||
status = .notConnected
|
status = .notConnected
|
||||||
|
updateCallback(event: .error(ConnectionProviderError.connection))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// If limit exceeded is for a particular subscription identifier, throttle using `limitExceededSubject`
|
// Return back as generic error if there is no identifier.
|
||||||
if case .limitExceeded(let id) = error,
|
guard let identifier = response.id else {
|
||||||
id == nil,
|
let genericError = ConnectionProviderError.other
|
||||||
#available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *) {
|
updateCallback(event: .error(genericError))
|
||||||
self.limitExceededSubject.send(error)
|
return
|
||||||
} else {
|
|
||||||
updateCallback(event: .error(error))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Map to limit exceed error if we get MaxSubscriptionsReachedException
|
||||||
|
if let errorType = response.payload?["errorType"],
|
||||||
|
errorType == "MaxSubscriptionsReachedException" {
|
||||||
|
let limitExceedError = ConnectionProviderError.limitExceeded(identifier)
|
||||||
|
updateCallback(event: .error(limitExceedError))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let subscriptionError = ConnectionProviderError.subscription(identifier, response.payload)
|
||||||
|
updateCallback(event: .error(subscriptionError))
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension RealtimeConnectionProviderResponse {
|
extension RealtimeConnectionProviderResponse {
|
||||||
|
|
|
@ -1,21 +1,16 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2021 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import Combine
|
|
||||||
|
|
||||||
/// Appsync Real time connection that connects to subscriptions
|
/// Appsync Real time connection that connects to subscriptions
|
||||||
/// through websocket.
|
/// through websocket.
|
||||||
public class RealtimeConnectionProvider: ConnectionProvider {
|
public class RealtimeConnectionProvider: ConnectionProvider {
|
||||||
/// Maximum number of seconds a connection may go without receiving a keep alive
|
private let url: URL
|
||||||
/// message before we consider it stale and force a disconnect
|
|
||||||
static let staleConnectionTimeout: TimeInterval = 5 * 60
|
|
||||||
|
|
||||||
private let urlRequest: URLRequest
|
|
||||||
var listeners: [String: ConnectionProviderCallback]
|
var listeners: [String: ConnectionProviderCallback]
|
||||||
|
|
||||||
let websocket: AppSyncWebsocketProvider
|
let websocket: AppSyncWebsocketProvider
|
||||||
|
@ -24,13 +19,17 @@ public class RealtimeConnectionProvider: ConnectionProvider {
|
||||||
var messageInterceptors: [MessageInterceptor]
|
var messageInterceptors: [MessageInterceptor]
|
||||||
var connectionInterceptors: [ConnectionInterceptor]
|
var connectionInterceptors: [ConnectionInterceptor]
|
||||||
|
|
||||||
|
/// Maximum number of seconds a connection may go without receiving a keep alive
|
||||||
|
/// message before we consider it stale and force a disconnect
|
||||||
|
let staleConnectionTimeout: AtomicValue<TimeInterval>
|
||||||
|
|
||||||
|
/// Optional overide for taking client side precendent over service's `connectionTimeoutMs`
|
||||||
|
var overrideConnectionTimeoutInSeconds: Int?
|
||||||
|
|
||||||
/// A timer that automatically disconnects the current connection if it goes longer
|
/// A timer that automatically disconnects the current connection if it goes longer
|
||||||
/// than `staleConnectionTimeout` without activity. Receiving any data or "keep
|
/// than `staleConnectionTimeout` without activity. Receiving any data or "keep
|
||||||
/// alive" message will cause the timer to be reset to the full interval.
|
/// alive" message will cause the timer to be reset to the full interval.
|
||||||
var staleConnectionTimer: CountdownTimer
|
var staleConnectionTimer: CountdownTimer?
|
||||||
|
|
||||||
/// Intermediate state when the connection is connected and connectivity updates to unsatisfied (offline)
|
|
||||||
var isStaleConnection: Bool
|
|
||||||
|
|
||||||
/// Manages concurrency for socket connections, disconnections, writes, and status reports.
|
/// Manages concurrency for socket connections, disconnections, writes, and status reports.
|
||||||
///
|
///
|
||||||
|
@ -38,58 +37,29 @@ public class RealtimeConnectionProvider: ConnectionProvider {
|
||||||
/// handled one at a time.
|
/// handled one at a time.
|
||||||
let connectionQueue: DispatchQueue
|
let connectionQueue: DispatchQueue
|
||||||
|
|
||||||
/// Monitor for connectivity updates
|
|
||||||
let connectivityMonitor: ConnectivityMonitor
|
|
||||||
|
|
||||||
/// The serial queue on which status & message callbacks from the web socket are invoked.
|
/// The serial queue on which status & message callbacks from the web socket are invoked.
|
||||||
private let serialCallbackQueue: DispatchQueue
|
private let serialCallbackQueue = DispatchQueue(
|
||||||
|
|
||||||
/// Throttle when AppSync sends LimitExceeded error. High rate of subscriptions requests will cause AppSync to send
|
|
||||||
/// connection level LimitExceeded errors for each subscribe made. A connection level error means that there is no
|
|
||||||
/// subscription id associated with the error. When handling these errors, all subscriptions will receive a message
|
|
||||||
/// for the error. Use this subject to send and throttle the errors on the client side.
|
|
||||||
var limitExceededThrottleSink: Any?
|
|
||||||
var iLimitExceededSubject: Any?
|
|
||||||
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
|
|
||||||
var limitExceededSubject: PassthroughSubject<ConnectionProviderError, Never> {
|
|
||||||
if iLimitExceededSubject == nil {
|
|
||||||
iLimitExceededSubject = PassthroughSubject<ConnectionProviderError, Never>()
|
|
||||||
}
|
|
||||||
return iLimitExceededSubject as! PassthroughSubject<ConnectionProviderError, Never> // swiftlint:disable:this force_cast line_length
|
|
||||||
}
|
|
||||||
|
|
||||||
public convenience init(for urlRequest: URLRequest, websocket: AppSyncWebsocketProvider) {
|
|
||||||
self.init(urlRequest: urlRequest, websocket: websocket)
|
|
||||||
}
|
|
||||||
|
|
||||||
init(
|
|
||||||
urlRequest: URLRequest,
|
|
||||||
websocket: AppSyncWebsocketProvider,
|
|
||||||
connectionQueue: DispatchQueue = DispatchQueue(
|
|
||||||
label: "com.amazonaws.AppSyncRealTimeConnectionProvider.serialQueue"
|
|
||||||
),
|
|
||||||
serialCallbackQueue: DispatchQueue = DispatchQueue(
|
|
||||||
label: "com.amazonaws.AppSyncRealTimeConnectionProvider.callbackQueue"
|
label: "com.amazonaws.AppSyncRealTimeConnectionProvider.callbackQueue"
|
||||||
),
|
)
|
||||||
connectivityMonitor: ConnectivityMonitor = ConnectivityMonitor()
|
|
||||||
) {
|
public init(for url: URL, websocket: AppSyncWebsocketProvider, overrideConnectionTimeoutInSeconds: Int? = nil) {
|
||||||
self.urlRequest = urlRequest
|
self.url = url
|
||||||
self.websocket = websocket
|
self.websocket = websocket
|
||||||
|
|
||||||
self.listeners = [:]
|
self.listeners = [:]
|
||||||
self.status = .notConnected
|
self.status = .notConnected
|
||||||
self.messageInterceptors = []
|
self.messageInterceptors = []
|
||||||
self.connectionInterceptors = []
|
self.connectionInterceptors = []
|
||||||
self.staleConnectionTimer = CountdownTimer()
|
self.overrideConnectionTimeoutInSeconds = overrideConnectionTimeoutInSeconds
|
||||||
self.isStaleConnection = false
|
if let overrideConnectionTimeoutInSeconds = overrideConnectionTimeoutInSeconds {
|
||||||
self.connectionQueue = connectionQueue
|
self.staleConnectionTimeout = AtomicValue(initialValue: Double(overrideConnectionTimeoutInSeconds))
|
||||||
self.serialCallbackQueue = serialCallbackQueue
|
} else {
|
||||||
self.connectivityMonitor = connectivityMonitor
|
self.staleConnectionTimeout = AtomicValue(initialValue: 5 * 60)
|
||||||
|
|
||||||
connectivityMonitor.start(onUpdates: handleConnectivityUpdates(connectivity:))
|
|
||||||
|
|
||||||
if #available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *) {
|
|
||||||
subscribeToLimitExceededThrottle()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.connectionQueue = DispatchQueue(
|
||||||
|
label: "com.amazonaws.AppSyncRealTimeConnectionProvider.serialQueue"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - ConnectionProvider methods
|
// MARK: - ConnectionProvider methods
|
||||||
|
@ -103,32 +73,19 @@ public class RealtimeConnectionProvider: ConnectionProvider {
|
||||||
self.updateCallback(event: .connection(self.status))
|
self.updateCallback(event: .connection(self.status))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
guard let url = self.urlRequest.url else {
|
|
||||||
self.updateCallback(event: .error(ConnectionProviderError.unknown(
|
|
||||||
message: "Missing URL",
|
|
||||||
payload: nil
|
|
||||||
)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
self.status = .inProgress
|
self.status = .inProgress
|
||||||
self.updateCallback(event: .connection(self.status))
|
self.updateCallback(event: .connection(self.status))
|
||||||
|
let request = AppSyncConnectionRequest(url: self.url)
|
||||||
let request = AppSyncConnectionRequest(url: url)
|
let signedRequest = self.interceptConnection(request, for: self.url)
|
||||||
self.interceptConnection(request, for: url) { signedRequest in
|
|
||||||
var urlRequest = self.urlRequest
|
|
||||||
urlRequest.url = signedRequest.url
|
|
||||||
|
|
||||||
DispatchQueue.global().async {
|
DispatchQueue.global().async {
|
||||||
self.websocket.connect(
|
self.websocket.connect(
|
||||||
urlRequest: urlRequest,
|
url: signedRequest.url,
|
||||||
protocols: ["graphql-ws"],
|
protocols: ["graphql-ws"],
|
||||||
delegate: self
|
delegate: self
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public func write(_ message: AppSyncMessage) {
|
public func write(_ message: AppSyncMessage) {
|
||||||
|
|
||||||
|
@ -136,14 +93,8 @@ public class RealtimeConnectionProvider: ConnectionProvider {
|
||||||
guard let self = self else {
|
guard let self = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
guard let url = self.urlRequest.url else {
|
|
||||||
self.updateCallback(event: .error(ConnectionProviderError.unknown(
|
let signedMessage = self.interceptMessage(message, for: self.url)
|
||||||
message: "Missing URL",
|
|
||||||
payload: nil
|
|
||||||
)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
self.interceptMessage(message, for: url) { signedMessage in
|
|
||||||
let jsonEncoder = JSONEncoder()
|
let jsonEncoder = JSONEncoder()
|
||||||
do {
|
do {
|
||||||
let jsonData = try jsonEncoder.encode(signedMessage)
|
let jsonData = try jsonEncoder.encode(signedMessage)
|
||||||
|
@ -166,12 +117,11 @@ public class RealtimeConnectionProvider: ConnectionProvider {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public func disconnect() {
|
public func disconnect() {
|
||||||
connectionQueue.async {
|
connectionQueue.async {
|
||||||
self.websocket.disconnect()
|
self.websocket.disconnect()
|
||||||
self.invalidateStaleConnectionTimer()
|
self.staleConnectionTimer?.invalidate()
|
||||||
|
self.staleConnectionTimer = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,12 +140,11 @@ public class RealtimeConnectionProvider: ConnectionProvider {
|
||||||
self.listeners.removeValue(forKey: identifier)
|
self.listeners.removeValue(forKey: identifier)
|
||||||
|
|
||||||
if self.listeners.isEmpty {
|
if self.listeners.isEmpty {
|
||||||
AppSyncLogger.debug(
|
AppSyncLogger.debug("[RealtimeConnectionProvider] all subscriptions removed, disconnecting websocket connection.")
|
||||||
"[RealtimeConnectionProvider] all subscriptions removed, disconnecting websocket connection."
|
|
||||||
)
|
|
||||||
self.status = .notConnected
|
self.status = .notConnected
|
||||||
self.websocket.disconnect()
|
self.websocket.disconnect()
|
||||||
self.invalidateStaleConnectionTimer()
|
self.staleConnectionTimer?.invalidate()
|
||||||
|
self.staleConnectionTimer = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -222,30 +171,6 @@ public class RealtimeConnectionProvider: ConnectionProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
|
|
||||||
func subscribeToLimitExceededThrottle() {
|
|
||||||
limitExceededThrottleSink = limitExceededSubject
|
|
||||||
.filter {
|
|
||||||
// Make sure the limitExceeded error is a connection level error (no subscription id present).
|
|
||||||
// When id is present, it is passed back directly subscription via `updateCallback`.
|
|
||||||
if case .limitExceeded(let id) = $0, id == nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
.throttle(for: .milliseconds(150), scheduler: connectionQueue, latest: true)
|
|
||||||
.sink { completion in
|
|
||||||
switch completion {
|
|
||||||
case .failure(let error):
|
|
||||||
AppSyncLogger.verbose("limitExceededThrottleSink failed \(error)")
|
|
||||||
case .finished:
|
|
||||||
AppSyncLogger.verbose("limitExceededThrottleSink finished")
|
|
||||||
}
|
|
||||||
} receiveValue: { result in
|
|
||||||
self.updateCallback(event: .error(result))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// - Warning: This must be invoked from the `connectionQueue`
|
/// - Warning: This must be invoked from the `connectionQueue`
|
||||||
private func receivedConnectionInit() {
|
private func receivedConnectionInit() {
|
||||||
status = .notConnected
|
status = .notConnected
|
||||||
|
|
|
@ -1,14 +1,12 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2020 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
/// More information about the response can be found here
|
|
||||||
/// https://docs.aws.amazon.com/appsync/latest/devguide/real-time-websocket-client.html#connection-init-message
|
|
||||||
struct RealtimeConnectionProviderResponse {
|
struct RealtimeConnectionProviderResponse {
|
||||||
|
|
||||||
/// Subscription Identifier
|
/// Subscription Identifier
|
||||||
|
@ -44,8 +42,6 @@ enum RealtimeConnectionProviderResponseType: String, Decodable {
|
||||||
case data
|
case data
|
||||||
|
|
||||||
case error
|
case error
|
||||||
|
|
||||||
case connectionError = "connection_error"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension RealtimeConnectionProviderResponse: Decodable {
|
extension RealtimeConnectionProviderResponse: Decodable {
|
||||||
|
@ -56,122 +52,3 @@ extension RealtimeConnectionProviderResponse: Decodable {
|
||||||
case responseType = "type"
|
case responseType = "type"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Helper methods to check which type of errors, such as `MaxSubscriptionsReachedError`, `LimitExceededError`.
|
|
||||||
/// Errors have the following shape
|
|
||||||
///
|
|
||||||
/// "type": "error": A constant <string> parameter.
|
|
||||||
/// "id": <string>: The ID of the corresponding registered subscription, if relevant.
|
|
||||||
/// "payload" <Object>: An object that contains the corresponding error information.
|
|
||||||
///
|
|
||||||
/// More information can be found here
|
|
||||||
/// https://docs.aws.amazon.com/appsync/latest/devguide/real-time-websocket-client.html#error-message
|
|
||||||
extension RealtimeConnectionProviderResponse {
|
|
||||||
|
|
||||||
func toConnectionProviderError(connectionState: ConnectionState) -> ConnectionProviderError {
|
|
||||||
// If it is Unauthorized, return `.unauthorized` error.
|
|
||||||
guard !isUnauthorizationError() else {
|
|
||||||
return .unauthorized
|
|
||||||
}
|
|
||||||
|
|
||||||
// If it is in-progress, return `.connection`.
|
|
||||||
guard connectionState != .inProgress else {
|
|
||||||
return .connection
|
|
||||||
}
|
|
||||||
|
|
||||||
if isLimitExceededError() || isMaxSubscriptionReachedError() {
|
|
||||||
// Is it observed that LimitExceeded error does not have `id` while MaxSubscriptionReached does have a
|
|
||||||
// corresponding identifier. Both are mapped to `limitExceeded` with optional identifier.
|
|
||||||
return .limitExceeded(id)
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the type of error is not handled (by checking `isLimitExceededError`, `isMaxSubscriptionReachedError`,
|
|
||||||
// etc), and is not for a specific subscription, then return unknown error
|
|
||||||
guard let identifier = id else {
|
|
||||||
return .unknown(message: nil, causedBy: nil, payload: payload)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default scenario - return the error with subscription id and error payload.
|
|
||||||
return .subscription(identifier, payload)
|
|
||||||
}
|
|
||||||
|
|
||||||
func isMaxSubscriptionReachedError() -> Bool {
|
|
||||||
// It is expected to contain payload with corresponding error information
|
|
||||||
guard let payload = payload else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keep this here for backwards compatibility for previously provisioned AppSync services
|
|
||||||
if let errorType = payload["errorType"],
|
|
||||||
errorType == "MaxSubscriptionsReachedException" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// The observed response from the service
|
|
||||||
// { "id":"DB23EC80-C51A-4FEE-82F7-AA4949B4F299",
|
|
||||||
// "type":"error",
|
|
||||||
// "payload": {
|
|
||||||
// "errors": {
|
|
||||||
// "errorType":"MaxSubscriptionsReachedError",
|
|
||||||
// "message":"Max number of 100 subscriptions reached" }}}
|
|
||||||
if let errors = payload["errors"],
|
|
||||||
case let .object(errorsObject) = errors,
|
|
||||||
let errorType = errorsObject["errorType"],
|
|
||||||
errorType == "MaxSubscriptionsReachedError" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func isLimitExceededError() -> Bool {
|
|
||||||
// It is expected to contain payload with corresponding error information
|
|
||||||
guard let payload = payload else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// The observed response from the service
|
|
||||||
// { "type":"error",
|
|
||||||
// "payload": {
|
|
||||||
// "errors": {
|
|
||||||
// "errorType":"LimitExceededError",
|
|
||||||
// "message":"Rate limit exceeded" }}}
|
|
||||||
if let errors = payload["errors"],
|
|
||||||
case let .object(errorsObject) = errors,
|
|
||||||
let errorType = errorsObject["errorType"],
|
|
||||||
errorType == "LimitExceededError" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func isUnauthorizationError() -> Bool {
|
|
||||||
// It is expected to contain payload with corresponding error information
|
|
||||||
guard let payload = payload,
|
|
||||||
let errors = payload["errors"],
|
|
||||||
case let .array(errorsArray) = errors else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// The observed response from the service
|
|
||||||
// { "payload": {
|
|
||||||
// "errors": [{
|
|
||||||
// "errorType":"com.amazonaws.deepdish.graphql.auth#UnauthorizedException",
|
|
||||||
// "message":"You are not authorized to make this call.",
|
|
||||||
// "errorCode":400 }]},
|
|
||||||
// "type":"connection_error" }
|
|
||||||
return errorsArray.contains { error in
|
|
||||||
guard case let .object(errorObject) = error,
|
|
||||||
case let .string(errorObjectString) = errorObject["errorType"] else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if errorObjectString.contains("UnauthorizedException") {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
//
|
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
|
||||||
// All Rights Reserved.
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
//
|
|
||||||
|
|
||||||
#if swift(>=5.5.2)
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
|
|
||||||
extension RealtimeConnectionProviderAsync: ConnectionInterceptableAsync {
|
|
||||||
|
|
||||||
public func addInterceptor(_ interceptor: ConnectionInterceptorAsync) {
|
|
||||||
connectionInterceptors.append(interceptor)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func interceptConnection(
|
|
||||||
_ request: AppSyncConnectionRequest,
|
|
||||||
for endpoint: URL
|
|
||||||
) async -> AppSyncConnectionRequest {
|
|
||||||
var finalRequest = request
|
|
||||||
for interceptor in connectionInterceptors {
|
|
||||||
finalRequest = await interceptor.interceptConnection(finalRequest, for: endpoint)
|
|
||||||
}
|
|
||||||
|
|
||||||
return finalRequest
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,28 +0,0 @@
|
||||||
//
|
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
|
||||||
// All Rights Reserved.
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
//
|
|
||||||
|
|
||||||
#if swift(>=5.5.2)
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
|
|
||||||
extension RealtimeConnectionProviderAsync: MessageInterceptableAsync {
|
|
||||||
public func addInterceptor(_ interceptor: MessageInterceptorAsync) {
|
|
||||||
messageInterceptors.append(interceptor)
|
|
||||||
}
|
|
||||||
|
|
||||||
public func interceptMessage(_ message: AppSyncMessage, for endpoint: URL) async -> AppSyncMessage {
|
|
||||||
var finalMessage = message
|
|
||||||
for interceptor in messageInterceptors {
|
|
||||||
finalMessage = await interceptor.interceptMessage(finalMessage, for: endpoint)
|
|
||||||
}
|
|
||||||
|
|
||||||
return finalMessage
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,77 +0,0 @@
|
||||||
//
|
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
|
||||||
// All Rights Reserved.
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
//
|
|
||||||
|
|
||||||
#if swift(>=5.5.2)
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
/// Consolidates usage and parameters passed to the `staleConnectionTimer` methods.
|
|
||||||
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
|
|
||||||
extension RealtimeConnectionProviderAsync {
|
|
||||||
|
|
||||||
/// Start a stale connection timer, first invalidating and destroying any existing timer
|
|
||||||
func startStaleConnectionTimer() {
|
|
||||||
AppSyncLogger.debug(
|
|
||||||
"[RealtimeConnectionProvider] Starting stale connection timer for \(staleConnectionTimer.interval)s"
|
|
||||||
)
|
|
||||||
|
|
||||||
staleConnectionTimer.start(interval: RealtimeConnectionProviderAsync.staleConnectionTimeout) {
|
|
||||||
self.disconnectStaleConnection()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reset the stale connection timer in response to receiving a message from the websocket
|
|
||||||
func resetStaleConnectionTimer(interval: TimeInterval? = nil) {
|
|
||||||
AppSyncLogger.verbose("[RealtimeConnectionProvider] Resetting stale connection timer")
|
|
||||||
staleConnectionTimer.reset(interval: interval)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Stops the timer when disconnecting the websocket.
|
|
||||||
func invalidateStaleConnectionTimer() {
|
|
||||||
staleConnectionTimer.invalidate()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Handle updates from the ConnectivityMonitor
|
|
||||||
func handleConnectivityUpdates(connectivity: ConnectivityPath) {
|
|
||||||
taskQueue.async { [weak self] in
|
|
||||||
guard let self = self else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
AppSyncLogger.debug(
|
|
||||||
"[RealtimeConnectionProvider] Status: \(self.status). Connectivity status: \(connectivity.status)"
|
|
||||||
)
|
|
||||||
if self.status == .connected && connectivity.status == .unsatisfied && !self.isStaleConnection {
|
|
||||||
AppSyncLogger.debug(
|
|
||||||
"[RealtimeConnectionProvider] Connetion is stale. Pending reconnect on connectivity."
|
|
||||||
)
|
|
||||||
self.isStaleConnection = true
|
|
||||||
|
|
||||||
} else if self.status == .connected && self.isStaleConnection && connectivity.status == .satisfied {
|
|
||||||
AppSyncLogger.debug(
|
|
||||||
"[RealtimeConnectionProvider] Connetion is stale. Disconnecting to begin reconnect."
|
|
||||||
)
|
|
||||||
self.staleConnectionTimer.invalidate()
|
|
||||||
self.disconnectStaleConnection()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Fired when the stale connection timer expires
|
|
||||||
private func disconnectStaleConnection() {
|
|
||||||
taskQueue.async { [weak self] in
|
|
||||||
guard let self = self else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
AppSyncLogger.error("[RealtimeConnectionProvider] Realtime connection is stale, disconnecting.")
|
|
||||||
self.status = .notConnected
|
|
||||||
self.isStaleConnection = false
|
|
||||||
self.websocket.disconnect()
|
|
||||||
self.updateCallback(event: .error(ConnectionProviderError.connection))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
|
@ -1,130 +0,0 @@
|
||||||
//
|
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
|
||||||
// All Rights Reserved.
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
//
|
|
||||||
|
|
||||||
#if swift(>=5.5.2)
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
|
|
||||||
extension RealtimeConnectionProviderAsync: AppSyncWebsocketDelegate {
|
|
||||||
|
|
||||||
public func websocketDidConnect(provider: AppSyncWebsocketProvider) {
|
|
||||||
// Call the ack to finish the connection handshake
|
|
||||||
// Inform the callback when ack gives back a response.
|
|
||||||
AppSyncLogger.debug("[RealtimeConnectionProvider] WebsocketDidConnect, sending init message")
|
|
||||||
sendConnectionInitMessage()
|
|
||||||
startStaleConnectionTimer()
|
|
||||||
}
|
|
||||||
|
|
||||||
public func websocketDidDisconnect(provider: AppSyncWebsocketProvider, error: Error?) {
|
|
||||||
taskQueue.async { [weak self] in
|
|
||||||
guard let self = self else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
self.status = .notConnected
|
|
||||||
guard error != nil else {
|
|
||||||
self.updateCallback(event: .connection(self.status))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
self.updateCallback(event: .error(ConnectionProviderError.connection))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func websocketDidReceiveData(provider: AppSyncWebsocketProvider, data: Data) {
|
|
||||||
do {
|
|
||||||
let response = try JSONDecoder().decode(RealtimeConnectionProviderResponse.self, from: data)
|
|
||||||
handleResponse(response)
|
|
||||||
} catch {
|
|
||||||
AppSyncLogger.error(error)
|
|
||||||
updateCallback(event: .error(ConnectionProviderError.jsonParse(nil, error)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Handle websocket response
|
|
||||||
|
|
||||||
private func handleResponse(_ response: RealtimeConnectionProviderResponse) {
|
|
||||||
resetStaleConnectionTimer()
|
|
||||||
|
|
||||||
switch response.responseType {
|
|
||||||
case .connectionAck:
|
|
||||||
AppSyncLogger.debug("[RealtimeConnectionProvider] received connectionAck")
|
|
||||||
taskQueue.async { [weak self] in
|
|
||||||
self?.handleConnectionAck(response: response)
|
|
||||||
}
|
|
||||||
case .error:
|
|
||||||
AppSyncLogger.verbose("[RealtimeConnectionProvider] received error")
|
|
||||||
taskQueue.async { [weak self] in
|
|
||||||
self?.handleError(response: response)
|
|
||||||
}
|
|
||||||
case .connectionError:
|
|
||||||
AppSyncLogger.verbose("[RealtimeConnectionProvider] received error")
|
|
||||||
taskQueue.async { [weak self] in
|
|
||||||
self?.handleError(response: response)
|
|
||||||
}
|
|
||||||
case .subscriptionAck, .unsubscriptionAck, .data:
|
|
||||||
if let appSyncResponse = response.toAppSyncResponse() {
|
|
||||||
updateCallback(event: .data(appSyncResponse))
|
|
||||||
}
|
|
||||||
case .keepAlive:
|
|
||||||
AppSyncLogger.verbose("[RealtimeConnectionProvider] received keepAlive")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Updates connection status callbacks and sets stale connection timeout
|
|
||||||
///
|
|
||||||
/// - Warning: This method must be invoked on the `connectionQueue`
|
|
||||||
private func handleConnectionAck(response: RealtimeConnectionProviderResponse) {
|
|
||||||
// Only from in progress state, the connection can transition to connected state.
|
|
||||||
// The below guard statement make sure that. If we get connectionAck in other
|
|
||||||
// state means that we have initiated a disconnect parallely.
|
|
||||||
guard status == .inProgress else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
status = .connected
|
|
||||||
updateCallback(event: .connection(status))
|
|
||||||
|
|
||||||
// If the service returns a connection timeout, use that instead of the default
|
|
||||||
guard case let .number(value) = response.payload?["connectionTimeoutMs"] else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let interval = value / 1_000
|
|
||||||
|
|
||||||
guard interval != staleConnectionTimer.interval else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
AppSyncLogger.debug(
|
|
||||||
"""
|
|
||||||
Resetting keep alive timer in response to service timeout \
|
|
||||||
instructions: \(interval)s
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
resetStaleConnectionTimer(interval: interval)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Resolves & dispatches errors from `response`.
|
|
||||||
///
|
|
||||||
/// - Warning: This method must be invoked on the `connectionQueue`
|
|
||||||
func handleError(response: RealtimeConnectionProviderResponse) {
|
|
||||||
// If we get an error while the connection was inProgress state,
|
|
||||||
let error = response.toConnectionProviderError(connectionState: status)
|
|
||||||
if status == .inProgress {
|
|
||||||
status = .notConnected
|
|
||||||
}
|
|
||||||
|
|
||||||
// If limit exceeded is for a particular subscription identifier, throttle using `limitExceededSubject`
|
|
||||||
if case .limitExceeded(let id) = error, id == nil {
|
|
||||||
self.limitExceededSubject.send(error)
|
|
||||||
} else {
|
|
||||||
updateCallback(event: .error(error))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
#endif
|
|
|
@ -1,242 +0,0 @@
|
||||||
//
|
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
|
||||||
// All Rights Reserved.
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
//
|
|
||||||
|
|
||||||
#if swift(>=5.5.2)
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import Combine
|
|
||||||
|
|
||||||
/// Appsync Real time connection that connects to subscriptions
|
|
||||||
/// through websocket.
|
|
||||||
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
|
|
||||||
public class RealtimeConnectionProviderAsync: ConnectionProvider {
|
|
||||||
/// Maximum number of seconds a connection may go without receiving a keep alive
|
|
||||||
/// message before we consider it stale and force a disconnect
|
|
||||||
static let staleConnectionTimeout: TimeInterval = 5 * 60
|
|
||||||
|
|
||||||
let urlRequest: URLRequest
|
|
||||||
var listeners: [String: ConnectionProviderCallback]
|
|
||||||
|
|
||||||
let websocket: AppSyncWebsocketProvider
|
|
||||||
|
|
||||||
var status: ConnectionState
|
|
||||||
|
|
||||||
var messageInterceptors = [MessageInterceptorAsync]()
|
|
||||||
var connectionInterceptors = [ConnectionInterceptorAsync]()
|
|
||||||
|
|
||||||
/// A timer that automatically disconnects the current connection if it goes longer
|
|
||||||
/// than `staleConnectionTimeout` without activity. Receiving any data or "keep
|
|
||||||
/// alive" message will cause the timer to be reset to the full interval.
|
|
||||||
var staleConnectionTimer: CountdownTimer
|
|
||||||
|
|
||||||
/// Intermediate state when the connection is connected and connectivity updates to unsatisfied (offline)
|
|
||||||
var isStaleConnection: Bool
|
|
||||||
|
|
||||||
/// Manages concurrency for socket connections, disconnections, writes, and status reports.
|
|
||||||
///
|
|
||||||
/// Each connection request will be sent to this queue. Connection request are
|
|
||||||
/// handled one at a time.
|
|
||||||
let taskQueue = TaskQueue<Void>()
|
|
||||||
|
|
||||||
/// Monitor for connectivity updates
|
|
||||||
let connectivityMonitor: ConnectivityMonitor
|
|
||||||
|
|
||||||
/// The serial queue on which status & message callbacks from the web socket are invoked.
|
|
||||||
private let serialCallbackQueue: DispatchQueue
|
|
||||||
|
|
||||||
/// Throttle when AppSync sends LimitExceeded error. High rate of subscriptions requests will cause AppSync to send
|
|
||||||
/// connection level LimitExceeded errors for each subscribe made. A connection level error means that there is no
|
|
||||||
/// subscription id associated with the error. When handling these errors, all subscriptions will receive a message
|
|
||||||
/// for the error. Use this subject to send and throttle the errors on the client side.
|
|
||||||
var limitExceededThrottleSink: Any?
|
|
||||||
var iLimitExceededSubject: Any?
|
|
||||||
var limitExceededSubject: PassthroughSubject<ConnectionProviderError, Never> {
|
|
||||||
if iLimitExceededSubject == nil {
|
|
||||||
iLimitExceededSubject = PassthroughSubject<ConnectionProviderError, Never>()
|
|
||||||
}
|
|
||||||
// swiftlint:disable:next force_cast
|
|
||||||
return iLimitExceededSubject as! PassthroughSubject<ConnectionProviderError, Never>
|
|
||||||
}
|
|
||||||
|
|
||||||
init(
|
|
||||||
urlRequest: URLRequest,
|
|
||||||
websocket: AppSyncWebsocketProvider,
|
|
||||||
|
|
||||||
serialCallbackQueue: DispatchQueue = DispatchQueue(
|
|
||||||
label: "com.amazonaws.AppSyncRealTimeConnectionProvider.callbackQueue"
|
|
||||||
),
|
|
||||||
connectivityMonitor: ConnectivityMonitor = ConnectivityMonitor()
|
|
||||||
) {
|
|
||||||
self.urlRequest = urlRequest
|
|
||||||
self.websocket = websocket
|
|
||||||
self.listeners = [:]
|
|
||||||
self.status = .notConnected
|
|
||||||
self.staleConnectionTimer = CountdownTimer()
|
|
||||||
self.isStaleConnection = false
|
|
||||||
self.serialCallbackQueue = serialCallbackQueue
|
|
||||||
self.connectivityMonitor = connectivityMonitor
|
|
||||||
|
|
||||||
connectivityMonitor.start(onUpdates: handleConnectivityUpdates(connectivity:))
|
|
||||||
subscribeToLimitExceededThrottle()
|
|
||||||
}
|
|
||||||
|
|
||||||
public convenience init(for urlRequest: URLRequest, websocket: AppSyncWebsocketProvider) {
|
|
||||||
self.init(urlRequest: urlRequest, websocket: websocket)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - ConnectionProvider methods
|
|
||||||
|
|
||||||
public func connect() {
|
|
||||||
taskQueue.async { [weak self] in
|
|
||||||
guard let self = self else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
guard self.status == .notConnected else {
|
|
||||||
self.updateCallback(event: .connection(self.status))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
guard let url = self.urlRequest.url else {
|
|
||||||
self.updateCallback(event: .error(ConnectionProviderError.unknown(
|
|
||||||
message: "Missing URL",
|
|
||||||
payload: nil
|
|
||||||
)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
self.status = .inProgress
|
|
||||||
self.updateCallback(event: .connection(self.status))
|
|
||||||
let request = AppSyncConnectionRequest(url: url)
|
|
||||||
let signedRequest = await self.interceptConnection(request, for: url)
|
|
||||||
var urlRequest = self.urlRequest
|
|
||||||
urlRequest.url = signedRequest.url
|
|
||||||
self.websocket.connect(
|
|
||||||
urlRequest: urlRequest,
|
|
||||||
protocols: ["graphql-ws"],
|
|
||||||
delegate: self
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func write(_ message: AppSyncMessage) {
|
|
||||||
taskQueue.async { [weak self] in
|
|
||||||
guard let self = self else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
guard let url = self.urlRequest.url else {
|
|
||||||
self.updateCallback(event: .error(ConnectionProviderError.unknown(
|
|
||||||
message: "Missing URL",
|
|
||||||
payload: nil
|
|
||||||
)))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
let signedMessage = await self.interceptMessage(message, for: url)
|
|
||||||
|
|
||||||
let jsonEncoder = JSONEncoder()
|
|
||||||
do {
|
|
||||||
let jsonData = try jsonEncoder.encode(signedMessage)
|
|
||||||
guard let jsonString = String(data: jsonData, encoding: .utf8) else {
|
|
||||||
let jsonError = ConnectionProviderError.jsonParse(signedMessage.id, nil)
|
|
||||||
self.updateCallback(event: .error(jsonError))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
self.websocket.write(message: jsonString)
|
|
||||||
} catch {
|
|
||||||
AppSyncLogger.error(error)
|
|
||||||
switch signedMessage.messageType {
|
|
||||||
case .connectionInit:
|
|
||||||
self.receivedConnectionInit()
|
|
||||||
default:
|
|
||||||
self.updateCallback(event: .error(ConnectionProviderError.jsonParse(signedMessage.id, error)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func disconnect() {
|
|
||||||
taskQueue.async {
|
|
||||||
self.websocket.disconnect()
|
|
||||||
self.invalidateStaleConnectionTimer()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func addListener(identifier: String, callback: @escaping ConnectionProviderCallback) {
|
|
||||||
taskQueue.async { [weak self] in
|
|
||||||
self?.listeners[identifier] = callback
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func removeListener(identifier: String) {
|
|
||||||
taskQueue.async { [weak self] in
|
|
||||||
guard let self = self else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
self.listeners.removeValue(forKey: identifier)
|
|
||||||
|
|
||||||
if self.listeners.isEmpty {
|
|
||||||
AppSyncLogger.debug(
|
|
||||||
"[RealtimeConnectionProvider] all subscriptions removed, disconnecting websocket connection."
|
|
||||||
)
|
|
||||||
self.status = .notConnected
|
|
||||||
self.websocket.disconnect()
|
|
||||||
self.invalidateStaleConnectionTimer()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: -
|
|
||||||
|
|
||||||
func sendConnectionInitMessage() {
|
|
||||||
let message = AppSyncMessage(type: .connectionInit("connection_init"))
|
|
||||||
write(message)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Invokes all registered listeners with `event`. The event is dispatched on `serialCallbackQueue`,
|
|
||||||
/// but internally this method uses the connectionQueue to get the currently registered listeners.
|
|
||||||
///
|
|
||||||
/// - Parameter event: The connection event to dispatch
|
|
||||||
func updateCallback(event: ConnectionProviderEvent) {
|
|
||||||
taskQueue.async { [weak self] in
|
|
||||||
guard let self = self else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
let allListeners = Array(self.listeners.values)
|
|
||||||
self.serialCallbackQueue.async {
|
|
||||||
allListeners.forEach { $0(event) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func subscribeToLimitExceededThrottle() {
|
|
||||||
limitExceededThrottleSink = limitExceededSubject
|
|
||||||
.filter {
|
|
||||||
// Make sure the limitExceeded error is a connection level error (no subscription id present).
|
|
||||||
// When id is present, it is passed back directly subscription via `updateCallback`.
|
|
||||||
if case .limitExceeded(_?) = $0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
.throttle(for: .milliseconds(150), scheduler: serialCallbackQueue, latest: true)
|
|
||||||
.sink { completion in
|
|
||||||
switch completion {
|
|
||||||
case .failure(let error):
|
|
||||||
AppSyncLogger.verbose("limitExceededThrottleSink failed \(error)")
|
|
||||||
case .finished:
|
|
||||||
AppSyncLogger.verbose("limitExceededThrottleSink finished")
|
|
||||||
}
|
|
||||||
} receiveValue: { result in
|
|
||||||
self.updateCallback(event: .error(result))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// - Warning: This must be invoked from the `taskQueue`
|
|
||||||
private func receivedConnectionInit() {
|
|
||||||
status = .notConnected
|
|
||||||
updateCallback(event: .error(ConnectionProviderError.connection))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2020 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2020 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
@ -23,9 +23,6 @@ public enum ConnectionProviderError: Error {
|
||||||
/// payload in dictionary format.
|
/// payload in dictionary format.
|
||||||
case subscription(String, [String: Any]?)
|
case subscription(String, [String: Any]?)
|
||||||
|
|
||||||
/// Caused when not authorized to establish the connection.
|
/// Any other error is identified by this type
|
||||||
case unauthorized
|
case other
|
||||||
|
|
||||||
/// Unknown error
|
|
||||||
case unknown(message: String? = nil, causedBy: Error? = nil, payload: [String: Any]?)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2021 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
@ -8,20 +8,19 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
/// Create connection providers to connect to the websocket endpoint of the AppSync endpoint.
|
/// Create connection providers to connect to the websocket endpoint of the AppSync endpoint.
|
||||||
public enum ConnectionProviderFactory {
|
public struct ConnectionProviderFactory {
|
||||||
|
|
||||||
public static func createConnectionProvider(
|
public static func createConnectionProvider(
|
||||||
for urlRequest: URLRequest,
|
for url: URL,
|
||||||
authInterceptor: AuthInterceptor,
|
authInterceptor: AuthInterceptor,
|
||||||
connectionType: SubscriptionConnectionType
|
connectionType: SubscriptionConnectionType,
|
||||||
|
overrideConnectionTimeoutInSeconds: Int?
|
||||||
) -> ConnectionProvider {
|
) -> ConnectionProvider {
|
||||||
let provider: ConnectionProvider
|
let provider = ConnectionProviderFactory.createConnectionProvider(
|
||||||
|
for: url,
|
||||||
switch connectionType {
|
connectionType: connectionType,
|
||||||
case .appSyncRealtime:
|
overrideConnectionTimeoutInSeconds: overrideConnectionTimeoutInSeconds
|
||||||
let websocketProvider = StarscreamAdapter()
|
)
|
||||||
provider = RealtimeConnectionProvider(for: urlRequest, websocket: websocketProvider)
|
|
||||||
}
|
|
||||||
|
|
||||||
if let messageInterceptable = provider as? MessageInterceptable {
|
if let messageInterceptable = provider as? MessageInterceptable {
|
||||||
messageInterceptable.addInterceptor(authInterceptor)
|
messageInterceptable.addInterceptor(authInterceptor)
|
||||||
|
@ -34,30 +33,20 @@ public enum ConnectionProviderFactory {
|
||||||
return provider
|
return provider
|
||||||
}
|
}
|
||||||
|
|
||||||
#if swift(>=5.5.2)
|
static func createConnectionProvider(
|
||||||
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
|
for url: URL,
|
||||||
public static func createConnectionProviderAsync(
|
connectionType: SubscriptionConnectionType,
|
||||||
for urlRequest: URLRequest,
|
overrideConnectionTimeoutInSeconds: Int?
|
||||||
authInterceptor: AuthInterceptorAsync,
|
|
||||||
connectionType: SubscriptionConnectionType
|
|
||||||
) -> ConnectionProvider {
|
) -> ConnectionProvider {
|
||||||
let provider: ConnectionProvider
|
|
||||||
|
|
||||||
switch connectionType {
|
switch connectionType {
|
||||||
case .appSyncRealtime:
|
case .appSyncRealtime:
|
||||||
let websocketProvider = StarscreamAdapter()
|
let websocketProvider = StarscreamAdapter()
|
||||||
provider = RealtimeConnectionProviderAsync(for: urlRequest, websocket: websocketProvider)
|
let connectionProvider = RealtimeConnectionProvider(
|
||||||
|
for: url,
|
||||||
|
websocket: websocketProvider,
|
||||||
|
overrideConnectionTimeoutInSeconds: overrideConnectionTimeoutInSeconds
|
||||||
|
)
|
||||||
|
return connectionProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
if let messageInterceptable = provider as? MessageInterceptableAsync {
|
|
||||||
messageInterceptable.addInterceptor(authInterceptor)
|
|
||||||
}
|
}
|
||||||
if let connectionInterceptable = provider as? ConnectionInterceptableAsync {
|
|
||||||
connectionInterceptable.addInterceptor(RealtimeGatewayURLInterceptor())
|
|
||||||
connectionInterceptable.addInterceptor(authInterceptor)
|
|
||||||
}
|
|
||||||
|
|
||||||
return provider
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,56 +0,0 @@
|
||||||
//
|
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
|
||||||
// All Rights Reserved.
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
public extension ConnectionInterceptable {
|
|
||||||
|
|
||||||
func interceptConnection(
|
|
||||||
_ request: AppSyncConnectionRequest,
|
|
||||||
for endpoint: URL,
|
|
||||||
completion: @escaping (AppSyncConnectionRequest) -> Void
|
|
||||||
) {
|
|
||||||
let result = interceptConnection(request, for: endpoint)
|
|
||||||
completion(result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public extension MessageInterceptable {
|
|
||||||
|
|
||||||
func interceptMessage(
|
|
||||||
_ message: AppSyncMessage,
|
|
||||||
for endpoint: URL,
|
|
||||||
completion: @escaping (AppSyncMessage) -> Void
|
|
||||||
) {
|
|
||||||
let result = interceptMessage(message, for: endpoint)
|
|
||||||
completion(result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public extension ConnectionInterceptor {
|
|
||||||
|
|
||||||
func interceptConnection(
|
|
||||||
_ request: AppSyncConnectionRequest,
|
|
||||||
for endpoint: URL,
|
|
||||||
completion: @escaping (AppSyncConnectionRequest) -> Void
|
|
||||||
) {
|
|
||||||
let result = interceptConnection(request, for: endpoint)
|
|
||||||
completion(result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public extension MessageInterceptor {
|
|
||||||
|
|
||||||
func interceptMessage(
|
|
||||||
_ message: AppSyncMessage,
|
|
||||||
for endpoint: URL,
|
|
||||||
completion: @escaping (AppSyncMessage) -> Void
|
|
||||||
) {
|
|
||||||
let result = interceptMessage(message, for: endpoint)
|
|
||||||
completion(result)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2020 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
@ -15,86 +15,24 @@ public protocol ConnectionInterceptable {
|
||||||
/// - Parameter interceptor: interceptor to be added
|
/// - Parameter interceptor: interceptor to be added
|
||||||
func addInterceptor(_ interceptor: ConnectionInterceptor)
|
func addInterceptor(_ interceptor: ConnectionInterceptor)
|
||||||
|
|
||||||
@available(
|
func interceptConnection(_ request: AppSyncConnectionRequest, for endpoint: URL) -> AppSyncConnectionRequest
|
||||||
*,
|
|
||||||
deprecated,
|
|
||||||
message:
|
|
||||||
"""
|
|
||||||
Use the async version under ConnectionInterceptableAsync or completion handler flavor
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
func interceptConnection(
|
|
||||||
_ request: AppSyncConnectionRequest,
|
|
||||||
for endpoint: URL
|
|
||||||
) -> AppSyncConnectionRequest
|
|
||||||
|
|
||||||
func interceptConnection(
|
|
||||||
_ request: AppSyncConnectionRequest,
|
|
||||||
for endpoint: URL,
|
|
||||||
completion: @escaping (AppSyncConnectionRequest) -> Void
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public protocol MessageInterceptable {
|
public protocol MessageInterceptable {
|
||||||
|
|
||||||
func addInterceptor(_ interceptor: MessageInterceptor)
|
func addInterceptor(_ interceptor: MessageInterceptor)
|
||||||
|
|
||||||
@available(
|
|
||||||
*,
|
|
||||||
deprecated,
|
|
||||||
message:
|
|
||||||
"""
|
|
||||||
Use the async version under MessageInterceptableAsync or completion handler flavor
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
func interceptMessage(_ message: AppSyncMessage, for endpoint: URL) -> AppSyncMessage
|
func interceptMessage(_ message: AppSyncMessage, for endpoint: URL) -> AppSyncMessage
|
||||||
|
|
||||||
func interceptMessage(
|
|
||||||
_ message: AppSyncMessage,
|
|
||||||
for endpoint: URL,
|
|
||||||
completion: @escaping (AppSyncMessage) -> Void
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public protocol ConnectionInterceptor {
|
public protocol ConnectionInterceptor {
|
||||||
|
|
||||||
@available(
|
func interceptConnection(_ request: AppSyncConnectionRequest, for endpoint: URL) -> AppSyncConnectionRequest
|
||||||
*,
|
|
||||||
deprecated,
|
|
||||||
message:
|
|
||||||
"""
|
|
||||||
Use the async version under ConnectionInterceptorAsync or completion handler flavor
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
func interceptConnection(
|
|
||||||
_ request: AppSyncConnectionRequest,
|
|
||||||
for endpoint: URL
|
|
||||||
) -> AppSyncConnectionRequest
|
|
||||||
|
|
||||||
func interceptConnection(
|
|
||||||
_ request: AppSyncConnectionRequest,
|
|
||||||
for endpoint: URL,
|
|
||||||
completion: @escaping (AppSyncConnectionRequest) -> Void
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public protocol MessageInterceptor {
|
public protocol MessageInterceptor {
|
||||||
|
|
||||||
@available(
|
|
||||||
*,
|
|
||||||
deprecated,
|
|
||||||
message:
|
|
||||||
"""
|
|
||||||
Use the async version under MessageInterceptorAsync or completion handler flavor
|
|
||||||
"""
|
|
||||||
)
|
|
||||||
func interceptMessage(_ message: AppSyncMessage, for endpoint: URL) -> AppSyncMessage
|
func interceptMessage(_ message: AppSyncMessage, for endpoint: URL) -> AppSyncMessage
|
||||||
|
|
||||||
func interceptMessage(
|
|
||||||
_ message: AppSyncMessage,
|
|
||||||
for endpoint: URL,
|
|
||||||
completion: @escaping (AppSyncMessage) -> Void
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public protocol AuthInterceptor: MessageInterceptor, ConnectionInterceptor {}
|
public protocol AuthInterceptor: MessageInterceptor, ConnectionInterceptor {}
|
||||||
|
|
|
@ -1,46 +0,0 @@
|
||||||
//
|
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
|
||||||
// All Rights Reserved.
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
|
|
||||||
public protocol ConnectionInterceptableAsync {
|
|
||||||
#if swift(>=5.5.2)
|
|
||||||
/// Add a new interceptor to the object.
|
|
||||||
///
|
|
||||||
/// - Parameter interceptor: interceptor to be added
|
|
||||||
func addInterceptor(_ interceptor: ConnectionInterceptorAsync)
|
|
||||||
|
|
||||||
func interceptConnection(_ request: AppSyncConnectionRequest, for endpoint: URL) async -> AppSyncConnectionRequest
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
|
|
||||||
public protocol MessageInterceptableAsync {
|
|
||||||
#if swift(>=5.5.2)
|
|
||||||
func addInterceptor(_ interceptor: MessageInterceptorAsync)
|
|
||||||
|
|
||||||
func interceptMessage(_ message: AppSyncMessage, for endpoint: URL) async -> AppSyncMessage
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
|
|
||||||
public protocol ConnectionInterceptorAsync {
|
|
||||||
#if swift(>=5.5.2)
|
|
||||||
func interceptConnection(_ request: AppSyncConnectionRequest, for endpoint: URL) async -> AppSyncConnectionRequest
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
|
|
||||||
public protocol MessageInterceptorAsync {
|
|
||||||
#if swift(>=5.5.2)
|
|
||||||
func interceptMessage(_ message: AppSyncMessage, for endpoint: URL) async -> AppSyncMessage
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
|
|
||||||
public protocol AuthInterceptorAsync: MessageInterceptorAsync, ConnectionInterceptorAsync {}
|
|
|
@ -1,93 +0,0 @@
|
||||||
//
|
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
|
||||||
// All Rights Reserved.
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import Network
|
|
||||||
|
|
||||||
typealias ConnectivityUpdates = (ConnectivityPath) -> Void
|
|
||||||
|
|
||||||
protocol AnyConnectivityMonitor {
|
|
||||||
func start(connectivityUpdatesQueue: DispatchQueue, onConnectivityUpdates: @escaping ConnectivityUpdates)
|
|
||||||
func cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
class ConnectivityMonitor {
|
|
||||||
|
|
||||||
private let connectivityUpdatesQueue = DispatchQueue(
|
|
||||||
label: "com.amazonaws.ConnectivityMonitor.connectivityUpdatesQueue",
|
|
||||||
qos: .background
|
|
||||||
)
|
|
||||||
private var monitor: AnyConnectivityMonitor?
|
|
||||||
|
|
||||||
init(monitor: AnyConnectivityMonitor? = nil) {
|
|
||||||
self.monitor = monitor
|
|
||||||
}
|
|
||||||
|
|
||||||
func start(onUpdates: @escaping ConnectivityUpdates) {
|
|
||||||
if let monitor = monitor {
|
|
||||||
monitor.start(
|
|
||||||
connectivityUpdatesQueue: connectivityUpdatesQueue,
|
|
||||||
onConnectivityUpdates: onUpdates
|
|
||||||
)
|
|
||||||
} else if #available(iOS 12.0, macOS 10.14, tvOS 12.0, watchOS 6.0, *) {
|
|
||||||
let monitor = NetworkMonitor()
|
|
||||||
self.monitor = monitor
|
|
||||||
monitor.start(
|
|
||||||
connectivityUpdatesQueue: connectivityUpdatesQueue,
|
|
||||||
onConnectivityUpdates: onUpdates
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func cancel() {
|
|
||||||
guard let monitor = monitor else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
monitor.cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
deinit {
|
|
||||||
cancel()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@available(iOS 12.0, macOS 10.14, tvOS 12.0, watchOS 6.0, *)
|
|
||||||
class NetworkMonitor: AnyConnectivityMonitor {
|
|
||||||
private var monitor: NWPathMonitor?
|
|
||||||
private var onConnectivityUpdates: ConnectivityUpdates?
|
|
||||||
private var connectivityUpdatesQueue: DispatchQueue?
|
|
||||||
private let queue = DispatchQueue(label: "com.amazonaws.NetworkMonitor.queue", qos: .background)
|
|
||||||
|
|
||||||
func start(connectivityUpdatesQueue: DispatchQueue, onConnectivityUpdates: @escaping ConnectivityUpdates) {
|
|
||||||
self.connectivityUpdatesQueue = connectivityUpdatesQueue
|
|
||||||
self.onConnectivityUpdates = onConnectivityUpdates
|
|
||||||
// A new instance is required each time a monitor is started
|
|
||||||
let monitor = NWPathMonitor()
|
|
||||||
monitor.pathUpdateHandler = didUpdate(path:)
|
|
||||||
monitor.start(queue: queue)
|
|
||||||
self.monitor = monitor
|
|
||||||
}
|
|
||||||
|
|
||||||
func cancel() {
|
|
||||||
guard let monitor = monitor else { return }
|
|
||||||
defer {
|
|
||||||
self.monitor = nil
|
|
||||||
}
|
|
||||||
monitor.cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
func didUpdate(path: NWPath) {
|
|
||||||
guard let onConnectivityUpdates = onConnectivityUpdates,
|
|
||||||
let connectivityUpdatesQueue = connectivityUpdatesQueue else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
let connectivityPath = ConnectivityPath(path: path)
|
|
||||||
connectivityUpdatesQueue.async {
|
|
||||||
onConnectivityUpdates(connectivityPath)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,125 +0,0 @@
|
||||||
//
|
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
|
||||||
// All Rights Reserved.
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import Network
|
|
||||||
|
|
||||||
struct ConnectivityPath {
|
|
||||||
let status: ConnectivityStatus
|
|
||||||
let availableInterfaces: [ConnectivityInterface]
|
|
||||||
let isExpensive: Bool
|
|
||||||
let supportsDNS: Bool
|
|
||||||
let supportsIPv4: Bool
|
|
||||||
let supportsIPv6: Bool
|
|
||||||
|
|
||||||
init(
|
|
||||||
status: ConnectivityStatus = .unsatisfied,
|
|
||||||
availableInterfaces: [ConnectivityInterface] = [],
|
|
||||||
isExpensive: Bool = false,
|
|
||||||
supportsDNS: Bool = false,
|
|
||||||
supportsIPv4: Bool = false,
|
|
||||||
supportsIPv6: Bool = false
|
|
||||||
) {
|
|
||||||
self.status = status
|
|
||||||
self.availableInterfaces = availableInterfaces
|
|
||||||
self.isExpensive = isExpensive
|
|
||||||
self.supportsDNS = supportsDNS
|
|
||||||
self.supportsIPv4 = supportsIPv4
|
|
||||||
self.supportsIPv6 = supportsIPv6
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ConnectivityPath: CustomStringConvertible {
|
|
||||||
var description: String {
|
|
||||||
[
|
|
||||||
"\(status): \(availableInterfaces.description)",
|
|
||||||
"Expensive = \(isExpensive ? "YES" : "NO")",
|
|
||||||
"DNS = \(supportsDNS ? "YES" : "NO")",
|
|
||||||
"IPv4 = \(supportsIPv4 ? "YES" : "NO")",
|
|
||||||
"IPv6 = \(supportsIPv6 ? "YES" : "NO")"
|
|
||||||
].joined(separator: "; ")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ConnectivityPath {
|
|
||||||
@available(iOS 12.0, macOS 10.14, tvOS 12.0, watchOS 6.0, *)
|
|
||||||
init(path: NWPath) {
|
|
||||||
self.status = ConnectivityStatus(status: path.status)
|
|
||||||
self.availableInterfaces = path.availableInterfaces.map { ConnectivityInterface(interface: $0) }
|
|
||||||
self.isExpensive = path.isExpensive
|
|
||||||
self.supportsDNS = path.supportsDNS
|
|
||||||
self.supportsIPv4 = path.supportsIPv4
|
|
||||||
self.supportsIPv6 = path.supportsIPv6
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum ConnectivityInterfaceType: String {
|
|
||||||
case other
|
|
||||||
case wifi
|
|
||||||
case cellular
|
|
||||||
case wiredEthernet
|
|
||||||
case loopback
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ConnectivityInterfaceType {
|
|
||||||
@available(iOS 12.0, macOS 10.14, tvOS 12.0, watchOS 6.0, *)
|
|
||||||
init(interfaceType: NWInterface.InterfaceType) {
|
|
||||||
switch interfaceType {
|
|
||||||
case .other:
|
|
||||||
self = .other
|
|
||||||
case .wifi:
|
|
||||||
self = .wifi
|
|
||||||
case .cellular:
|
|
||||||
self = .cellular
|
|
||||||
case .wiredEthernet:
|
|
||||||
self = .wiredEthernet
|
|
||||||
case .loopback:
|
|
||||||
self = .loopback
|
|
||||||
@unknown default:
|
|
||||||
self = .other
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ConnectivityInterface {
|
|
||||||
public let name: String
|
|
||||||
public let type: ConnectivityInterfaceType
|
|
||||||
|
|
||||||
public init(name: String, type: ConnectivityInterfaceType) {
|
|
||||||
self.name = name
|
|
||||||
self.type = type
|
|
||||||
}
|
|
||||||
}
|
|
||||||
extension ConnectivityInterface {
|
|
||||||
@available(iOS 12.0, macOS 10.14, tvOS 12.0, watchOS 6.0, *)
|
|
||||||
init(interface: NWInterface) {
|
|
||||||
self.name = interface.name
|
|
||||||
self.type = ConnectivityInterfaceType(interfaceType: interface.type)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum ConnectivityStatus: String {
|
|
||||||
case satisfied
|
|
||||||
case unsatisfied
|
|
||||||
case requiresConnection
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ConnectivityStatus {
|
|
||||||
@available(iOS 12.0, macOS 10.14, tvOS 12.0, watchOS 6.0, *)
|
|
||||||
init(status: NWPath.Status) {
|
|
||||||
switch status {
|
|
||||||
case .satisfied:
|
|
||||||
self = .satisfied
|
|
||||||
case .unsatisfied:
|
|
||||||
self = .unsatisfied
|
|
||||||
case .requiresConnection:
|
|
||||||
self = .requiresConnection
|
|
||||||
@unknown default:
|
|
||||||
self = .unsatisfied
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -15,7 +15,7 @@
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>3.1.0</string>
|
<string>1.5.0</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||||
</dict>
|
</dict>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2021 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
@ -8,7 +8,7 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
/// Auth interceptor for API Key based authentication
|
/// Auth interceptor for API Key based authentication
|
||||||
public class APIKeyAuthInterceptor: AuthInterceptor, AuthInterceptorAsync {
|
public class APIKeyAuthInterceptor: AuthInterceptor {
|
||||||
|
|
||||||
let apiKey: String
|
let apiKey: String
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2021 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
@ -15,90 +15,44 @@ public class OIDCAuthInterceptor: AuthInterceptor {
|
||||||
self.authProvider = authProvider
|
self.authProvider = authProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
public func interceptMessage(
|
public func interceptMessage(_ message: AppSyncMessage, for endpoint: URL) -> AppSyncMessage {
|
||||||
_ message: AppSyncMessage,
|
let host = endpoint.host!
|
||||||
for endpoint: URL,
|
|
||||||
completion: @escaping (AppSyncMessage) -> Void
|
|
||||||
) {
|
|
||||||
guard let host = endpoint.host else {
|
|
||||||
completion(message)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
guard case .subscribe = message.messageType else {
|
|
||||||
completion(message)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
authProvider.getLatestAuthToken { result in
|
|
||||||
let signedMessage = self.signMessage(message, with: result, host: host)
|
|
||||||
completion(signedMessage)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public func interceptConnection(
|
|
||||||
_ request: AppSyncConnectionRequest,
|
|
||||||
for endpoint: URL,
|
|
||||||
completion: @escaping (AppSyncConnectionRequest) -> Void
|
|
||||||
) {
|
|
||||||
|
|
||||||
guard let host = endpoint.host else {
|
|
||||||
completion(request)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
authProvider.getLatestAuthToken { result in
|
|
||||||
let signedRequest = self.signRequest(request, with: result, host: host)
|
|
||||||
completion(signedRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func signMessage(
|
|
||||||
_ message: AppSyncMessage,
|
|
||||||
with tokenResult: Result<String, Error>,
|
|
||||||
host: String
|
|
||||||
) -> AppSyncMessage {
|
|
||||||
let jwtToken: String
|
let jwtToken: String
|
||||||
switch tokenResult {
|
switch authProvider.getLatestAuthToken() {
|
||||||
case .success(let token):
|
case .success(let token):
|
||||||
jwtToken = token
|
jwtToken = token
|
||||||
case .failure:
|
case .failure:
|
||||||
return message
|
return message
|
||||||
}
|
}
|
||||||
|
switch message.messageType {
|
||||||
|
case .subscribe:
|
||||||
let authHeader = UserPoolsAuthenticationHeader(token: jwtToken, host: host)
|
let authHeader = UserPoolsAuthenticationHeader(token: jwtToken, host: host)
|
||||||
var payload = message.payload ?? AppSyncMessage.Payload()
|
var payload = message.payload ?? AppSyncMessage.Payload()
|
||||||
payload.authHeader = authHeader
|
payload.authHeader = authHeader
|
||||||
|
|
||||||
return AppSyncMessage(
|
let signedMessage = AppSyncMessage(
|
||||||
id: message.id,
|
id: message.id,
|
||||||
payload: payload,
|
payload: payload,
|
||||||
type: message.messageType
|
type: message.messageType
|
||||||
)
|
)
|
||||||
|
return signedMessage
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return message
|
||||||
}
|
}
|
||||||
|
|
||||||
private func signRequest(
|
public func interceptConnection(
|
||||||
_ request: AppSyncConnectionRequest,
|
_ request: AppSyncConnectionRequest,
|
||||||
with tokenResult: Result<String, Error>,
|
for endpoint: URL
|
||||||
host: String
|
|
||||||
) -> AppSyncConnectionRequest {
|
) -> AppSyncConnectionRequest {
|
||||||
|
let host = endpoint.host!
|
||||||
let jwtToken: String
|
let jwtToken: String
|
||||||
switch tokenResult {
|
switch authProvider.getLatestAuthToken() {
|
||||||
case .success(let token):
|
case .success(let token):
|
||||||
jwtToken = token
|
jwtToken = token
|
||||||
case .failure:
|
case .failure:
|
||||||
// A user that is not signed in should receive an unauthorized error from
|
return request
|
||||||
// the connection attempt. This code achieves this by always creating a valid
|
|
||||||
// request to AppSync even when the token cannot be retrieved. The request sent
|
|
||||||
// to AppSync will receive a response indicating the request is unauthorized.
|
|
||||||
// If we do not use empty token string and perform the remaining logic of the
|
|
||||||
// request construction then it will fail request validation at AppSync before
|
|
||||||
// the authorization check, which ends up being propagated back to the caller
|
|
||||||
// as a "bad request". Example of bad requests are when the header and payload
|
|
||||||
// query strings are missing or when the data is not base64 encoded.
|
|
||||||
jwtToken = ""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let authHeader = UserPoolsAuthenticationHeader(token: jwtToken, host: host)
|
let authHeader = UserPoolsAuthenticationHeader(token: jwtToken, host: host)
|
||||||
|
@ -119,40 +73,6 @@ public class OIDCAuthInterceptor: AuthInterceptor {
|
||||||
let signedRequest = AppSyncConnectionRequest(url: url)
|
let signedRequest = AppSyncConnectionRequest(url: url)
|
||||||
return signedRequest
|
return signedRequest
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Deprecated methods
|
|
||||||
// Keeping these methods for backward compatibility
|
|
||||||
|
|
||||||
public func interceptMessage(
|
|
||||||
_ message: AppSyncMessage,
|
|
||||||
for endpoint: URL
|
|
||||||
) -> AppSyncMessage {
|
|
||||||
|
|
||||||
guard let host = endpoint.host else {
|
|
||||||
return message
|
|
||||||
}
|
|
||||||
|
|
||||||
guard case .subscribe = message.messageType else {
|
|
||||||
return message
|
|
||||||
}
|
|
||||||
let result = authProvider.getLatestAuthToken()
|
|
||||||
let signedMessage = signMessage(message, with: result, host: host)
|
|
||||||
return signedMessage
|
|
||||||
}
|
|
||||||
|
|
||||||
public func interceptConnection(
|
|
||||||
_ request: AppSyncConnectionRequest,
|
|
||||||
for endpoint: URL
|
|
||||||
) -> AppSyncConnectionRequest {
|
|
||||||
|
|
||||||
guard let host = endpoint.host else {
|
|
||||||
return request
|
|
||||||
}
|
|
||||||
|
|
||||||
let result = authProvider.getLatestAuthToken()
|
|
||||||
let signedRequest = signRequest(request, with: result, host: host)
|
|
||||||
return signedRequest
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Authentication header for user pool based auth
|
/// Authentication header for user pool based auth
|
||||||
|
|
|
@ -1,105 +0,0 @@
|
||||||
//
|
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
|
||||||
// All Rights Reserved.
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
//
|
|
||||||
|
|
||||||
#if swift(>=5.5.2)
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
|
|
||||||
public class OIDCAuthInterceptorAsync: AuthInterceptorAsync {
|
|
||||||
|
|
||||||
let authProvider: OIDCAuthProviderAsync
|
|
||||||
|
|
||||||
public init(_ authProvider: OIDCAuthProviderAsync) {
|
|
||||||
self.authProvider = authProvider
|
|
||||||
}
|
|
||||||
|
|
||||||
public func interceptMessage(_ message: AppSyncMessage, for endpoint: URL) async -> AppSyncMessage {
|
|
||||||
let host = endpoint.host!
|
|
||||||
let jwtToken: String
|
|
||||||
do {
|
|
||||||
jwtToken = try await authProvider.getLatestAuthToken()
|
|
||||||
} catch {
|
|
||||||
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:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
return message
|
|
||||||
}
|
|
||||||
|
|
||||||
public func interceptConnection(
|
|
||||||
_ request: AppSyncConnectionRequest,
|
|
||||||
for endpoint: URL
|
|
||||||
) async -> AppSyncConnectionRequest {
|
|
||||||
let host = endpoint.host!
|
|
||||||
let jwtToken: String
|
|
||||||
do {
|
|
||||||
jwtToken = try await authProvider.getLatestAuthToken()
|
|
||||||
} catch {
|
|
||||||
// A user that is not signed in should receive an unauthorized error from the connection attempt. This code
|
|
||||||
// achieves this by always creating a valid request to AppSync even when the token cannot be retrieved. The
|
|
||||||
// request sent to AppSync will receive a response indicating the request is unauthorized. If we do not use
|
|
||||||
// empty token string and perform the remaining logic of the request construction then it will fail request
|
|
||||||
// validation at AppSync before the authorization check, which ends up being propagated back to the caller
|
|
||||||
// as a "bad request". Example of bad requests are when the header and payload query strings are missing
|
|
||||||
// or when the data is not base64 encoded.
|
|
||||||
jwtToken = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2020 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
@ -8,7 +8,7 @@
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
/// Connection interceptor for real time connection provider
|
/// Connection interceptor for real time connection provider
|
||||||
public class RealtimeGatewayURLInterceptor: ConnectionInterceptor, ConnectionInterceptorAsync {
|
public class RealtimeGatewayURLInterceptor: ConnectionInterceptor {
|
||||||
public init() {
|
public init() {
|
||||||
// Do nothing
|
// Do nothing
|
||||||
}
|
}
|
||||||
|
@ -23,18 +23,11 @@ public class RealtimeGatewayURLInterceptor: ConnectionInterceptor, ConnectionInt
|
||||||
guard var urlComponents = URLComponents(url: request.url, resolvingAgainstBaseURL: false) else {
|
guard var urlComponents = URLComponents(url: request.url, resolvingAgainstBaseURL: false) else {
|
||||||
return request
|
return request
|
||||||
}
|
}
|
||||||
|
|
||||||
urlComponents.scheme = SubscriptionConstants.realtimeWebsocketScheme
|
urlComponents.scheme = SubscriptionConstants.realtimeWebsocketScheme
|
||||||
if AppSyncURLHelper.isStandardAppSyncGraphQLEndpoint(url: endpoint) {
|
|
||||||
urlComponents.host = host.replacingOccurrences(
|
urlComponents.host = host.replacingOccurrences(
|
||||||
of: SubscriptionConstants.appsyncHostPart,
|
of: SubscriptionConstants.appsyncHostPart,
|
||||||
with: SubscriptionConstants.appsyncRealtimeHostPart
|
with: SubscriptionConstants.appsyncRealtimeHostPart
|
||||||
)
|
)
|
||||||
} else {
|
|
||||||
// else this is a custom domain such that the host remains untouched and "/realtime" path is added
|
|
||||||
urlComponents.path.append(contentsOf: "/" + SubscriptionConstants.appsyncCustomDomainRealtimePath)
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let url = urlComponents.url else {
|
guard let url = urlComponents.url else {
|
||||||
return request
|
return request
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +1,19 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2021 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public enum AppSyncJSONHelper {
|
public struct AppSyncJSONHelper {
|
||||||
|
|
||||||
public static func base64AuthenticationBlob(_ header: AuthenticationHeader ) -> String {
|
public static func base64AuthenticationBlob(_ header: AuthenticationHeader ) -> String {
|
||||||
let jsonEncoder = JSONEncoder()
|
let jsonEncoder = JSONEncoder()
|
||||||
do {
|
do {
|
||||||
let jsonHeader = try jsonEncoder.encode(header)
|
let jsonHeader = try jsonEncoder.encode(header)
|
||||||
AppSyncLogger.verbose(
|
AppSyncLogger.verbose("Generated Header for request - \(String(describing: String(data: jsonHeader, encoding: .utf8)))")
|
||||||
"Generated Header for request - \(String(describing: String(data: jsonHeader, encoding: .utf8)))"
|
|
||||||
)
|
|
||||||
return jsonHeader.base64EncodedString()
|
return jsonHeader.base64EncodedString()
|
||||||
} catch {
|
} catch {
|
||||||
AppSyncLogger.error(error.localizedDescription)
|
AppSyncLogger.error(error.localizedDescription)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2020 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2020 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
@ -10,58 +10,16 @@ import os
|
||||||
|
|
||||||
struct AppSyncLogger {
|
struct AppSyncLogger {
|
||||||
|
|
||||||
static var logLevel: AppSyncRealTimeClient.LogLevel {
|
|
||||||
AppSyncRealTimeClient.logLevel
|
|
||||||
}
|
|
||||||
|
|
||||||
// iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *
|
|
||||||
static func error(_ log: String) {
|
static func error(_ log: String) {
|
||||||
// Always logged, no conditional check needed
|
if #available(iOS 10.0, *) {
|
||||||
if #available(iOS 10.0, macOS 10.12, tvOS 10.0, watchOS 3.0, *) {
|
|
||||||
os_log("%@", type: .error, log)
|
os_log("%@", type: .error, log)
|
||||||
} else {
|
} else {
|
||||||
NSLog("%@", log)
|
NSLog("%@", log)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static func error(_ error: Error) {
|
|
||||||
if #available(iOS 10.0, macOS 10.12, tvOS 10.0, watchOS 3.0, *) {
|
|
||||||
os_log("%@", type: .error, error.localizedDescription)
|
|
||||||
} else {
|
|
||||||
NSLog("%@", error.localizedDescription)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static func warn(_ log: String) {
|
|
||||||
guard logLevel.rawValue >= AppSyncRealTimeClient.LogLevel.warn.rawValue else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if #available(iOS 10.0, macOS 10.12, tvOS 10.0, watchOS 3.0, *) {
|
|
||||||
os_log("%@", type: .info, log)
|
|
||||||
} else {
|
|
||||||
NSLog("%@", log)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static func info(_ log: String) {
|
|
||||||
guard logLevel.rawValue >= AppSyncRealTimeClient.LogLevel.info.rawValue else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if #available(iOS 10.0, macOS 10.12, tvOS 10.0, watchOS 3.0, *) {
|
|
||||||
os_log("%@", type: .info, log)
|
|
||||||
} else {
|
|
||||||
NSLog("%@", log)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static func debug(_ log: String) {
|
static func debug(_ log: String) {
|
||||||
guard logLevel.rawValue >= AppSyncRealTimeClient.LogLevel.debug.rawValue else {
|
if #available(iOS 10.0, *) {
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if #available(iOS 10.0, macOS 10.12, tvOS 10.0, watchOS 3.0, *) {
|
|
||||||
os_log("%@", type: .debug, log)
|
os_log("%@", type: .debug, log)
|
||||||
} else {
|
} else {
|
||||||
NSLog("%@", log)
|
NSLog("%@", log)
|
||||||
|
@ -69,14 +27,34 @@ struct AppSyncLogger {
|
||||||
}
|
}
|
||||||
|
|
||||||
static func verbose(_ log: String) {
|
static func verbose(_ log: String) {
|
||||||
guard logLevel.rawValue >= AppSyncRealTimeClient.LogLevel.verbose.rawValue else {
|
if #available(iOS 10.0, *) {
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if #available(iOS 10.0, macOS 10.12, tvOS 10.0, watchOS 3.0, *) {
|
|
||||||
os_log("%@", type: .debug, log)
|
os_log("%@", type: .debug, log)
|
||||||
} else {
|
} else {
|
||||||
NSLog("%@", log)
|
NSLog("%@", log)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static func info(_ log: String) {
|
||||||
|
if #available(iOS 10.0, *) {
|
||||||
|
os_log("%@", type: .info, log)
|
||||||
|
} else {
|
||||||
|
NSLog("%@", log)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static func warn(_ log: String) {
|
||||||
|
if #available(iOS 10.0, *) {
|
||||||
|
os_log("%@", type: .info, log)
|
||||||
|
} else {
|
||||||
|
NSLog("%@", log)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static func error(_ error: Error) {
|
||||||
|
if #available(iOS 10.0, *) {
|
||||||
|
os_log("%@", type: .error, error.localizedDescription)
|
||||||
|
} else {
|
||||||
|
NSLog("%@", error.localizedDescription)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
//
|
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
|
||||||
// All Rights Reserved.
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
public enum AppSyncURLHelper {
|
|
||||||
|
|
||||||
// A standard AppSync URL has the format of
|
|
||||||
// https://[DOMAIN].appsync-api.[REGION].amazonaws.com/graphql
|
|
||||||
// The regex `\w{26}` is used to check that the DOMAIN is 26 alphanumeric characters long.
|
|
||||||
// The regex `\w{2}(?:(?:-\w{2,})+)-\d` is used to check that the REGION matches the pattern
|
|
||||||
// {2letterword}{atleast one instance of pattern {{-}{word with atleast 2 letters}}}{-}{single digit}.
|
|
||||||
// for example, "us-west-1'
|
|
||||||
// AppSync endpoints reference : https://docs.aws.amazon.com/general/latest/gr/appsync.html
|
|
||||||
public static let standardDomainPattern =
|
|
||||||
"^https://\\w{26}.appsync-api.\\w{2}(?:(?:-\\w{2,})+)-\\d.amazonaws.com/graphql$"
|
|
||||||
|
|
||||||
// Check whether the provided GraphQL endpoint has standard appsync domain
|
|
||||||
public static func isStandardAppSyncGraphQLEndpoint(url: URL) -> Bool {
|
|
||||||
return url.absoluteString.range(
|
|
||||||
of: standardDomainPattern,
|
|
||||||
options: [
|
|
||||||
.regularExpression,
|
|
||||||
.caseInsensitive
|
|
||||||
],
|
|
||||||
range: nil,
|
|
||||||
locale: nil
|
|
||||||
) != nil
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2020 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2021 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
@ -14,54 +14,31 @@ import Foundation
|
||||||
/// includes work that must be performed on a specific queue, make sure to dispatch
|
/// includes work that must be performed on a specific queue, make sure to dispatch
|
||||||
/// it inside the closure.
|
/// it inside the closure.
|
||||||
class CountdownTimer {
|
class CountdownTimer {
|
||||||
private static let defaultInterval: TimeInterval = 5 * 60
|
/// The interval after which the timer will fire
|
||||||
|
let interval: TimeInterval
|
||||||
|
|
||||||
/// The interval in seconds of the timer
|
|
||||||
private var _interval: TimeInterval?
|
|
||||||
private let lock: NSLock
|
private let lock: NSLock
|
||||||
private var workItem: DispatchWorkItem?
|
private var workItem: DispatchWorkItem?
|
||||||
private var onCountdownComplete: (() -> Void)?
|
private let onCountdownComplete: () -> Void
|
||||||
|
|
||||||
init() {
|
init(interval: TimeInterval, onCountdownComplete: @escaping () -> Void) {
|
||||||
self.lock = NSLock()
|
self.lock = NSLock()
|
||||||
}
|
self.interval = interval
|
||||||
|
|
||||||
var interval: TimeInterval {
|
|
||||||
_interval ?? CountdownTimer.defaultInterval
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Starts the countdown of the timer with `interval` and perform
|
|
||||||
///
|
|
||||||
/// - Parameters:
|
|
||||||
/// - interval: The interval after which the timer will fire, and be reset on.
|
|
||||||
/// - onCountdownComplete: The closure to perform when the timer fires.
|
|
||||||
func start(interval: TimeInterval, onCountdownComplete: @escaping () -> Void) {
|
|
||||||
lock.lock()
|
|
||||||
defer {
|
|
||||||
lock.unlock()
|
|
||||||
}
|
|
||||||
_interval = interval
|
|
||||||
self.onCountdownComplete = onCountdownComplete
|
self.onCountdownComplete = onCountdownComplete
|
||||||
cancelAndClearWorkItem()
|
createAndScheduleTimer()
|
||||||
createAndScheduleTimer(interval: interval)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resets the timer to begin counting down from the `interval` again.
|
/// Resets the countdown of the timer to `interval`
|
||||||
///
|
func resetCountdown() {
|
||||||
/// - Parameter interval: Optionally pass in a new interval for the timer.
|
|
||||||
func reset(interval: TimeInterval? = nil) {
|
|
||||||
lock.lock()
|
lock.lock()
|
||||||
defer {
|
defer {
|
||||||
lock.unlock()
|
lock.unlock()
|
||||||
}
|
}
|
||||||
if let interval = interval {
|
|
||||||
_interval = interval
|
|
||||||
}
|
|
||||||
cancelAndClearWorkItem()
|
cancelAndClearWorkItem()
|
||||||
createAndScheduleTimer(interval: self.interval)
|
createAndScheduleTimer()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Invalidates/stops the timer
|
/// Invalidates the timer
|
||||||
func invalidate() {
|
func invalidate() {
|
||||||
lock.lock()
|
lock.lock()
|
||||||
defer {
|
defer {
|
||||||
|
@ -70,16 +47,9 @@ class CountdownTimer {
|
||||||
cancelAndClearWorkItem()
|
cancelAndClearWorkItem()
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Private helpers
|
|
||||||
|
|
||||||
/// Invoked by all puclic methods (`start`, `reset`, `invalidate`) to clear the previous timer
|
|
||||||
private func cancelAndClearWorkItem() {
|
private func cancelAndClearWorkItem() {
|
||||||
guard let workItem = workItem else {
|
workItem?.cancel()
|
||||||
return
|
workItem = nil
|
||||||
}
|
|
||||||
|
|
||||||
workItem.cancel()
|
|
||||||
self.workItem = nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Invoked by the timer. Do not execute this method directly.
|
/// Invoked by the timer. Do not execute this method directly.
|
||||||
|
@ -94,13 +64,13 @@ class CountdownTimer {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
onCountdownComplete?()
|
onCountdownComplete()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Invoked by `start` and `reset` when creating a new timer.
|
private func createAndScheduleTimer() {
|
||||||
private func createAndScheduleTimer(interval: TimeInterval) {
|
|
||||||
let workItem = DispatchWorkItem { self.timerFired() }
|
let workItem = DispatchWorkItem { self.timerFired() }
|
||||||
self.workItem = workItem
|
self.workItem = workItem
|
||||||
DispatchQueue.global().asyncAfter(deadline: .now() + interval, execute: workItem)
|
DispatchQueue.global().asyncAfter(deadline: .now() + interval, execute: workItem)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,10 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2020 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
|
||||||
public protocol OIDCAuthProvider {
|
public protocol OIDCAuthProvider {
|
||||||
func getLatestAuthToken() -> Result<String, Error>
|
func getLatestAuthToken() -> Result<String, Error>
|
||||||
|
|
||||||
func getLatestAuthToken(completion: @escaping (Result<String, Error>) -> Void )
|
|
||||||
}
|
|
||||||
|
|
||||||
public extension OIDCAuthProvider {
|
|
||||||
|
|
||||||
func getLatestAuthToken(completion: @escaping (Result<String, Error>) -> Void) {
|
|
||||||
let result = getLatestAuthToken()
|
|
||||||
completion(result)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
//
|
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
|
||||||
// All Rights Reserved.
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
//
|
|
||||||
|
|
||||||
#if swift(>=5.5.2)
|
|
||||||
|
|
||||||
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
|
|
||||||
public protocol OIDCAuthProviderAsync {
|
|
||||||
func getLatestAuthToken() async throws -> String
|
|
||||||
}
|
|
||||||
#endif
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2020 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
|
|
@ -1,21 +1,19 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2021 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public enum SubscriptionConstants {
|
public struct SubscriptionConstants {
|
||||||
public static let appsyncHostPart = "appsync-api"
|
public static let appsyncHostPart = "appsync-api"
|
||||||
|
|
||||||
public static let appsyncRealtimeHostPart = "appsync-realtime-api"
|
public static let appsyncRealtimeHostPart = "appsync-realtime-api"
|
||||||
|
|
||||||
public static let realtimeWebsocketScheme = "wss"
|
public static let realtimeWebsocketScheme = "wss"
|
||||||
|
|
||||||
public static let appsyncCustomDomainRealtimePath = "realtime"
|
|
||||||
|
|
||||||
public static let emptyPayload = "{}"
|
public static let emptyPayload = "{}"
|
||||||
|
|
||||||
public static let appsyncServiceName = "appsync"
|
public static let appsyncServiceName = "appsync"
|
||||||
|
@ -23,7 +21,7 @@ public enum SubscriptionConstants {
|
||||||
public static let authorizationkey = "Authorization"
|
public static let authorizationkey = "Authorization"
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum RealtimeProviderConstants {
|
public struct RealtimeProviderConstants {
|
||||||
public static let header = "header"
|
public static let header = "header"
|
||||||
|
|
||||||
public static let payload = "payload"
|
public static let payload = "payload"
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
//
|
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
|
||||||
// All Rights Reserved.
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
//
|
|
||||||
|
|
||||||
#if swift(>=5.5.2)
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
|
|
||||||
actor TaskQueue<Success> {
|
|
||||||
private var previousTask: Task<Success, Error>?
|
|
||||||
|
|
||||||
func sync(block: @Sendable @escaping () async throws -> Success) async throws {
|
|
||||||
previousTask = Task { [previousTask] in
|
|
||||||
_ = await previousTask?.result
|
|
||||||
return try await block()
|
|
||||||
}
|
|
||||||
_ = try await previousTask?.value
|
|
||||||
}
|
|
||||||
|
|
||||||
nonisolated func async(block: @Sendable @escaping () async throws -> Success) rethrows {
|
|
||||||
Task {
|
|
||||||
try await sync(block: block)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2020 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
@ -14,7 +14,7 @@ public protocol AppSyncWebsocketProvider {
|
||||||
///
|
///
|
||||||
/// This is an async call. After the connection is succesfully established, the delegate
|
/// This is an async call. After the connection is succesfully established, the delegate
|
||||||
/// will receive the callback on `websocketDidConnect(:)`
|
/// will receive the callback on `websocketDidConnect(:)`
|
||||||
func connect(urlRequest: URLRequest, protocols: [String], delegate: AppSyncWebsocketDelegate?)
|
func connect(url: URL, protocols: [String], delegate: AppSyncWebsocketDelegate?)
|
||||||
|
|
||||||
/// Disconnects the websocket.
|
/// Disconnects the websocket.
|
||||||
func disconnect()
|
func disconnect()
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2021 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
@ -10,57 +10,24 @@ import Starscream
|
||||||
|
|
||||||
/// Extension to handle delegate callback from Starscream
|
/// Extension to handle delegate callback from Starscream
|
||||||
extension StarscreamAdapter: Starscream.WebSocketDelegate {
|
extension StarscreamAdapter: Starscream.WebSocketDelegate {
|
||||||
public func didReceive(event: WebSocketEvent, client: WebSocket) {
|
|
||||||
switch event {
|
|
||||||
case .connected:
|
|
||||||
websocketDidConnect(socket: client)
|
|
||||||
case .disconnected(let reason, let code):
|
|
||||||
AppSyncLogger.verbose("[StarscreamAdapter] disconnected: reason=\(reason); code=\(code)")
|
|
||||||
websocketDidDisconnect(socket: client, error: nil)
|
|
||||||
case .text(let string):
|
|
||||||
websocketDidReceiveMessage(socket: client, text: string)
|
|
||||||
case .binary(let data):
|
|
||||||
websocketDidReceiveData(socket: client, data: data)
|
|
||||||
case .ping:
|
|
||||||
AppSyncLogger.verbose("[StarscreamAdapter] ping")
|
|
||||||
case .pong:
|
|
||||||
AppSyncLogger.verbose("[StarscreamAdapter] pong")
|
|
||||||
case .viabilityChanged(let viability):
|
|
||||||
AppSyncLogger.verbose("[StarscreamAdapter] viabilityChanged: \(viability)")
|
|
||||||
case .reconnectSuggested(let suggestion):
|
|
||||||
AppSyncLogger.verbose("[StarscreamAdapter] reconnectSuggested: \(suggestion)")
|
|
||||||
case .cancelled:
|
|
||||||
websocketDidDisconnect(socket: client, error: nil)
|
|
||||||
case .error(let error):
|
|
||||||
websocketDidDisconnect(socket: client, error: error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private func websocketDidConnect(socket: WebSocketClient) {
|
public func websocketDidConnect(socket: WebSocketClient) {
|
||||||
AppSyncLogger.verbose("[StarscreamAdapter] websocketDidConnect: websocket has been connected.")
|
AppSyncLogger.verbose("[StarscreamAdapter] websocketDidConnect: websocket has been connected.")
|
||||||
serialQueue.async {
|
delegate?.websocketDidConnect(provider: self)
|
||||||
self._isConnected = true
|
|
||||||
self.delegate?.websocketDidConnect(provider: self)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func websocketDidDisconnect(socket: WebSocketClient, error: Error?) {
|
public func websocketDidDisconnect(socket: WebSocketClient, error: Error?) {
|
||||||
AppSyncLogger.verbose(
|
AppSyncLogger.verbose("[StarscreamAdapter] websocketDidDisconnect: \(error?.localizedDescription ?? "No error")")
|
||||||
"[StarscreamAdapter] websocketDidDisconnect: \(error?.localizedDescription ?? "No error")"
|
delegate?.websocketDidDisconnect(provider: self, error: error)
|
||||||
)
|
|
||||||
serialQueue.async {
|
|
||||||
self._isConnected = false
|
|
||||||
self.delegate?.websocketDidDisconnect(provider: self, error: error)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func websocketDidReceiveMessage(socket: WebSocketClient, text: String) {
|
public func websocketDidReceiveMessage(socket: WebSocketClient, text: String) {
|
||||||
AppSyncLogger.verbose("[StarscreamAdapter] websocketDidReceiveMessage: - \(text)")
|
AppSyncLogger.verbose("[StarscreamAdapter] websocketDidReceiveMessage: - \(text)")
|
||||||
let data = text.data(using: .utf8) ?? Data()
|
let data = text.data(using: .utf8) ?? Data()
|
||||||
delegate?.websocketDidReceiveData(provider: self, data: data)
|
delegate?.websocketDidReceiveData(provider: self, data: data)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func websocketDidReceiveData(socket: WebSocketClient, data: Data) {
|
public func websocketDidReceiveData(socket: WebSocketClient, data: Data) {
|
||||||
AppSyncLogger.verbose("[StarscreamAdapter] WebsocketDidReceiveData - \(data)")
|
AppSyncLogger.verbose("[StarscreamAdapter] WebsocketDidReceiveData - \(data)")
|
||||||
delegate?.websocketDidReceiveData(provider: self, data: data)
|
delegate?.websocketDidReceiveData(provider: self, data: data)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2021 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
@ -9,45 +9,22 @@ import Foundation
|
||||||
import Starscream
|
import Starscream
|
||||||
|
|
||||||
public class StarscreamAdapter: AppSyncWebsocketProvider {
|
public class StarscreamAdapter: AppSyncWebsocketProvider {
|
||||||
let serialQueue: DispatchQueue
|
public init() {
|
||||||
private let callbackQueue: DispatchQueue
|
// Do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
private let serialQueue = DispatchQueue(label: "com.amazonaws.StarscreamAdapter.serialQueue")
|
||||||
|
|
||||||
var socket: WebSocket?
|
var socket: WebSocket?
|
||||||
weak var delegate: AppSyncWebsocketDelegate?
|
weak var delegate: AppSyncWebsocketDelegate?
|
||||||
|
|
||||||
// swiftlint:disable:next identifier_name
|
public func connect(url: URL, protocols: [String], delegate: AppSyncWebsocketDelegate?) {
|
||||||
var _isConnected: Bool
|
|
||||||
public var isConnected: Bool {
|
|
||||||
serialQueue.sync {
|
|
||||||
_isConnected
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public init() {
|
|
||||||
let serialQueue = DispatchQueue(label: "com.amazonaws.StarscreamAdapter.serialQueue")
|
|
||||||
let callbackQueue = DispatchQueue(
|
|
||||||
label: "com.amazonaws.StarscreamAdapter.callBack",
|
|
||||||
target: serialQueue
|
|
||||||
)
|
|
||||||
self._isConnected = false
|
|
||||||
self.serialQueue = serialQueue
|
|
||||||
self.callbackQueue = callbackQueue
|
|
||||||
}
|
|
||||||
|
|
||||||
public func connect(urlRequest: URLRequest, protocols: [String], delegate: AppSyncWebsocketDelegate?) {
|
|
||||||
serialQueue.async {
|
serialQueue.async {
|
||||||
AppSyncLogger.verbose("[StarscreamAdapter] connect. Connecting to url")
|
AppSyncLogger.verbose("[StarscreamAdapter] connect. Connecting to url")
|
||||||
var urlRequest = urlRequest
|
self.socket = WebSocket(url: url, protocols: protocols)
|
||||||
|
|
||||||
urlRequest.setValue("no-store", forHTTPHeaderField: "Cache-Control")
|
|
||||||
|
|
||||||
let protocolHeaderValue = protocols.joined(separator: ", ")
|
|
||||||
urlRequest.setValue(protocolHeaderValue, forHTTPHeaderField: "Sec-WebSocket-Protocol")
|
|
||||||
|
|
||||||
self.socket = WebSocket(request: urlRequest)
|
|
||||||
self.delegate = delegate
|
self.delegate = delegate
|
||||||
self.socket?.delegate = self
|
self.socket?.delegate = self
|
||||||
self.socket?.callbackQueue = self.callbackQueue
|
self.socket?.callbackQueue = DispatchQueue(label: "com.amazonaws.StarscreamAdapter.callBack")
|
||||||
self.socket?.connect()
|
self.socket?.connect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,4 +43,10 @@ public class StarscreamAdapter: AppSyncWebsocketProvider {
|
||||||
self.socket?.write(string: message)
|
self.socket?.write(string: message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public var isConnected: Bool {
|
||||||
|
serialQueue.sync {
|
||||||
|
return socket?.isConnected ?? false
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,190 +0,0 @@
|
||||||
//
|
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
|
||||||
// All Rights Reserved.
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
//
|
|
||||||
|
|
||||||
import XCTest
|
|
||||||
@testable import AppSyncRealTimeClient
|
|
||||||
|
|
||||||
class AppSyncRealTimeClientAsyncFailureTests: AppSyncRealTimeClientTestBase {
|
|
||||||
|
|
||||||
/// Test the current AppSync limit of 100 subscriptions per connection
|
|
||||||
func testMaxSubscriptionReached() { // swiftlint:disable:this cyclomatic_complexity
|
|
||||||
let subscribeSuccess = expectation(description: "subscribe successfully")
|
|
||||||
subscribeSuccess.expectedFulfillmentCount = 100
|
|
||||||
let authInterceptor = APIKeyAuthInterceptor(apiKey)
|
|
||||||
let connectionProvider = ConnectionProviderFactory.createConnectionProviderAsync(
|
|
||||||
for: urlRequest,
|
|
||||||
authInterceptor: authInterceptor,
|
|
||||||
connectionType: .appSyncRealtime
|
|
||||||
)
|
|
||||||
var subscriptions = [AppSyncSubscriptionConnection]()
|
|
||||||
for _ in 1 ... 100 {
|
|
||||||
let subscription = AppSyncSubscriptionConnection(provider: connectionProvider)
|
|
||||||
_ = subscription.subscribe(
|
|
||||||
requestString: requestString,
|
|
||||||
variables: nil
|
|
||||||
) { event, _ 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)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
subscriptions.append(subscription)
|
|
||||||
}
|
|
||||||
|
|
||||||
wait(for: [subscribeSuccess], timeout: TestCommonConstants.networkTimeout)
|
|
||||||
XCTAssertEqual(subscriptions.count, 100)
|
|
||||||
let limitExceeded = expectation(description: "Received Limit Exceeded error")
|
|
||||||
let subscription = AppSyncSubscriptionConnection(provider: connectionProvider)
|
|
||||||
_ = subscription.subscribe(
|
|
||||||
requestString: requestString,
|
|
||||||
variables: nil
|
|
||||||
) { event, _ in
|
|
||||||
switch event {
|
|
||||||
case .connection(let subscriptionConnectionEvent):
|
|
||||||
switch subscriptionConnectionEvent {
|
|
||||||
case .connecting:
|
|
||||||
break
|
|
||||||
case .connected:
|
|
||||||
XCTFail("Got connected successfully - Should have been limit exceeded")
|
|
||||||
case .disconnected:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case .data(let data):
|
|
||||||
print("Got data back \(data)")
|
|
||||||
case .failed(let error):
|
|
||||||
guard let connectionError = error as? ConnectionProviderError,
|
|
||||||
case .limitExceeded = connectionError else {
|
|
||||||
XCTFail("Should Be Limited Exceeded error")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
limitExceeded.fulfill()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
wait(for: [limitExceeded], timeout: TestCommonConstants.networkTimeout)
|
|
||||||
|
|
||||||
for subscription in subscriptions {
|
|
||||||
if let item = subscription.subscriptionItem {
|
|
||||||
subscription.unsubscribe(item: item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Subscriptions receiving a failed event should only receive it once.
|
|
||||||
func testMaxSubscriptionReachedWithRetry() { // swiftlint:disable:this cyclomatic_complexity
|
|
||||||
let subscribeSuccess = expectation(description: "subscribe successfully")
|
|
||||||
subscribeSuccess.expectedFulfillmentCount = 100
|
|
||||||
let authInterceptor = APIKeyAuthInterceptor(apiKey)
|
|
||||||
let connectionProvider = ConnectionProviderFactory.createConnectionProviderAsync(
|
|
||||||
for: urlRequest,
|
|
||||||
authInterceptor: authInterceptor,
|
|
||||||
connectionType: .appSyncRealtime
|
|
||||||
)
|
|
||||||
var subscriptions = [AppSyncSubscriptionConnection]()
|
|
||||||
for _ in 1 ... 100 {
|
|
||||||
let subscription = AppSyncSubscriptionConnection(provider: connectionProvider)
|
|
||||||
_ = subscription.subscribe(
|
|
||||||
requestString: requestString,
|
|
||||||
variables: nil
|
|
||||||
) { event, _ 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)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
subscriptions.append(subscription)
|
|
||||||
}
|
|
||||||
|
|
||||||
wait(for: [subscribeSuccess], timeout: TestCommonConstants.networkTimeout)
|
|
||||||
XCTAssertEqual(subscriptions.count, 100)
|
|
||||||
let limitExceeded = expectation(description: "Received Limit Exceeded error")
|
|
||||||
limitExceeded.expectedFulfillmentCount = 2
|
|
||||||
for _ in 1 ... 2 {
|
|
||||||
let subscription = AppSyncSubscriptionConnection(provider: connectionProvider)
|
|
||||||
subscription.addRetryHandler(handler: TestConnectionRetryHandler())
|
|
||||||
_ = subscription.subscribe(
|
|
||||||
requestString: requestString,
|
|
||||||
variables: nil
|
|
||||||
) { event, _ in
|
|
||||||
switch event {
|
|
||||||
case .connection(let subscriptionConnectionEvent):
|
|
||||||
switch subscriptionConnectionEvent {
|
|
||||||
case .connecting:
|
|
||||||
break
|
|
||||||
case .connected:
|
|
||||||
XCTFail("Got connected successfully - Should have been limit exceeded")
|
|
||||||
case .disconnected:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case .data(let data):
|
|
||||||
print("Got data back \(data)")
|
|
||||||
case .failed(let error):
|
|
||||||
guard let connectionError = error as? ConnectionProviderError,
|
|
||||||
case .limitExceeded = connectionError else {
|
|
||||||
XCTFail("Should Be Limited Exceeded error")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
limitExceeded.fulfill()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
subscriptions.append(subscription)
|
|
||||||
}
|
|
||||||
|
|
||||||
wait(for: [limitExceeded], timeout: TestCommonConstants.networkTimeout)
|
|
||||||
|
|
||||||
for subscription in subscriptions {
|
|
||||||
if let item = subscription.subscriptionItem {
|
|
||||||
subscription.unsubscribe(item: item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class TestConnectionRetryHandler: ConnectionRetryHandler {
|
|
||||||
var count: Int = 0
|
|
||||||
func shouldRetryRequest(for error: ConnectionProviderError) -> RetryAdvice {
|
|
||||||
if count > 10 {
|
|
||||||
return TestRetryAdvice(shouldRetry: false)
|
|
||||||
}
|
|
||||||
|
|
||||||
if case .limitExceeded = error {
|
|
||||||
self.count += 1
|
|
||||||
return TestRetryAdvice(shouldRetry: true, retryInterval: .seconds(1))
|
|
||||||
}
|
|
||||||
return TestRetryAdvice(shouldRetry: false)
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct TestRetryAdvice: RetryAdvice {
|
|
||||||
var shouldRetry: Bool
|
|
||||||
|
|
||||||
var retryInterval: DispatchTimeInterval?
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,262 +0,0 @@
|
||||||
//
|
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
|
||||||
// All Rights Reserved.
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
//
|
|
||||||
|
|
||||||
import XCTest
|
|
||||||
@testable import AppSyncRealTimeClient
|
|
||||||
|
|
||||||
class AppSyncRealTimeClientAsyncIntegrationTests: AppSyncRealTimeClientTestBase { // swiftlint:disable:this type_name
|
|
||||||
|
|
||||||
/// 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.createConnectionProviderAsync(
|
|
||||||
for: urlRequest,
|
|
||||||
authInterceptor: authInterceptor,
|
|
||||||
connectionType: .appSyncRealtime
|
|
||||||
)
|
|
||||||
let subscriptionConnection = AppSyncSubscriptionConnection(provider: connectionProvider)
|
|
||||||
_ = subscriptionConnection.subscribe(
|
|
||||||
requestString: requestString,
|
|
||||||
variables: nil
|
|
||||||
) { event, _ 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The purpose of this test is to ensure that all websockets can be successfully
|
|
||||||
/// created, exercised and terminated while keeping a single connection provider in
|
|
||||||
/// memory.
|
|
||||||
///
|
|
||||||
/// Specifically, the following test exercises the following:
|
|
||||||
/// 1. Create a new connection provider
|
|
||||||
/// 2. Create multiple subscriptions
|
|
||||||
/// 3. Unsubscribe the subscriptions
|
|
||||||
/// 4. Ensure the socket is disconnected
|
|
||||||
/// 5. Repeat Steps 2-4 with the existing connection provider.
|
|
||||||
///
|
|
||||||
/// - Given: Connected subscriptions
|
|
||||||
/// - When:
|
|
||||||
/// - All subscription items are unsubscribed
|
|
||||||
/// - Then:
|
|
||||||
/// - Underlying websocket is disconnected
|
|
||||||
func testAllSubscriptionsCancelledShouldDisconnectTheWebsocket2() {
|
|
||||||
let connectedInvoked = expectation(description: "Connection established")
|
|
||||||
connectedInvoked.expectedFulfillmentCount = 3
|
|
||||||
|
|
||||||
let authInterceptor = APIKeyAuthInterceptor(apiKey)
|
|
||||||
let connectionProvider = ConnectionProviderFactory.createConnectionProviderAsync(
|
|
||||||
for: urlRequest,
|
|
||||||
authInterceptor: authInterceptor,
|
|
||||||
connectionType: .appSyncRealtime
|
|
||||||
)
|
|
||||||
let subscriptionConnection1 = AppSyncSubscriptionConnection(provider: connectionProvider)
|
|
||||||
let item1 = subscriptionConnection1.subscribe(
|
|
||||||
requestString: requestString,
|
|
||||||
variables: nil
|
|
||||||
) { event, _ in
|
|
||||||
if case let .connection(state) = event {
|
|
||||||
if case .connected = state {
|
|
||||||
connectedInvoked.fulfill()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let subscriptionConnection2 = AppSyncSubscriptionConnection(provider: connectionProvider)
|
|
||||||
let item2 = subscriptionConnection2.subscribe(
|
|
||||||
requestString: requestString,
|
|
||||||
variables: nil
|
|
||||||
) { event, _ in
|
|
||||||
if case let .connection(state) = event {
|
|
||||||
if case .connected = state {
|
|
||||||
connectedInvoked.fulfill()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let subscriptionConnection3 = AppSyncSubscriptionConnection(provider: connectionProvider)
|
|
||||||
let item3 = subscriptionConnection3.subscribe(
|
|
||||||
requestString: requestString,
|
|
||||||
variables: nil
|
|
||||||
) { event, _ in
|
|
||||||
if case let .connection(state) = event {
|
|
||||||
if case .connected = state {
|
|
||||||
connectedInvoked.fulfill()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
XCTAssertNotNil(item1)
|
|
||||||
XCTAssertNotNil(item2)
|
|
||||||
XCTAssertNotNil(item3)
|
|
||||||
wait(for: [connectedInvoked], timeout: TestCommonConstants.networkTimeout)
|
|
||||||
|
|
||||||
guard let realTimeConnectionProvider = connectionProvider as? RealtimeConnectionProviderAsync else {
|
|
||||||
XCTFail("Could not retrieve concrete connection provider")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
assertStatus(of: realTimeConnectionProvider, equals: .connected)
|
|
||||||
|
|
||||||
subscriptionConnection1.unsubscribe(item: item1)
|
|
||||||
assertStatus(of: realTimeConnectionProvider, equals: .connected)
|
|
||||||
subscriptionConnection2.unsubscribe(item: item2)
|
|
||||||
assertStatus(of: realTimeConnectionProvider, equals: .connected)
|
|
||||||
subscriptionConnection3.unsubscribe(item: item3)
|
|
||||||
|
|
||||||
assertStatus(of: realTimeConnectionProvider, equals: .notConnected)
|
|
||||||
|
|
||||||
let newConnectedInvoked = expectation(description: "Connection established")
|
|
||||||
let subscriptionConnection4 = AppSyncSubscriptionConnection(provider: connectionProvider)
|
|
||||||
let newItem = subscriptionConnection4.subscribe(
|
|
||||||
requestString: requestString,
|
|
||||||
variables: nil
|
|
||||||
) { event, _ in
|
|
||||||
if case let .connection(state) = event {
|
|
||||||
if case .connected = state {
|
|
||||||
newConnectedInvoked.fulfill()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
wait(for: [newConnectedInvoked], timeout: TestCommonConstants.networkTimeout)
|
|
||||||
assertStatus(of: realTimeConnectionProvider, equals: .connected)
|
|
||||||
subscriptionConnection4.unsubscribe(item: newItem)
|
|
||||||
sleep(5)
|
|
||||||
assertStatus(of: realTimeConnectionProvider, equals: .notConnected)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The purpose of this test is to ensure that a signifcant number of subscriptions
|
|
||||||
/// can be created on a websocket, then unsubscribed, and repeated.
|
|
||||||
///
|
|
||||||
/// Specifically, the following test exercises the following:
|
|
||||||
/// 1. Create a new connection provider
|
|
||||||
/// 2. Create multiple subscriptions
|
|
||||||
/// 3. Unsubscribe the subscriptions
|
|
||||||
/// 4. Ensure the socket is disconnected
|
|
||||||
/// 5. Repeat Steps 2-4 with the existing connection provider.
|
|
||||||
///
|
|
||||||
/// - Given: Connected subscriptions
|
|
||||||
/// - When:
|
|
||||||
/// - All subscription items are unsubscribed
|
|
||||||
/// - Then:
|
|
||||||
/// - Underlying websocket is disconnected
|
|
||||||
func testSubscribeUnsubscribeRepeat() {
|
|
||||||
let authInterceptor = APIKeyAuthInterceptor(apiKey)
|
|
||||||
let connectionProvider = ConnectionProviderFactory.createConnectionProviderAsync(
|
|
||||||
for: urlRequest,
|
|
||||||
authInterceptor: authInterceptor,
|
|
||||||
connectionType: .appSyncRealtime
|
|
||||||
)
|
|
||||||
guard let realTimeConnectionProvider = connectionProvider as? RealtimeConnectionProviderAsync else {
|
|
||||||
XCTFail("Could not retrieve concrete connection provider")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let count = 30
|
|
||||||
let subscriptions = subscribe(connectionProvider, count: count)
|
|
||||||
assertStatus(of: realTimeConnectionProvider, equals: .connected)
|
|
||||||
for index in 0 ..< count {
|
|
||||||
subscriptions[index].1.unsubscribe(item: subscriptions[index].0)
|
|
||||||
}
|
|
||||||
assertStatus(of: realTimeConnectionProvider, equals: .notConnected)
|
|
||||||
let subscriptions2 = subscribe(connectionProvider, count: count)
|
|
||||||
assertStatus(of: realTimeConnectionProvider, equals: .connected)
|
|
||||||
for index in 0 ..< count {
|
|
||||||
subscriptions2[index].1.unsubscribe(item: subscriptions2[index].0)
|
|
||||||
}
|
|
||||||
assertStatus(of: realTimeConnectionProvider, equals: .notConnected)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testMultipleThreadsSubscribeUnsubscribe() {
|
|
||||||
let authInterceptor = APIKeyAuthInterceptor(apiKey)
|
|
||||||
let connectionProvider = ConnectionProviderFactory.createConnectionProviderAsync(
|
|
||||||
for: urlRequest,
|
|
||||||
authInterceptor: authInterceptor,
|
|
||||||
connectionType: .appSyncRealtime
|
|
||||||
)
|
|
||||||
guard let realTimeConnectionProvider = connectionProvider as? RealtimeConnectionProviderAsync else {
|
|
||||||
XCTFail("Could not retrieve concrete connection provider")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
let expectedPerforms = expectation(description: "total performs")
|
|
||||||
expectedPerforms.expectedFulfillmentCount = 1_000
|
|
||||||
DispatchQueue.concurrentPerform(iterations: 1_000) { _ in
|
|
||||||
let subscriptionConnection = AppSyncSubscriptionConnection(provider: connectionProvider)
|
|
||||||
let item = subscriptionConnection.subscribe(
|
|
||||||
requestString: requestString,
|
|
||||||
variables: nil
|
|
||||||
) { _, _ in }
|
|
||||||
subscriptionConnection.unsubscribe(item: item)
|
|
||||||
expectedPerforms.fulfill()
|
|
||||||
}
|
|
||||||
wait(for: [expectedPerforms], timeout: 1)
|
|
||||||
assertStatus(of: realTimeConnectionProvider, equals: .notConnected)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Helpers
|
|
||||||
|
|
||||||
private func subscribe(
|
|
||||||
_ connectionProvider: ConnectionProvider,
|
|
||||||
count: Int
|
|
||||||
) -> [(SubscriptionItem, AppSyncSubscriptionConnection)] {
|
|
||||||
let connectedInvoked = expectation(description: "Connection established")
|
|
||||||
connectedInvoked.expectedFulfillmentCount = count
|
|
||||||
var subscriptions = [(SubscriptionItem, AppSyncSubscriptionConnection)]()
|
|
||||||
|
|
||||||
for _ in 1 ... count {
|
|
||||||
let subscriptionConnection = AppSyncSubscriptionConnection(provider: connectionProvider)
|
|
||||||
let item = subscriptionConnection.subscribe(
|
|
||||||
requestString: requestString,
|
|
||||||
variables: nil
|
|
||||||
) { event, _ in
|
|
||||||
if case let .connection(state) = event {
|
|
||||||
if case .connected = state {
|
|
||||||
connectedInvoked.fulfill()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
subscriptions.append((item, subscriptionConnection))
|
|
||||||
}
|
|
||||||
|
|
||||||
wait(for: [connectedInvoked], timeout: TestCommonConstants.networkTimeout)
|
|
||||||
return subscriptions
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks the status of the provider in a thread-safe way. This is only needed for tests; real-world
|
|
||||||
/// call sites wouldn't be able to access the `status` as it has `internal` access.
|
|
||||||
private func assertStatus(
|
|
||||||
of provider: RealtimeConnectionProviderAsync,
|
|
||||||
equals status: ConnectionState
|
|
||||||
) {
|
|
||||||
provider.taskQueue.async {
|
|
||||||
XCTAssertEqual(provider.status, status)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,223 +0,0 @@
|
||||||
//
|
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
|
||||||
// All Rights Reserved.
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
//
|
|
||||||
|
|
||||||
import XCTest
|
|
||||||
@testable import AppSyncRealTimeClient
|
|
||||||
|
|
||||||
class AppSyncRealTimeClientFailureTests: AppSyncRealTimeClientTestBase {
|
|
||||||
|
|
||||||
/// Test the current AppSync limit of 100 subscriptions per connection
|
|
||||||
func testMaxSubscriptionReached() { // swiftlint:disable:this cyclomatic_complexity
|
|
||||||
let subscribeSuccess = expectation(description: "subscribe successfully")
|
|
||||||
subscribeSuccess.expectedFulfillmentCount = 100
|
|
||||||
let authInterceptor = APIKeyAuthInterceptor(apiKey)
|
|
||||||
let connectionProvider = ConnectionProviderFactory.createConnectionProvider(
|
|
||||||
for: urlRequest,
|
|
||||||
authInterceptor: authInterceptor,
|
|
||||||
connectionType: .appSyncRealtime
|
|
||||||
)
|
|
||||||
var subscriptions = [AppSyncSubscriptionConnection]()
|
|
||||||
for _ in 1 ... 100 {
|
|
||||||
let subscription = AppSyncSubscriptionConnection(provider: connectionProvider)
|
|
||||||
_ = subscription.subscribe(
|
|
||||||
requestString: requestString,
|
|
||||||
variables: nil
|
|
||||||
) { event, _ 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)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
subscriptions.append(subscription)
|
|
||||||
}
|
|
||||||
|
|
||||||
wait(for: [subscribeSuccess], timeout: TestCommonConstants.networkTimeout)
|
|
||||||
XCTAssertEqual(subscriptions.count, 100)
|
|
||||||
let limitExceeded = expectation(description: "Received Limit Exceeded error")
|
|
||||||
let subscription = AppSyncSubscriptionConnection(provider: connectionProvider)
|
|
||||||
_ = subscription.subscribe(
|
|
||||||
requestString: requestString,
|
|
||||||
variables: nil
|
|
||||||
) { event, _ in
|
|
||||||
switch event {
|
|
||||||
case .connection(let subscriptionConnectionEvent):
|
|
||||||
switch subscriptionConnectionEvent {
|
|
||||||
case .connecting:
|
|
||||||
break
|
|
||||||
case .connected:
|
|
||||||
XCTFail("Got connected successfully - Should have been limit exceeded")
|
|
||||||
case .disconnected:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case .data(let data):
|
|
||||||
print("Got data back \(data)")
|
|
||||||
case .failed(let error):
|
|
||||||
guard let connectionError = error as? ConnectionProviderError,
|
|
||||||
case .limitExceeded = connectionError else {
|
|
||||||
XCTFail("Should Be Limited Exceeded error")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
limitExceeded.fulfill()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
wait(for: [limitExceeded], timeout: TestCommonConstants.networkTimeout)
|
|
||||||
|
|
||||||
for subscription in subscriptions {
|
|
||||||
if let item = subscription.subscriptionItem {
|
|
||||||
subscription.unsubscribe(item: item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Subscriptions receiving a failed event should only receive it once.
|
|
||||||
func testMaxSubscriptionReachedWithRetry() { // swiftlint:disable:this cyclomatic_complexity
|
|
||||||
let subscribeSuccess = expectation(description: "subscribe successfully")
|
|
||||||
subscribeSuccess.expectedFulfillmentCount = 100
|
|
||||||
let authInterceptor = APIKeyAuthInterceptor(apiKey)
|
|
||||||
let connectionProvider = ConnectionProviderFactory.createConnectionProvider(
|
|
||||||
for: urlRequest,
|
|
||||||
authInterceptor: authInterceptor,
|
|
||||||
connectionType: .appSyncRealtime
|
|
||||||
)
|
|
||||||
var subscriptions = [AppSyncSubscriptionConnection]()
|
|
||||||
for _ in 1 ... 100 {
|
|
||||||
let subscription = AppSyncSubscriptionConnection(provider: connectionProvider)
|
|
||||||
_ = subscription.subscribe(
|
|
||||||
requestString: requestString,
|
|
||||||
variables: nil
|
|
||||||
) { event, _ 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)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
subscriptions.append(subscription)
|
|
||||||
}
|
|
||||||
|
|
||||||
wait(for: [subscribeSuccess], timeout: TestCommonConstants.networkTimeout)
|
|
||||||
XCTAssertEqual(subscriptions.count, 100)
|
|
||||||
let limitExceeded = expectation(description: "Received Limit Exceeded error")
|
|
||||||
limitExceeded.expectedFulfillmentCount = 2
|
|
||||||
for _ in 1 ... 2 {
|
|
||||||
let subscription = AppSyncSubscriptionConnection(provider: connectionProvider)
|
|
||||||
subscription.addRetryHandler(handler: TestConnectionRetryHandler())
|
|
||||||
_ = subscription.subscribe(
|
|
||||||
requestString: requestString,
|
|
||||||
variables: nil
|
|
||||||
) { event, _ in
|
|
||||||
switch event {
|
|
||||||
case .connection(let subscriptionConnectionEvent):
|
|
||||||
switch subscriptionConnectionEvent {
|
|
||||||
case .connecting:
|
|
||||||
break
|
|
||||||
case .connected:
|
|
||||||
XCTFail("Got connected successfully - Should have been limit exceeded")
|
|
||||||
case .disconnected:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
case .data(let data):
|
|
||||||
print("Got data back \(data)")
|
|
||||||
case .failed(let error):
|
|
||||||
guard let connectionError = error as? ConnectionProviderError,
|
|
||||||
case .limitExceeded = connectionError else {
|
|
||||||
XCTFail("Should Be Limited Exceeded error")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
limitExceeded.fulfill()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
subscriptions.append(subscription)
|
|
||||||
}
|
|
||||||
|
|
||||||
wait(for: [limitExceeded], timeout: TestCommonConstants.networkTimeout)
|
|
||||||
|
|
||||||
for subscription in subscriptions {
|
|
||||||
if let item = subscription.subscriptionItem {
|
|
||||||
subscription.unsubscribe(item: item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func testAPIKeyInvalid() {
|
|
||||||
apiKey = "invalid"
|
|
||||||
let subscribeFailed = expectation(description: "subscribe failed")
|
|
||||||
let authInterceptor = APIKeyAuthInterceptor(apiKey)
|
|
||||||
let connectionProvider = ConnectionProviderFactory.createConnectionProvider(
|
|
||||||
for: urlRequest,
|
|
||||||
authInterceptor: authInterceptor,
|
|
||||||
connectionType: .appSyncRealtime
|
|
||||||
)
|
|
||||||
let subscriptionConnection = AppSyncSubscriptionConnection(provider: connectionProvider)
|
|
||||||
_ = subscriptionConnection.subscribe(
|
|
||||||
requestString: requestString,
|
|
||||||
variables: nil
|
|
||||||
) { event, _ in
|
|
||||||
|
|
||||||
switch event {
|
|
||||||
case .connection:
|
|
||||||
break
|
|
||||||
case .data:
|
|
||||||
break
|
|
||||||
case .failed(let error):
|
|
||||||
guard let connectionError = error as? ConnectionProviderError,
|
|
||||||
case .unauthorized = connectionError else {
|
|
||||||
XCTFail("Should be `.unauthorized` error")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
subscribeFailed.fulfill()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
wait(for: [subscribeFailed], timeout: TestCommonConstants.networkTimeout)
|
|
||||||
}
|
|
||||||
|
|
||||||
class TestConnectionRetryHandler: ConnectionRetryHandler {
|
|
||||||
var count: Int = 0
|
|
||||||
func shouldRetryRequest(for error: ConnectionProviderError) -> RetryAdvice {
|
|
||||||
if count > 10 {
|
|
||||||
return TestRetryAdvice(shouldRetry: false)
|
|
||||||
}
|
|
||||||
|
|
||||||
if case .limitExceeded = error {
|
|
||||||
self.count += 1
|
|
||||||
return TestRetryAdvice(shouldRetry: true, retryInterval: .seconds(1))
|
|
||||||
}
|
|
||||||
return TestRetryAdvice(shouldRetry: false)
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct TestRetryAdvice: RetryAdvice {
|
|
||||||
var shouldRetry: Bool
|
|
||||||
|
|
||||||
var retryInterval: DispatchTimeInterval?
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2021 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
@ -23,7 +23,7 @@ class AppSyncRealTimeClientIntegrationTests: AppSyncRealTimeClientTestBase {
|
||||||
let subscribeSuccess = expectation(description: "subscribe successfully")
|
let subscribeSuccess = expectation(description: "subscribe successfully")
|
||||||
let authInterceptor = APIKeyAuthInterceptor(apiKey)
|
let authInterceptor = APIKeyAuthInterceptor(apiKey)
|
||||||
let connectionProvider = ConnectionProviderFactory.createConnectionProvider(
|
let connectionProvider = ConnectionProviderFactory.createConnectionProvider(
|
||||||
for: urlRequest,
|
for: url,
|
||||||
authInterceptor: authInterceptor,
|
authInterceptor: authInterceptor,
|
||||||
connectionType: .appSyncRealtime
|
connectionType: .appSyncRealtime
|
||||||
)
|
)
|
||||||
|
@ -75,7 +75,7 @@ class AppSyncRealTimeClientIntegrationTests: AppSyncRealTimeClientTestBase {
|
||||||
|
|
||||||
let authInterceptor = APIKeyAuthInterceptor(apiKey)
|
let authInterceptor = APIKeyAuthInterceptor(apiKey)
|
||||||
let connectionProvider = ConnectionProviderFactory.createConnectionProvider(
|
let connectionProvider = ConnectionProviderFactory.createConnectionProvider(
|
||||||
for: urlRequest,
|
for: url,
|
||||||
authInterceptor: authInterceptor,
|
authInterceptor: authInterceptor,
|
||||||
connectionType: .appSyncRealtime
|
connectionType: .appSyncRealtime
|
||||||
)
|
)
|
||||||
|
@ -152,8 +152,7 @@ class AppSyncRealTimeClientIntegrationTests: AppSyncRealTimeClientTestBase {
|
||||||
assertStatus(of: realTimeConnectionProvider, equals: .notConnected)
|
assertStatus(of: realTimeConnectionProvider, equals: .notConnected)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The purpose of this test is to ensure that a signifcant number of subscriptions
|
/// The purpose of this test is to ensure that a signifcant number of subscriptions can be created on a websocket, then unsubscribed, and repeated.
|
||||||
/// can be created on a websocket, then unsubscribed, and repeated.
|
|
||||||
///
|
///
|
||||||
/// Specifically, the following test exercises the following:
|
/// Specifically, the following test exercises the following:
|
||||||
/// 1. Create a new connection provider
|
/// 1. Create a new connection provider
|
||||||
|
@ -170,7 +169,7 @@ class AppSyncRealTimeClientIntegrationTests: AppSyncRealTimeClientTestBase {
|
||||||
func testSubscribeUnsubscribeRepeat() {
|
func testSubscribeUnsubscribeRepeat() {
|
||||||
let authInterceptor = APIKeyAuthInterceptor(apiKey)
|
let authInterceptor = APIKeyAuthInterceptor(apiKey)
|
||||||
let connectionProvider = ConnectionProviderFactory.createConnectionProvider(
|
let connectionProvider = ConnectionProviderFactory.createConnectionProvider(
|
||||||
for: urlRequest,
|
for: url,
|
||||||
authInterceptor: authInterceptor,
|
authInterceptor: authInterceptor,
|
||||||
connectionType: .appSyncRealtime
|
connectionType: .appSyncRealtime
|
||||||
)
|
)
|
||||||
|
@ -197,7 +196,7 @@ class AppSyncRealTimeClientIntegrationTests: AppSyncRealTimeClientTestBase {
|
||||||
func testMultipleThreadsSubscribeUnsubscribe() {
|
func testMultipleThreadsSubscribeUnsubscribe() {
|
||||||
let authInterceptor = APIKeyAuthInterceptor(apiKey)
|
let authInterceptor = APIKeyAuthInterceptor(apiKey)
|
||||||
let connectionProvider = ConnectionProviderFactory.createConnectionProvider(
|
let connectionProvider = ConnectionProviderFactory.createConnectionProvider(
|
||||||
for: urlRequest,
|
for: url,
|
||||||
authInterceptor: authInterceptor,
|
authInterceptor: authInterceptor,
|
||||||
connectionType: .appSyncRealtime
|
connectionType: .appSyncRealtime
|
||||||
)
|
)
|
||||||
|
@ -212,7 +211,7 @@ class AppSyncRealTimeClientIntegrationTests: AppSyncRealTimeClientTestBase {
|
||||||
let item = subscriptionConnection.subscribe(
|
let item = subscriptionConnection.subscribe(
|
||||||
requestString: requestString,
|
requestString: requestString,
|
||||||
variables: nil
|
variables: nil
|
||||||
) { _, _ in }
|
) { event, _ in }
|
||||||
subscriptionConnection.unsubscribe(item: item)
|
subscriptionConnection.unsubscribe(item: item)
|
||||||
expectedPerforms.fulfill()
|
expectedPerforms.fulfill()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2021 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
@ -10,7 +10,7 @@ import XCTest
|
||||||
|
|
||||||
class AppSyncRealTimeClientTestBase: XCTestCase {
|
class AppSyncRealTimeClientTestBase: XCTestCase {
|
||||||
|
|
||||||
var urlRequest: URLRequest!
|
var url: URL!
|
||||||
var apiKey: String!
|
var apiKey: String!
|
||||||
let requestString = """
|
let requestString = """
|
||||||
subscription onCreate {
|
subscription onCreate {
|
||||||
|
@ -23,7 +23,6 @@ class AppSyncRealTimeClientTestBase: XCTestCase {
|
||||||
"""
|
"""
|
||||||
|
|
||||||
override func setUp() {
|
override func setUp() {
|
||||||
AppSyncRealTimeClient.logLevel = .debug
|
|
||||||
do {
|
do {
|
||||||
let json = try ConfigurationHelper.retrieve(forResource: "amplifyconfiguration")
|
let json = try ConfigurationHelper.retrieve(forResource: "amplifyconfiguration")
|
||||||
if let data = json as? [String: Any],
|
if let data = json as? [String: Any],
|
||||||
|
@ -35,8 +34,7 @@ class AppSyncRealTimeClientTestBase: XCTestCase {
|
||||||
let endpoint = apiName["endpoint"] as? String,
|
let endpoint = apiName["endpoint"] as? String,
|
||||||
let apiKey = apiName["apiKey"] as? String {
|
let apiKey = apiName["apiKey"] as? String {
|
||||||
|
|
||||||
urlRequest = URLRequest(url: URL(string: endpoint)!)
|
url = URL(string: endpoint)
|
||||||
|
|
||||||
self.apiKey = apiKey
|
self.apiKey = apiKey
|
||||||
} else {
|
} else {
|
||||||
throw "Could not retrieve endpoint"
|
throw "Could not retrieve endpoint"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2021 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
@ -14,14 +14,13 @@ class StarscreamAdapterTests: AppSyncRealTimeClientTestBase {
|
||||||
func testConnectDisconnect() throws {
|
func testConnectDisconnect() throws {
|
||||||
let starscreamAdapter = StarscreamAdapter()
|
let starscreamAdapter = StarscreamAdapter()
|
||||||
let apiKeyAuthInterceptor = APIKeyAuthInterceptor(apiKey)
|
let apiKeyAuthInterceptor = APIKeyAuthInterceptor(apiKey)
|
||||||
let request = AppSyncConnectionRequest(url: urlRequest.url!)
|
let request = AppSyncConnectionRequest(url: url)
|
||||||
let signedRequest = apiKeyAuthInterceptor.interceptConnection(request, for: urlRequest.url!)
|
let signedRequest = apiKeyAuthInterceptor.interceptConnection(request, for: url)
|
||||||
urlRequest.url = signedRequest.url
|
|
||||||
let expectedPerforms = expectation(description: "total performs")
|
let expectedPerforms = expectation(description: "total performs")
|
||||||
expectedPerforms.expectedFulfillmentCount = 1_000
|
expectedPerforms.expectedFulfillmentCount = 1_000
|
||||||
DispatchQueue.concurrentPerform(iterations: 1_000) { _ in
|
DispatchQueue.concurrentPerform(iterations: 1_000) { _ in
|
||||||
starscreamAdapter.connect(
|
starscreamAdapter.connect(
|
||||||
urlRequest: urlRequest,
|
url: signedRequest.url,
|
||||||
protocols: ["graphql-ws"],
|
protocols: ["graphql-ws"],
|
||||||
delegate: nil
|
delegate: nil
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2020 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2020 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2020 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
|
|
@ -1,348 +0,0 @@
|
||||||
//
|
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
|
||||||
// All Rights Reserved.
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
//
|
|
||||||
|
|
||||||
import XCTest
|
|
||||||
@testable import AppSyncRealTimeClient
|
|
||||||
|
|
||||||
// swiftlint:disable:next type_name type_body_length
|
|
||||||
class AppSyncSubscriptionConnectionErrorHandlerTests: XCTestCase {
|
|
||||||
|
|
||||||
let connectionProvider = MockConnectionProvider()
|
|
||||||
|
|
||||||
let mockRequestString = """
|
|
||||||
subscription OnCreateMessage {
|
|
||||||
onCreateMessage {
|
|
||||||
__typename
|
|
||||||
id
|
|
||||||
message
|
|
||||||
createdAt
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
|
|
||||||
let variables = [String: Any]()
|
|
||||||
|
|
||||||
func testOtherSubscription() throws {
|
|
||||||
let connection = AppSyncSubscriptionConnection(provider: connectionProvider)
|
|
||||||
let connectedMessageExpectation = expectation(description: "Connected event should be fired")
|
|
||||||
let failedEvent = expectation(description: "Failed event should not be fired")
|
|
||||||
failedEvent.isInverted = true
|
|
||||||
let item = connection.subscribe(requestString: mockRequestString, variables: variables) { event, _ in
|
|
||||||
switch event {
|
|
||||||
case .connection(let status):
|
|
||||||
if status == .connected {
|
|
||||||
connectedMessageExpectation.fulfill()
|
|
||||||
}
|
|
||||||
case .data:
|
|
||||||
XCTFail("Data event should not be published")
|
|
||||||
case .failed:
|
|
||||||
failedEvent.fulfill()
|
|
||||||
XCTFail("Error should not be thrown")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
XCTAssertNotNil(item, "Subscription item should not be nil")
|
|
||||||
wait(for: [connectedMessageExpectation], timeout: 5)
|
|
||||||
XCTAssertNotNil(connectionProvider.listener)
|
|
||||||
|
|
||||||
let otherSubscriptionError = ConnectionProviderError.subscription("otherId", nil)
|
|
||||||
connection.handleError(error: otherSubscriptionError)
|
|
||||||
wait(for: [failedEvent], timeout: 5)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testThisSubscription() throws {
|
|
||||||
let connection = AppSyncSubscriptionConnection(provider: connectionProvider)
|
|
||||||
let connectedMessageExpectation = expectation(description: "Connected event should be fired")
|
|
||||||
let failedEvent = expectation(description: "Failed event should be fired")
|
|
||||||
let item = connection.subscribe(requestString: mockRequestString, variables: variables) { event, _ in
|
|
||||||
switch event {
|
|
||||||
case .connection(let status):
|
|
||||||
if status == .connected {
|
|
||||||
connectedMessageExpectation.fulfill()
|
|
||||||
}
|
|
||||||
case .data:
|
|
||||||
XCTFail("Data event should not be published")
|
|
||||||
case .failed(let error):
|
|
||||||
guard let connection = error as? ConnectionProviderError,
|
|
||||||
case .subscription(let id, _) = connection,
|
|
||||||
!id.isEmpty else {
|
|
||||||
XCTFail("Should be .subscription(item.identifier)")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
failedEvent.fulfill()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
wait(for: [connectedMessageExpectation], timeout: 5)
|
|
||||||
XCTAssertNotNil(connectionProvider.listener)
|
|
||||||
|
|
||||||
let thisSubscriptionError = ConnectionProviderError.subscription(item.identifier, nil)
|
|
||||||
connection.handleError(error: thisSubscriptionError)
|
|
||||||
wait(for: [failedEvent], timeout: 5)
|
|
||||||
XCTAssertNil(connectionProvider.listener)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testLimitExceededOtherSubscription() throws {
|
|
||||||
let connection = AppSyncSubscriptionConnection(provider: connectionProvider)
|
|
||||||
let connectedMessageExpectation = expectation(description: "Connected event should be fired")
|
|
||||||
let failedEvent = expectation(description: "Failed event should not be fired")
|
|
||||||
failedEvent.isInverted = true
|
|
||||||
let item = connection.subscribe(requestString: mockRequestString, variables: variables) { event, _ in
|
|
||||||
switch event {
|
|
||||||
case .connection(let status):
|
|
||||||
if status == .connected {
|
|
||||||
connectedMessageExpectation.fulfill()
|
|
||||||
}
|
|
||||||
case .data:
|
|
||||||
XCTFail("Data event should not be published")
|
|
||||||
case .failed:
|
|
||||||
failedEvent.fulfill()
|
|
||||||
XCTFail("Error should not be thrown")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
XCTAssertNotNil(item, "Subscription item should not be nil")
|
|
||||||
wait(for: [connectedMessageExpectation], timeout: 5)
|
|
||||||
XCTAssertNotNil(connectionProvider.listener)
|
|
||||||
|
|
||||||
let limitExceeded = ConnectionProviderError.limitExceeded("otherId")
|
|
||||||
connection.handleError(error: limitExceeded)
|
|
||||||
wait(for: [failedEvent], timeout: 5)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testLimitExceededThisSubscription() throws {
|
|
||||||
let connection = AppSyncSubscriptionConnection(provider: connectionProvider)
|
|
||||||
let connectedMessageExpectation = expectation(description: "Connected event should be fired")
|
|
||||||
let failedEvent = expectation(description: "Failed event should be fired")
|
|
||||||
let item = connection.subscribe(requestString: mockRequestString, variables: variables) { event, _ in
|
|
||||||
switch event {
|
|
||||||
case .connection(let status):
|
|
||||||
if status == .connected {
|
|
||||||
connectedMessageExpectation.fulfill()
|
|
||||||
}
|
|
||||||
case .data:
|
|
||||||
XCTFail("Data event should not be published")
|
|
||||||
case .failed(let error):
|
|
||||||
guard let connection = error as? ConnectionProviderError,
|
|
||||||
case .limitExceeded(let id) = connection, id != nil else {
|
|
||||||
XCTFail("Should be .limitExceeded(item.identifier)")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
failedEvent.fulfill()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
wait(for: [connectedMessageExpectation], timeout: 5)
|
|
||||||
XCTAssertEqual(connection.subscriptionState, .subscribed)
|
|
||||||
XCTAssertNotNil(connectionProvider.listener)
|
|
||||||
|
|
||||||
let limitExceeded = ConnectionProviderError.limitExceeded(item.identifier)
|
|
||||||
connection.handleError(error: limitExceeded)
|
|
||||||
wait(for: [failedEvent], timeout: 5)
|
|
||||||
XCTAssertEqual(connection.subscriptionState, .notSubscribed)
|
|
||||||
XCTAssertNil(connectionProvider.listener)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testLimitExceededConnectionLevel_SubscribedShouldNoOp() throws {
|
|
||||||
let connection = AppSyncSubscriptionConnection(provider: connectionProvider)
|
|
||||||
let connectedMessageExpectation = expectation(description: "Connected event should be fired")
|
|
||||||
let failedEvent = expectation(description: "Failed event should not be fired")
|
|
||||||
failedEvent.isInverted = true
|
|
||||||
_ = connection.subscribe(requestString: mockRequestString, variables: variables) { event, _ in
|
|
||||||
switch event {
|
|
||||||
case .connection(let status):
|
|
||||||
if status == .connected {
|
|
||||||
connectedMessageExpectation.fulfill()
|
|
||||||
}
|
|
||||||
case .data:
|
|
||||||
XCTFail("Data event should not be published")
|
|
||||||
case .failed:
|
|
||||||
failedEvent.fulfill()
|
|
||||||
XCTFail("Error should not be thrown")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
wait(for: [connectedMessageExpectation], timeout: 5)
|
|
||||||
XCTAssertEqual(connection.subscriptionState, .subscribed)
|
|
||||||
XCTAssertNotNil(connectionProvider.listener)
|
|
||||||
|
|
||||||
let connectionLevelLimitedExceeded = ConnectionProviderError.limitExceeded(nil)
|
|
||||||
connection.handleError(error: connectionLevelLimitedExceeded)
|
|
||||||
wait(for: [failedEvent], timeout: 5)
|
|
||||||
XCTAssertEqual(connection.subscriptionState, .subscribed)
|
|
||||||
XCTAssertNotNil(connectionProvider.listener)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testLimitExceededConnectionLevel_InProgressToNotSubscribed() throws {
|
|
||||||
let connection = AppSyncSubscriptionConnection(provider: connectionProvider)
|
|
||||||
let connectedMessageExpectation = expectation(description: "Connected event should be fired")
|
|
||||||
let failedEvent = expectation(description: "Failed event should be fired")
|
|
||||||
_ = connection.subscribe(requestString: mockRequestString, variables: variables) { event, _ in
|
|
||||||
switch event {
|
|
||||||
case .connection(let status):
|
|
||||||
if status == .connected {
|
|
||||||
connectedMessageExpectation.fulfill()
|
|
||||||
}
|
|
||||||
case .data:
|
|
||||||
XCTFail("Data event should not be published")
|
|
||||||
case .failed(let error):
|
|
||||||
guard let connection = error as? ConnectionProviderError,
|
|
||||||
case .limitExceeded(let id) = connection,
|
|
||||||
id == nil else {
|
|
||||||
XCTFail("Should be .limitExceeded(nil)")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
failedEvent.fulfill()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
wait(for: [connectedMessageExpectation], timeout: 5)
|
|
||||||
XCTAssertEqual(connection.subscriptionState, .subscribed)
|
|
||||||
XCTAssertNotNil(connectionProvider.listener)
|
|
||||||
|
|
||||||
connection.subscriptionState = .inProgress
|
|
||||||
|
|
||||||
let connectionLevelLimitedExceeded = ConnectionProviderError.limitExceeded(nil)
|
|
||||||
connection.handleError(error: connectionLevelLimitedExceeded)
|
|
||||||
wait(for: [failedEvent], timeout: 5)
|
|
||||||
XCTAssertEqual(connection.subscriptionState, .notSubscribed)
|
|
||||||
XCTAssertNil(connectionProvider.listener)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testConnection() throws {
|
|
||||||
let connection = AppSyncSubscriptionConnection(provider: connectionProvider)
|
|
||||||
let connectedMessageExpectation = expectation(description: "Connected event should be fired")
|
|
||||||
let failedEvent = expectation(description: "Failed event should be fired")
|
|
||||||
_ = connection.subscribe(requestString: mockRequestString, variables: variables) { event, _ in
|
|
||||||
switch event {
|
|
||||||
case .connection(let status):
|
|
||||||
if status == .connected {
|
|
||||||
connectedMessageExpectation.fulfill()
|
|
||||||
}
|
|
||||||
case .data:
|
|
||||||
XCTFail("Data event should not be published")
|
|
||||||
case .failed(let error):
|
|
||||||
guard let connection = error as? ConnectionProviderError,
|
|
||||||
case .connection = connection else {
|
|
||||||
XCTFail("Should be .connection")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
failedEvent.fulfill()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
wait(for: [connectedMessageExpectation], timeout: 5)
|
|
||||||
XCTAssertEqual(connection.subscriptionState, .subscribed)
|
|
||||||
XCTAssertNotNil(connectionProvider.listener)
|
|
||||||
|
|
||||||
let connectionError = ConnectionProviderError.connection
|
|
||||||
connection.handleError(error: connectionError)
|
|
||||||
wait(for: [failedEvent], timeout: 5)
|
|
||||||
XCTAssertEqual(connection.subscriptionState, .notSubscribed)
|
|
||||||
XCTAssertNil(connectionProvider.listener)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testUnknown() throws {
|
|
||||||
let connection = AppSyncSubscriptionConnection(provider: connectionProvider)
|
|
||||||
let connectedMessageExpectation = expectation(description: "Connected event should be fired")
|
|
||||||
let failedEvent = expectation(description: "Failed event should be fired")
|
|
||||||
_ = connection.subscribe(requestString: mockRequestString, variables: variables) { event, _ in
|
|
||||||
switch event {
|
|
||||||
case .connection(let status):
|
|
||||||
if status == .connected {
|
|
||||||
connectedMessageExpectation.fulfill()
|
|
||||||
}
|
|
||||||
case .data:
|
|
||||||
XCTFail("Data event should not be published")
|
|
||||||
case .failed(let error):
|
|
||||||
guard let connection = error as? ConnectionProviderError,
|
|
||||||
case .unknown = connection else {
|
|
||||||
XCTFail("Should be .unknown")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
failedEvent.fulfill()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
wait(for: [connectedMessageExpectation], timeout: 5)
|
|
||||||
XCTAssertEqual(connection.subscriptionState, .subscribed)
|
|
||||||
XCTAssertNotNil(connectionProvider.listener)
|
|
||||||
|
|
||||||
let unknownError = ConnectionProviderError.unknown(message: nil, causedBy: nil, payload: nil)
|
|
||||||
connection.handleError(error: unknownError)
|
|
||||||
wait(for: [failedEvent], timeout: 5)
|
|
||||||
XCTAssertEqual(connection.subscriptionState, .notSubscribed)
|
|
||||||
XCTAssertNil(connectionProvider.listener)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testRetry() {
|
|
||||||
let connection = AppSyncSubscriptionConnection(provider: connectionProvider)
|
|
||||||
connection.addRetryHandler(handler: TestConnectionRetryHandler(maxCount: 0))
|
|
||||||
let connectedExpectation = expectation(description: "Connected event should be fired")
|
|
||||||
let connectedOnRetryExpectation = expectation(description: "Connected event should be fired")
|
|
||||||
var connectedOnce = false
|
|
||||||
let failedEvent = expectation(description: "Failed event should be fired")
|
|
||||||
_ = connection.subscribe(requestString: mockRequestString, variables: variables) { event, _ in
|
|
||||||
switch event {
|
|
||||||
case .connection(let status):
|
|
||||||
if status == .connected {
|
|
||||||
if !connectedOnce {
|
|
||||||
connectedOnce = true
|
|
||||||
connectedExpectation.fulfill()
|
|
||||||
} else {
|
|
||||||
connectedOnRetryExpectation.fulfill()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case .data:
|
|
||||||
XCTFail("Data event should not be published")
|
|
||||||
case .failed(let error):
|
|
||||||
guard let connection = error as? ConnectionProviderError,
|
|
||||||
case .connection = connection else {
|
|
||||||
XCTFail("Should be .connection")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
failedEvent.fulfill()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
wait(for: [connectedExpectation], timeout: 5)
|
|
||||||
XCTAssertEqual(connection.subscriptionState, .subscribed)
|
|
||||||
XCTAssertNotNil(connectionProvider.listener)
|
|
||||||
|
|
||||||
let connectionError = ConnectionProviderError.connection
|
|
||||||
connection.handleError(error: connectionError)
|
|
||||||
wait(for: [connectedOnRetryExpectation], timeout: 5)
|
|
||||||
XCTAssertEqual(connection.subscriptionState, .subscribed)
|
|
||||||
XCTAssertNotNil(connectionProvider.listener)
|
|
||||||
|
|
||||||
connection.handleError(error: connectionError)
|
|
||||||
wait(for: [failedEvent], timeout: 5)
|
|
||||||
XCTAssertEqual(connection.subscriptionState, .notSubscribed)
|
|
||||||
XCTAssertNil(connectionProvider.listener)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Helpers
|
|
||||||
|
|
||||||
class TestConnectionRetryHandler: ConnectionRetryHandler {
|
|
||||||
var count: Int = 0
|
|
||||||
let maxCount: Int
|
|
||||||
|
|
||||||
init(maxCount: Int = 10) {
|
|
||||||
self.maxCount = maxCount
|
|
||||||
}
|
|
||||||
|
|
||||||
func shouldRetryRequest(for error: ConnectionProviderError) -> RetryAdvice {
|
|
||||||
if count > maxCount {
|
|
||||||
return TestRetryAdvice(shouldRetry: false)
|
|
||||||
}
|
|
||||||
|
|
||||||
if case .connection = error {
|
|
||||||
self.count += 1
|
|
||||||
return TestRetryAdvice(shouldRetry: true, retryInterval: .seconds(1))
|
|
||||||
}
|
|
||||||
return TestRetryAdvice(shouldRetry: false)
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct TestRetryAdvice: RetryAdvice {
|
|
||||||
var shouldRetry: Bool
|
|
||||||
|
|
||||||
var retryInterval: DispatchTimeInterval?
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2020 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
@ -59,7 +59,6 @@ class AppSyncSubscriptionConnectionTests: XCTestCase {
|
||||||
}
|
}
|
||||||
XCTAssertNotNil(item, "Subscription item should not be nil")
|
XCTAssertNotNil(item, "Subscription item should not be nil")
|
||||||
wait(for: [connectingMessageExpectation, connectedMessageExpectation], timeout: 5, enforceOrder: true)
|
wait(for: [connectingMessageExpectation, connectedMessageExpectation], timeout: 5, enforceOrder: true)
|
||||||
XCTAssertNotNil(connectionProvider.listener)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Test unsubscribe subscription gives us back the right events
|
/// Test unsubscribe subscription gives us back the right events
|
||||||
|
@ -102,10 +101,8 @@ class AppSyncSubscriptionConnectionTests: XCTestCase {
|
||||||
XCTAssertNotNil(item, "Subscription item should not be nil")
|
XCTAssertNotNil(item, "Subscription item should not be nil")
|
||||||
wait(for: [connectingMessageExpectation, connectedMessageExpectation], timeout: 5, enforceOrder: true)
|
wait(for: [connectingMessageExpectation, connectedMessageExpectation], timeout: 5, enforceOrder: true)
|
||||||
|
|
||||||
XCTAssertNotNil(connectionProvider.listener)
|
|
||||||
connection.unsubscribe(item: item)
|
connection.unsubscribe(item: item)
|
||||||
wait(for: [unsubscribeAckExpectation], timeout: 2)
|
wait(for: [unsubscribeAckExpectation], timeout: 2)
|
||||||
XCTAssertNil(connectionProvider.listener)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Test subscription with invalid connection
|
/// Test subscription with invalid connection
|
||||||
|
@ -144,7 +141,6 @@ class AppSyncSubscriptionConnectionTests: XCTestCase {
|
||||||
}
|
}
|
||||||
XCTAssertNotNil(item, "Subscription item should not be nil")
|
XCTAssertNotNil(item, "Subscription item should not be nil")
|
||||||
wait(for: [connectingMessageExpectation, errorEventExpectation], timeout: 5, enforceOrder: true)
|
wait(for: [connectingMessageExpectation, errorEventExpectation], timeout: 5, enforceOrder: true)
|
||||||
XCTAssertNil(connectionProvider.listener)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Test if trying to subscribe with a 'not connected' connection gives error
|
/// Test if trying to subscribe with a 'not connected' connection gives error
|
||||||
|
@ -185,7 +181,6 @@ class AppSyncSubscriptionConnectionTests: XCTestCase {
|
||||||
}
|
}
|
||||||
XCTAssertNotNil(item, "Subscription item should not be nil")
|
XCTAssertNotNil(item, "Subscription item should not be nil")
|
||||||
wait(for: [connectingMessageExpectation, errorEventExpectation], timeout: 5, enforceOrder: true)
|
wait(for: [connectingMessageExpectation, errorEventExpectation], timeout: 5, enforceOrder: true)
|
||||||
XCTAssertNil(connectionProvider.listener)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Test if valid data is returned
|
/// Test if valid data is returned
|
||||||
|
@ -235,7 +230,6 @@ class AppSyncSubscriptionConnectionTests: XCTestCase {
|
||||||
)
|
)
|
||||||
connectionProvider.sendDataResponse(mockResponse)
|
connectionProvider.sendDataResponse(mockResponse)
|
||||||
wait(for: [dataEventExpectation], timeout: 2)
|
wait(for: [dataEventExpectation], timeout: 2)
|
||||||
XCTAssertNotNil(connectionProvider.listener)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func testNilDataInVariables() {
|
func testNilDataInVariables() {
|
||||||
|
@ -267,7 +261,6 @@ class AppSyncSubscriptionConnectionTests: XCTestCase {
|
||||||
}
|
}
|
||||||
XCTAssertNotNil(item, "Subscription item should not be nil")
|
XCTAssertNotNil(item, "Subscription item should not be nil")
|
||||||
wait(for: [connectingMessageExpectation, connectedMessageExpectation], timeout: 5, enforceOrder: true)
|
wait(for: [connectingMessageExpectation, connectedMessageExpectation], timeout: 5, enforceOrder: true)
|
||||||
XCTAssertNotNil(connectionProvider.listener)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,121 +0,0 @@
|
||||||
//
|
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
|
||||||
// All Rights Reserved.
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
//
|
|
||||||
|
|
||||||
import XCTest
|
|
||||||
@testable import AppSyncRealTimeClient
|
|
||||||
|
|
||||||
class RealTimeConnectionProviderResponseTests: XCTestCase {
|
|
||||||
|
|
||||||
func testIsMaxSubscriptionReached_EmptyPayload() throws {
|
|
||||||
let response = RealtimeConnectionProviderResponse(
|
|
||||||
id: "id",
|
|
||||||
payload: nil,
|
|
||||||
type: .error
|
|
||||||
)
|
|
||||||
|
|
||||||
XCTAssertFalse(response.isMaxSubscriptionReachedError())
|
|
||||||
XCTAssertEqual(response.toConnectionProviderError(connectionState: .connected), .subscription("id", nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
func testIsMaxSubscriptionReached_MaxSubscriptionsReachedException() throws {
|
|
||||||
let payload = ["errorType": AppSyncJSONValue.string("MaxSubscriptionsReachedException")]
|
|
||||||
let response = RealtimeConnectionProviderResponse(
|
|
||||||
id: "id",
|
|
||||||
payload: payload,
|
|
||||||
type: .error
|
|
||||||
)
|
|
||||||
|
|
||||||
XCTAssertTrue(response.isMaxSubscriptionReachedError())
|
|
||||||
XCTAssertEqual(response.toConnectionProviderError(connectionState: .connected), .limitExceeded("id"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func testIsMaxSubscriptionReached_MaxSubscriptionsReachedError() throws {
|
|
||||||
let payload = ["errors": AppSyncJSONValue.object(["errorType": "MaxSubscriptionsReachedError"])]
|
|
||||||
let response = RealtimeConnectionProviderResponse(
|
|
||||||
id: "id",
|
|
||||||
payload: payload,
|
|
||||||
type: .error
|
|
||||||
)
|
|
||||||
|
|
||||||
XCTAssertTrue(response.isMaxSubscriptionReachedError())
|
|
||||||
XCTAssertEqual(response.toConnectionProviderError(connectionState: .connected), .limitExceeded("id"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func testIsLimitExceeded_EmptyPayload() throws {
|
|
||||||
let response = RealtimeConnectionProviderResponse(
|
|
||||||
payload: nil,
|
|
||||||
type: .error
|
|
||||||
)
|
|
||||||
|
|
||||||
XCTAssertFalse(response.isLimitExceededError())
|
|
||||||
XCTAssertEqual(
|
|
||||||
response.toConnectionProviderError(connectionState: .connected),
|
|
||||||
.unknown(message: nil, causedBy: nil, payload: nil)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testIsLimitExceeded_LimitExceededError() throws {
|
|
||||||
let payload = ["errors": AppSyncJSONValue.object(["errorType": "LimitExceededError"])]
|
|
||||||
let response = RealtimeConnectionProviderResponse(
|
|
||||||
payload: payload,
|
|
||||||
type: .error
|
|
||||||
)
|
|
||||||
|
|
||||||
XCTAssertTrue(response.isLimitExceededError())
|
|
||||||
XCTAssertEqual(response.toConnectionProviderError(connectionState: .connected), .limitExceeded(nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
func testIsUnauthorized_EmptyPayload() throws {
|
|
||||||
let response = RealtimeConnectionProviderResponse(
|
|
||||||
payload: nil,
|
|
||||||
type: .error
|
|
||||||
)
|
|
||||||
|
|
||||||
XCTAssertFalse(response.isUnauthorizationError())
|
|
||||||
XCTAssertEqual(
|
|
||||||
response.toConnectionProviderError(connectionState: .connected),
|
|
||||||
.unknown(message: nil, causedBy: nil, payload: nil)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testIsUnauthorized_UnauthorizedException() throws {
|
|
||||||
let payload = ["errors": AppSyncJSONValue.array([
|
|
||||||
["errorType": "com.amazonaws.deepdish.graphql.auth#UnauthorizedException"]
|
|
||||||
])]
|
|
||||||
let response = RealtimeConnectionProviderResponse(
|
|
||||||
payload: payload,
|
|
||||||
type: .error
|
|
||||||
)
|
|
||||||
|
|
||||||
XCTAssertTrue(response.isUnauthorizationError())
|
|
||||||
XCTAssertEqual(
|
|
||||||
response.toConnectionProviderError(connectionState: .connected),
|
|
||||||
.unauthorized
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension ConnectionProviderError: Equatable {
|
|
||||||
public static func == (lhs: ConnectionProviderError, rhs: ConnectionProviderError) -> Bool {
|
|
||||||
switch (lhs, rhs) {
|
|
||||||
case (.connection, .connection):
|
|
||||||
return true
|
|
||||||
case (.jsonParse, .jsonParse):
|
|
||||||
return true
|
|
||||||
case (.limitExceeded(let id1), .limitExceeded(let id2)):
|
|
||||||
return id1 == id2
|
|
||||||
case (.subscription(let id1, _), .subscription(let id2, _)):
|
|
||||||
return id1 == id2
|
|
||||||
case (.unauthorized, .unauthorized):
|
|
||||||
return true
|
|
||||||
case (.unknown(let message1, _, _), .unknown(let message2, _, _)):
|
|
||||||
return message1 == message2
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,228 +0,0 @@
|
||||||
//
|
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
|
||||||
// All Rights Reserved.
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
//
|
|
||||||
|
|
||||||
import XCTest
|
|
||||||
@testable import AppSyncRealTimeClient
|
|
||||||
|
|
||||||
class ConnectionProviderAsyncTests: RealtimeConnectionProviderTestBase {
|
|
||||||
|
|
||||||
/// Provider test
|
|
||||||
///
|
|
||||||
/// Given:
|
|
||||||
/// - A configured subscriber -> provider -> websocket chain
|
|
||||||
/// When:
|
|
||||||
/// - I invoke `provider.connect()`
|
|
||||||
/// - And the websocket properly connects
|
|
||||||
/// Then:
|
|
||||||
/// - The subscriber is notified of the successful connection
|
|
||||||
func testSuccessfulConnection() {
|
|
||||||
receivedNotConnected.isInverted = true
|
|
||||||
receivedError.isInverted = true
|
|
||||||
|
|
||||||
let onConnect: MockWebsocketProvider.OnConnect = { _, _, delegate in
|
|
||||||
self.websocketDelegate = delegate
|
|
||||||
DispatchQueue.global().async {
|
|
||||||
delegate?.websocketDidConnect(provider: self.websocket)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let onDisconnect: MockWebsocketProvider.OnDisconnect = { }
|
|
||||||
|
|
||||||
let onWrite: MockWebsocketProvider.OnWrite = { message in
|
|
||||||
guard RealtimeConnectionProviderTestBase.messageType(of: message, equals: "connection_init") else {
|
|
||||||
XCTFail("Incoming message did not have 'connection_init' type")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
self.websocketDelegate.websocketDidReceiveData(
|
|
||||||
provider: self.websocket,
|
|
||||||
data: RealtimeConnectionProviderTestBase.makeConnectionAckMessage()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
websocket = MockWebsocketProvider(
|
|
||||||
onConnect: onConnect,
|
|
||||||
onDisconnect: onDisconnect,
|
|
||||||
onWrite: onWrite
|
|
||||||
)
|
|
||||||
|
|
||||||
// Retain the provider so it doesn't release prior to executing callbacks
|
|
||||||
let provider = createProviderAndConnect()
|
|
||||||
|
|
||||||
// Get rid of "written to, but never read" compiler warnings
|
|
||||||
print(provider)
|
|
||||||
|
|
||||||
waitForExpectations(timeout: 0.05)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Provider add and remove listeners tests
|
|
||||||
///
|
|
||||||
/// Given:
|
|
||||||
/// - A connected websocket with a listener
|
|
||||||
/// When:
|
|
||||||
/// - remove all listeners
|
|
||||||
/// Then:
|
|
||||||
/// - The listeners are removed and the connection is disconnected
|
|
||||||
func testAddRemoveListeners() {
|
|
||||||
receivedNotConnected.isInverted = true
|
|
||||||
receivedError.isInverted = true
|
|
||||||
|
|
||||||
let onConnect: MockWebsocketProvider.OnConnect = { _, _, delegate in
|
|
||||||
self.websocketDelegate = delegate
|
|
||||||
DispatchQueue.global().async {
|
|
||||||
delegate?.websocketDidConnect(provider: self.websocket)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let receivedDisconnect = expectation(description: "receivedDisconnect")
|
|
||||||
let onDisconnect: MockWebsocketProvider.OnDisconnect = {
|
|
||||||
receivedDisconnect.fulfill()
|
|
||||||
}
|
|
||||||
|
|
||||||
let onWrite: MockWebsocketProvider.OnWrite = { message in
|
|
||||||
guard RealtimeConnectionProviderTestBase.messageType(of: message, equals: "connection_init") else {
|
|
||||||
XCTFail("Incoming message did not have 'connection_init' type")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
self.websocketDelegate.websocketDidReceiveData(
|
|
||||||
provider: self.websocket,
|
|
||||||
data: RealtimeConnectionProviderTestBase.makeConnectionAckMessage()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
websocket = MockWebsocketProvider(
|
|
||||||
onConnect: onConnect,
|
|
||||||
onDisconnect: onDisconnect,
|
|
||||||
onWrite: onWrite
|
|
||||||
)
|
|
||||||
|
|
||||||
// Retain the provider so it doesn't release prior to executing callbacks
|
|
||||||
let provider = createProviderAndConnect(listeners: ["1", "2", "3", "4"])
|
|
||||||
|
|
||||||
wait(
|
|
||||||
for: [receivedInProgress, receivedConnected, receivedNotConnected, receivedError],
|
|
||||||
timeout: 1
|
|
||||||
)
|
|
||||||
|
|
||||||
XCTAssertFalse(provider.listeners.isEmpty)
|
|
||||||
|
|
||||||
let listenersToRemove = provider.listeners.map { $0.key }
|
|
||||||
|
|
||||||
// Removing all the listeners will disconnect the websocket connection
|
|
||||||
for identifier in listenersToRemove {
|
|
||||||
provider.removeListener(identifier: identifier)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Since removing listeners is asynchronous, we have to wait for the disconnect
|
|
||||||
wait(for: [receivedDisconnect], timeout: 1)
|
|
||||||
XCTAssertTrue(provider.listeners.isEmpty)
|
|
||||||
XCTAssertEqual(provider.status, .notConnected)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Provider test
|
|
||||||
///
|
|
||||||
/// Given:
|
|
||||||
/// - A configured subscriber -> provider -> websocket chain
|
|
||||||
/// When:
|
|
||||||
/// - I invoke `provider.connect()`
|
|
||||||
/// - And the websocket reports a connection error
|
|
||||||
/// Then:
|
|
||||||
/// - The subscriber is notified of the unsuccessful connection
|
|
||||||
func testConnectionError() {
|
|
||||||
receivedConnected.isInverted = true
|
|
||||||
receivedNotConnected.isInverted = true
|
|
||||||
|
|
||||||
let onConnect: MockWebsocketProvider.OnConnect = { _, _, delegate in
|
|
||||||
self.websocketDelegate = delegate
|
|
||||||
DispatchQueue.global().async {
|
|
||||||
delegate?.websocketDidConnect(provider: self.websocket)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let onDisconnect: MockWebsocketProvider.OnDisconnect = { }
|
|
||||||
|
|
||||||
let onWrite: MockWebsocketProvider.OnWrite = { message in
|
|
||||||
guard RealtimeConnectionProviderTestBase.messageType(of: message, equals: "connection_init") else {
|
|
||||||
XCTFail("Incoming message did not have 'connection_init' type")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
self.websocketDelegate.websocketDidDisconnect(
|
|
||||||
provider: self.websocket,
|
|
||||||
error: "test error"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
websocket = MockWebsocketProvider(
|
|
||||||
onConnect: onConnect,
|
|
||||||
onDisconnect: onDisconnect,
|
|
||||||
onWrite: onWrite
|
|
||||||
)
|
|
||||||
|
|
||||||
// Retain the provider so it doesn't release prior to executing callbacks
|
|
||||||
let provider = createProviderAndConnect()
|
|
||||||
|
|
||||||
// Get rid of "written to, but never read" compiler warnings
|
|
||||||
print(provider)
|
|
||||||
|
|
||||||
waitForExpectations(timeout: 0.05)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Stale connection test
|
|
||||||
///
|
|
||||||
/// Given:
|
|
||||||
/// - A provider configured with a default stale connection timeout
|
|
||||||
/// When:
|
|
||||||
/// - The service sends a message containing an override timeout value
|
|
||||||
/// Then:
|
|
||||||
/// - The provider updates its stale connection timeout to the service-provided value
|
|
||||||
func testServiceOverridesStaleConnectionTimeout() {
|
|
||||||
receivedNotConnected.isInverted = true
|
|
||||||
receivedError.isInverted = true
|
|
||||||
|
|
||||||
let expectedTimeoutInSeconds = 60.0
|
|
||||||
let timeoutInMilliseconds = Int(expectedTimeoutInSeconds) * 1_000
|
|
||||||
|
|
||||||
let onConnect: MockWebsocketProvider.OnConnect = { _, _, delegate in
|
|
||||||
self.websocketDelegate = delegate
|
|
||||||
DispatchQueue.global().async {
|
|
||||||
delegate?.websocketDidConnect(provider: self.websocket)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let onDisconnect: MockWebsocketProvider.OnDisconnect = { }
|
|
||||||
|
|
||||||
let connectionAckMessage = RealtimeConnectionProviderTestBase
|
|
||||||
.makeConnectionAckMessage(withTimeout: timeoutInMilliseconds)
|
|
||||||
let onWrite: MockWebsocketProvider.OnWrite = { message in
|
|
||||||
guard RealtimeConnectionProviderTestBase.messageType(of: message, equals: "connection_init") else {
|
|
||||||
XCTFail("Incoming message did not have 'connection_init' type")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
self.websocketDelegate.websocketDidReceiveData(
|
|
||||||
provider: self.websocket,
|
|
||||||
data: connectionAckMessage
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
websocket = MockWebsocketProvider(
|
|
||||||
onConnect: onConnect,
|
|
||||||
onDisconnect: onDisconnect,
|
|
||||||
onWrite: onWrite
|
|
||||||
)
|
|
||||||
|
|
||||||
let provider = createProviderAndConnect()
|
|
||||||
|
|
||||||
wait(for: [receivedConnected], timeout: 0.05)
|
|
||||||
XCTAssertEqual(provider.staleConnectionTimer.interval, expectedTimeoutInSeconds)
|
|
||||||
|
|
||||||
waitForExpectations(timeout: 0.05)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,107 +0,0 @@
|
||||||
//
|
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
|
||||||
// All Rights Reserved.
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
//
|
|
||||||
|
|
||||||
import XCTest
|
|
||||||
@testable import AppSyncRealTimeClient
|
|
||||||
|
|
||||||
class RealtimeConnectionProviderAsyncTestBase: XCTestCase {
|
|
||||||
|
|
||||||
let urlRequest = URLRequest(url: URL(string: "https://www.appsyncrealtimeclient.test/")!)
|
|
||||||
|
|
||||||
var websocket: MockWebsocketProvider!
|
|
||||||
|
|
||||||
// swiftlint:disable:next weak_delegate
|
|
||||||
var websocketDelegate: AppSyncWebsocketDelegate!
|
|
||||||
|
|
||||||
// Shared test expectations. Set expected fulfillment counts and inversions as
|
|
||||||
// needed in the body of the test.
|
|
||||||
var receivedInProgress: XCTestExpectation!
|
|
||||||
var receivedConnected: XCTestExpectation!
|
|
||||||
var receivedNotConnected: XCTestExpectation!
|
|
||||||
var receivedError: XCTestExpectation!
|
|
||||||
|
|
||||||
override func setUp() {
|
|
||||||
receivedInProgress = expectation(description: "receivedInProgress")
|
|
||||||
receivedConnected = expectation(description: "receivedConnected")
|
|
||||||
receivedNotConnected = expectation(description: "receivedNotConnected")
|
|
||||||
receivedError = expectation(description: "receivedError")
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Utilities
|
|
||||||
|
|
||||||
/// Creates a RealtimeConnectionProvider, adds a listener that fulfills the shared
|
|
||||||
/// expectations as appropriate, and invokes `connect()`. Returns the provider for
|
|
||||||
/// subsequent testing.
|
|
||||||
///
|
|
||||||
/// Preconditions:
|
|
||||||
/// - `self.websocket` must be initialized in the mock provider's `onConnect`
|
|
||||||
func createProviderAndConnect(
|
|
||||||
listeners: [String]? = nil,
|
|
||||||
serialCallbackQueue: DispatchQueue = DispatchQueue(
|
|
||||||
label: "com.amazonaws.RealtimeConnectionProviderTestBase.serialCallbackQueue"
|
|
||||||
),
|
|
||||||
connectivityMonitor: ConnectivityMonitor = ConnectivityMonitor()
|
|
||||||
) -> RealtimeConnectionProvider {
|
|
||||||
let provider = RealtimeConnectionProvider(
|
|
||||||
urlRequest: urlRequest,
|
|
||||||
websocket: websocket,
|
|
||||||
serialCallbackQueue: serialCallbackQueue,
|
|
||||||
connectivityMonitor: connectivityMonitor
|
|
||||||
)
|
|
||||||
provider.addListener(identifier: "testListener") { event in
|
|
||||||
switch event {
|
|
||||||
case .connection(let connectionState):
|
|
||||||
switch connectionState {
|
|
||||||
case .inProgress:
|
|
||||||
self.receivedInProgress.fulfill()
|
|
||||||
case .connected:
|
|
||||||
self.receivedConnected.fulfill()
|
|
||||||
case .notConnected:
|
|
||||||
self.receivedNotConnected.fulfill()
|
|
||||||
}
|
|
||||||
case .error:
|
|
||||||
self.receivedError.fulfill()
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let listeners = listeners {
|
|
||||||
listeners.forEach { identifier in
|
|
||||||
provider.addListener(identifier: identifier) { _ in }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
provider.connect()
|
|
||||||
return provider
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Given a Stringified AppSyncMessage, validates the `type` is equal to `expectedType`
|
|
||||||
/// - Parameter message: a string representation of a websocket message
|
|
||||||
/// - Parameter expectedType: the expected value of the type
|
|
||||||
/// - Returns: type `type` field of the message, if present
|
|
||||||
static func messageType(of message: String, equals expectedType: String) -> Bool {
|
|
||||||
guard
|
|
||||||
let messageData = message.data(using: .utf8),
|
|
||||||
let dict = try? JSONSerialization.jsonObject(with: messageData) as? [String: String]
|
|
||||||
else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
guard let type = dict["type"] else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return type == expectedType
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a connection acknowledgement message with the specified timeout
|
|
||||||
/// - Parameter timeout: stale connection timeout, in milliseconds (defaults to 300,000)
|
|
||||||
static func makeConnectionAckMessage(withTimeout timeout: Int = 300_000) -> Data {
|
|
||||||
#"{"type":"connection_ack","payload":{"connectionTimeoutMs":\#(timeout)}}"#
|
|
||||||
.data(using: .utf8)!
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,166 +0,0 @@
|
||||||
//
|
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
|
||||||
// All Rights Reserved.
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
//
|
|
||||||
|
|
||||||
import XCTest
|
|
||||||
@testable import AppSyncRealTimeClient
|
|
||||||
|
|
||||||
class ConnectionProviderHandleErrorTests: XCTestCase {
|
|
||||||
let urlRequest = URLRequest(url: URL(string: "https://www.appsyncrealtimeclient.test/")!)
|
|
||||||
var websocket = MockWebsocketProvider()
|
|
||||||
|
|
||||||
/// Error response is limit exceeded with id
|
|
||||||
/// Should receive ConnectionProviderError.limitExceeded with id
|
|
||||||
func testLimitExceededWithId() {
|
|
||||||
let provider = RealtimeConnectionProvider(urlRequest: urlRequest, websocket: websocket)
|
|
||||||
|
|
||||||
let subscriptionEvent = expectation(description: "Receieved subscription event")
|
|
||||||
provider.addListener(identifier: "id") { event in
|
|
||||||
guard case .error(let error) = event,
|
|
||||||
let connectionError = error as? ConnectionProviderError else {
|
|
||||||
XCTFail("Should have received error event")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
guard case .limitExceeded(let id) = connectionError else {
|
|
||||||
XCTFail("Should have received .limitExceeded error")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
XCTAssertEqual(id, "id")
|
|
||||||
subscriptionEvent.fulfill()
|
|
||||||
}
|
|
||||||
let payload = ["errors": AppSyncJSONValue.object(["errorType": "LimitExceededError"])]
|
|
||||||
let response = RealtimeConnectionProviderResponse.init(id: "id", payload: payload, type: .error)
|
|
||||||
|
|
||||||
provider.handleError(response: response)
|
|
||||||
wait(for: [subscriptionEvent], timeout: 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Error response is limit exceeded (connection level error without subscription id)
|
|
||||||
/// Should throttle and receive a fraction of ConnectionProviderError.limitExceeded event without id
|
|
||||||
@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
|
|
||||||
func testLimitExceededMissingIdThrottled() {
|
|
||||||
let provider = RealtimeConnectionProvider(urlRequest: urlRequest, websocket: websocket)
|
|
||||||
let limitExceededThrottle = expectation(description: "received limit exceeded")
|
|
||||||
limitExceededThrottle.expectedFulfillmentCount = 100
|
|
||||||
let sink = provider.limitExceededSubject.sink { error in
|
|
||||||
guard case .limitExceeded(let id) = error else {
|
|
||||||
XCTFail("Should have received .limitExceeded error")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
XCTAssertNil(id)
|
|
||||||
limitExceededThrottle.fulfill()
|
|
||||||
}
|
|
||||||
let subscriptionEvent = expectation(description: "Receieved subscription event")
|
|
||||||
subscriptionEvent.assertForOverFulfill = false
|
|
||||||
var subscriptionEventCount = 0
|
|
||||||
provider.addListener(identifier: "id") { event in
|
|
||||||
guard case .error(let error) = event,
|
|
||||||
let connectionError = error as? ConnectionProviderError else {
|
|
||||||
XCTFail("Should have received error event")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
guard case .limitExceeded(let id) = connectionError else {
|
|
||||||
XCTFail("Should have received .limitExceeded error")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
XCTAssertNil(id)
|
|
||||||
subscriptionEventCount += 1
|
|
||||||
subscriptionEvent.fulfill()
|
|
||||||
}
|
|
||||||
let payload = ["errors": AppSyncJSONValue.object(["errorType": "LimitExceededError"])]
|
|
||||||
let response = RealtimeConnectionProviderResponse.init(id: nil, payload: payload, type: .error)
|
|
||||||
|
|
||||||
DispatchQueue.concurrentPerform(iterations: 100) { _ in
|
|
||||||
provider.handleError(response: response)
|
|
||||||
}
|
|
||||||
|
|
||||||
wait(for: [subscriptionEvent, limitExceededThrottle], timeout: 1)
|
|
||||||
// The number of subscription events received should be a fraction of the number of throttled events (100)
|
|
||||||
XCTAssertTrue(subscriptionEventCount < 10)
|
|
||||||
sink.cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Error response is max subscription with subscription id
|
|
||||||
/// Should receive ConnectionProviderError.limitExceeded with id
|
|
||||||
func testMaxSubscriptionReached() {
|
|
||||||
let provider = RealtimeConnectionProvider(urlRequest: urlRequest, websocket: websocket)
|
|
||||||
|
|
||||||
let subscriptionEvent = expectation(description: "Receieved subscription event")
|
|
||||||
provider.addListener(identifier: "id") { event in
|
|
||||||
guard case .error(let error) = event,
|
|
||||||
let connectionError = error as? ConnectionProviderError else {
|
|
||||||
XCTFail("Should have received error event")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
guard case .limitExceeded(let id) = connectionError else {
|
|
||||||
XCTFail("Should have received .limitExceeded error")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
XCTAssertEqual(id, "id")
|
|
||||||
subscriptionEvent.fulfill()
|
|
||||||
}
|
|
||||||
let payload = ["errors": AppSyncJSONValue.object(["errorType": "MaxSubscriptionsReachedError"])]
|
|
||||||
let response = RealtimeConnectionProviderResponse.init(id: "id", payload: payload, type: .error)
|
|
||||||
|
|
||||||
provider.handleError(response: response)
|
|
||||||
wait(for: [subscriptionEvent], timeout: 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Error response with no indication for which subscription and missing payload
|
|
||||||
/// Should receive ConnectionProviderError.other
|
|
||||||
func testMissingId() throws {
|
|
||||||
let provider = RealtimeConnectionProvider(urlRequest: urlRequest, websocket: websocket)
|
|
||||||
|
|
||||||
let subscriptionEvent = expectation(description: "Receieved subscription event")
|
|
||||||
provider.addListener(identifier: "id") { event in
|
|
||||||
guard case .error(let error) = event,
|
|
||||||
let connectionError = error as? ConnectionProviderError else {
|
|
||||||
XCTFail("Should have received error event")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
guard case .unknown = connectionError else {
|
|
||||||
XCTFail("Should have received .unknown error")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
subscriptionEvent.fulfill()
|
|
||||||
}
|
|
||||||
let response = RealtimeConnectionProviderResponse.init(id: nil, payload: nil, type: .error)
|
|
||||||
|
|
||||||
provider.handleError(response: response)
|
|
||||||
wait(for: [subscriptionEvent], timeout: 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Error response subscription id and payload
|
|
||||||
/// Should receive ConnectionProviderError.subscription(id, payload)
|
|
||||||
func testWithSubscriptionId() throws {
|
|
||||||
let provider = RealtimeConnectionProvider(urlRequest: urlRequest, websocket: websocket)
|
|
||||||
|
|
||||||
let subscriptionEvent = expectation(description: "Receieved subscription event")
|
|
||||||
provider.addListener(identifier: "id") { event in
|
|
||||||
guard case .error(let error) = event,
|
|
||||||
let connectionError = error as? ConnectionProviderError else {
|
|
||||||
XCTFail("Should have received error event")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
guard case .subscription(let id, let payload) = connectionError else {
|
|
||||||
XCTFail("Should have received .subscription error")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
XCTAssertEqual(id, "id")
|
|
||||||
XCTAssertNotNil(payload)
|
|
||||||
subscriptionEvent.fulfill()
|
|
||||||
}
|
|
||||||
let response = RealtimeConnectionProviderResponse.init(
|
|
||||||
id: "id",
|
|
||||||
payload: ["errorType": "SomeError"],
|
|
||||||
type: .error
|
|
||||||
)
|
|
||||||
|
|
||||||
provider.handleError(response: response)
|
|
||||||
wait(for: [subscriptionEvent], timeout: 1)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2020 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
@ -57,92 +57,9 @@ class ConnectionProviderStaleConnectionTests: RealtimeConnectionProviderTestBase
|
||||||
let provider = createProviderAndConnect()
|
let provider = createProviderAndConnect()
|
||||||
|
|
||||||
wait(for: [receivedConnected], timeout: 0.05)
|
wait(for: [receivedConnected], timeout: 0.05)
|
||||||
XCTAssertEqual(provider.staleConnectionTimer.interval, expectedTimeoutInSeconds)
|
XCTAssertEqual(provider.staleConnectionTimeout.get(), expectedTimeoutInSeconds)
|
||||||
|
|
||||||
waitForExpectations(timeout: 0.05)
|
waitForExpectations(timeout: 0.05)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Given a connected websocket, when the network status toggles to disconnected and back to connected,
|
|
||||||
/// the connecion should be disconnected with `error`.
|
|
||||||
/// Note: This error is handled by the subscriptions to attempt to reconnect the websocket.
|
|
||||||
///
|
|
||||||
/// - Given: Connected websocket
|
|
||||||
/// - When:
|
|
||||||
/// - Connectivity updates to unsatisfied (network is down)
|
|
||||||
/// - Connectivity updates to satisfied (network is back up)
|
|
||||||
/// - Then:
|
|
||||||
/// - Websocket is disconnected
|
|
||||||
func testConnectionDisconnectsAfterConnectivityUpdates() {
|
|
||||||
receivedNotConnected.isInverted = true
|
|
||||||
receivedError.isInverted = true
|
|
||||||
|
|
||||||
let onConnect: MockWebsocketProvider.OnConnect = { _, _, delegate in
|
|
||||||
self.websocketDelegate = delegate
|
|
||||||
DispatchQueue.global().async {
|
|
||||||
delegate?.websocketDidConnect(provider: self.websocket)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let onDisconnect: MockWebsocketProvider.OnDisconnect = { }
|
|
||||||
|
|
||||||
let onWrite: MockWebsocketProvider.OnWrite = { message in
|
|
||||||
guard RealtimeConnectionProviderTestBase.messageType(of: message, equals: "connection_init") else {
|
|
||||||
XCTFail("Incoming message did not have 'connection_init' type")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
self.websocketDelegate.websocketDidReceiveData(
|
|
||||||
provider: self.websocket,
|
|
||||||
data: RealtimeConnectionProviderTestBase.makeConnectionAckMessage()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
websocket = MockWebsocketProvider(
|
|
||||||
onConnect: onConnect,
|
|
||||||
onDisconnect: onDisconnect,
|
|
||||||
onWrite: onWrite
|
|
||||||
)
|
|
||||||
let connectionQueue = DispatchQueue(
|
|
||||||
label: "com.amazonaws.ConnectionProviderStaleConnectionTests.connectionQueue")
|
|
||||||
|
|
||||||
let monitor = MockConnectivityMonitor()
|
|
||||||
let connectivityMonitor = ConnectivityMonitor(monitor: monitor)
|
|
||||||
|
|
||||||
// Retain the provider so it doesn't release prior to executing callbacks
|
|
||||||
let provider = createProviderAndConnect(
|
|
||||||
listeners: nil,
|
|
||||||
connectionQueue: connectionQueue,
|
|
||||||
connectivityMonitor: connectivityMonitor
|
|
||||||
)
|
|
||||||
|
|
||||||
// Wait for websocket to be connected
|
|
||||||
waitForExpectations(timeout: 0.05)
|
|
||||||
|
|
||||||
// Send connectivity update - network down
|
|
||||||
monitor.sendConnectivityUpdate(.init(status: .unsatisfied))
|
|
||||||
let connectionIsStale = expectation(description: "connection is stale")
|
|
||||||
connectionQueue.asyncAfter(deadline: .now() + 0.5) {
|
|
||||||
XCTAssertTrue(provider.isStaleConnection)
|
|
||||||
connectionIsStale.fulfill()
|
|
||||||
}
|
|
||||||
wait(for: [connectionIsStale], timeout: 1.0)
|
|
||||||
|
|
||||||
// Send connectivity update - network up
|
|
||||||
let receivedError = expectation(description: "listeners receive error event")
|
|
||||||
provider.addListener(identifier: "id") { event in
|
|
||||||
guard case .error = event else {
|
|
||||||
XCTFail("Event should be error")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
receivedError.fulfill()
|
|
||||||
}
|
|
||||||
monitor.sendConnectivityUpdate(.init(status: .satisfied))
|
|
||||||
let connectionIsDisconnected = expectation(description: "connection is disconnected")
|
|
||||||
connectionQueue.asyncAfter(deadline: .now() + 0.5) {
|
|
||||||
XCTAssertFalse(provider.isStaleConnection)
|
|
||||||
XCTAssertTrue(provider.status == .notConnected)
|
|
||||||
connectionIsDisconnected.fulfill()
|
|
||||||
}
|
|
||||||
wait(for: [connectionIsDisconnected, receivedError], timeout: 1.0)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2021 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
@ -220,7 +220,7 @@ class ConnectionProviderTests: RealtimeConnectionProviderTestBase {
|
||||||
let provider = createProviderAndConnect()
|
let provider = createProviderAndConnect()
|
||||||
|
|
||||||
wait(for: [receivedConnected], timeout: 0.05)
|
wait(for: [receivedConnected], timeout: 0.05)
|
||||||
XCTAssertEqual(provider.staleConnectionTimer.interval, expectedTimeoutInSeconds)
|
XCTAssertEqual(provider.staleConnectionTimeout.get(), expectedTimeoutInSeconds)
|
||||||
|
|
||||||
waitForExpectations(timeout: 0.05)
|
waitForExpectations(timeout: 0.05)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2021 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
@ -10,11 +10,11 @@ import XCTest
|
||||||
|
|
||||||
class RealtimeConnectionProviderTestBase: XCTestCase {
|
class RealtimeConnectionProviderTestBase: XCTestCase {
|
||||||
|
|
||||||
let urlRequest = URLRequest(url: URL(string: "https://www.appsyncrealtimeclient.test/")!)
|
let url = URL(string: "https://www.appsyncrealtimeclient.test/")!
|
||||||
|
|
||||||
var websocket: MockWebsocketProvider!
|
var websocket: MockWebsocketProvider!
|
||||||
|
|
||||||
// swiftlint:disable:next weak_delegate
|
//swiftlint:disable:next weak_delegate
|
||||||
var websocketDelegate: AppSyncWebsocketDelegate!
|
var websocketDelegate: AppSyncWebsocketDelegate!
|
||||||
|
|
||||||
// Shared test expectations. Set expected fulfillment counts and inversions as
|
// Shared test expectations. Set expected fulfillment counts and inversions as
|
||||||
|
@ -39,23 +39,8 @@ class RealtimeConnectionProviderTestBase: XCTestCase {
|
||||||
///
|
///
|
||||||
/// Preconditions:
|
/// Preconditions:
|
||||||
/// - `self.websocket` must be initialized in the mock provider's `onConnect`
|
/// - `self.websocket` must be initialized in the mock provider's `onConnect`
|
||||||
func createProviderAndConnect(
|
func createProviderAndConnect(listeners: [String]? = nil) -> RealtimeConnectionProvider {
|
||||||
listeners: [String]? = nil,
|
let provider = RealtimeConnectionProvider(for: url, websocket: websocket)
|
||||||
connectionQueue: DispatchQueue = DispatchQueue(
|
|
||||||
label: "com.amazonaws.RealtimeConnectionProviderTestBase.connectionQueue"
|
|
||||||
),
|
|
||||||
serialCallbackQueue: DispatchQueue = DispatchQueue(
|
|
||||||
label: "com.amazonaws.RealtimeConnectionProviderTestBase.serialCallbackQueue"
|
|
||||||
),
|
|
||||||
connectivityMonitor: ConnectivityMonitor = ConnectivityMonitor()
|
|
||||||
) -> RealtimeConnectionProvider {
|
|
||||||
let provider = RealtimeConnectionProvider(
|
|
||||||
urlRequest: urlRequest,
|
|
||||||
websocket: websocket,
|
|
||||||
connectionQueue: connectionQueue,
|
|
||||||
serialCallbackQueue: serialCallbackQueue,
|
|
||||||
connectivityMonitor: connectivityMonitor
|
|
||||||
)
|
|
||||||
provider.addListener(identifier: "testListener") { event in
|
provider.addListener(identifier: "testListener") { event in
|
||||||
switch event {
|
switch event {
|
||||||
case .connection(let connectionState):
|
case .connection(let connectionState):
|
||||||
|
|
|
@ -1,43 +0,0 @@
|
||||||
//
|
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
|
||||||
// All Rights Reserved.
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
//
|
|
||||||
|
|
||||||
import XCTest
|
|
||||||
@testable import AppSyncRealTimeClient
|
|
||||||
import Network
|
|
||||||
|
|
||||||
class ConnectivityMonitorTests: XCTestCase {
|
|
||||||
|
|
||||||
/// Test `NetworkMonitor` that uses `NWPathMonitor`
|
|
||||||
/// Only assert that the connectivity update isn't `nil` since the status is based on the conditions
|
|
||||||
/// of when test is run.
|
|
||||||
func testNetworkMonitor() {
|
|
||||||
let connectivityUpdateReceived = expectation(description: "connectivity update received")
|
|
||||||
let monitor = ConnectivityMonitor(monitor: nil)
|
|
||||||
monitor.start { connectivity in
|
|
||||||
connectivityUpdateReceived.fulfill()
|
|
||||||
XCTAssertNotNil(connectivity)
|
|
||||||
}
|
|
||||||
wait(for: [connectivityUpdateReceived], timeout: 1)
|
|
||||||
monitor.cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
func testMockConnectivityMonitor() {
|
|
||||||
let mockMonitor = MockConnectivityMonitor()
|
|
||||||
let monitor = ConnectivityMonitor(monitor: mockMonitor)
|
|
||||||
let connectivityUpdateReceived = expectation(description: "connectivity update received")
|
|
||||||
connectivityUpdateReceived.expectedFulfillmentCount = 3
|
|
||||||
monitor.start { updates in
|
|
||||||
XCTAssertNotNil(updates)
|
|
||||||
connectivityUpdateReceived.fulfill()
|
|
||||||
}
|
|
||||||
mockMonitor.sendConnectivityUpdate(.init())
|
|
||||||
mockMonitor.sendConnectivityUpdate(.init())
|
|
||||||
mockMonitor.sendConnectivityUpdate(.init())
|
|
||||||
wait(for: [connectivityUpdateReceived], timeout: 1)
|
|
||||||
monitor.cancel()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2020 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2020 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
|
|
@ -1,45 +0,0 @@
|
||||||
//
|
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
|
||||||
// All Rights Reserved.
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
//
|
|
||||||
|
|
||||||
import XCTest
|
|
||||||
import AppSyncRealTimeClient
|
|
||||||
|
|
||||||
class AppSyncURLHelperTests: XCTestCase {
|
|
||||||
|
|
||||||
/// Test if given graphql endpoint has standard appsync domain
|
|
||||||
///
|
|
||||||
/// - Given: A graphql endpoint with standard appsync domain
|
|
||||||
/// - When: I invoke AppSyncURLHelper.hasStandardAppsyncGraphQLDomain()
|
|
||||||
/// - Then: It should return true
|
|
||||||
func testURLStandardAppSyncDomain() {
|
|
||||||
let standardDomainURL =
|
|
||||||
URL(string: "https://abcdefghijklmnopqrstuvwxyz.appsync-api.us-west-2.amazonaws.com/graphql")!
|
|
||||||
XCTAssertTrue(AppSyncURLHelper.isStandardAppSyncGraphQLEndpoint(url: standardDomainURL))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Test if given graphql endpoint in capital letters has standard appsync domain
|
|
||||||
///
|
|
||||||
/// - Given: A graphql endpoint in capital letters with standard appsync domain
|
|
||||||
/// - When: I invoke AppSyncURLHelper.hasStandardAppsyncGraphQLDomain()
|
|
||||||
/// - Then: It should return true
|
|
||||||
func testURLStandardAppSyncDomainCaseInsensitive() {
|
|
||||||
let standardDomainURL =
|
|
||||||
URL(string: "HTTPS://ABCDEFGHIJKLMNOPQRSTUVWXYZ.APPSYNC-API.US-WEST-2.AMAZONAWS.COM/GRAPHQL")!
|
|
||||||
XCTAssertTrue(AppSyncURLHelper.isStandardAppSyncGraphQLEndpoint(url: standardDomainURL))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Test if given graphql endpoint has a custom domain
|
|
||||||
///
|
|
||||||
/// - Given: A graphql endpoint with a custom domain
|
|
||||||
/// - When: I invoke AppSyncURLHelper.hasStandardAppsyncGraphQLDomain()
|
|
||||||
/// - Then: It should return false
|
|
||||||
func testURLCustomAppSyncDomain() {
|
|
||||||
let customDomainURL =
|
|
||||||
URL(string: "https://api.example.com/graphql")!
|
|
||||||
XCTAssertFalse(AppSyncURLHelper.isStandardAppSyncGraphQLEndpoint(url: customDomainURL))
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2020 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
@ -10,29 +10,13 @@ import AppSyncRealTimeClient
|
||||||
|
|
||||||
class OIDCAuthInterceptorTests: XCTestCase {
|
class OIDCAuthInterceptorTests: XCTestCase {
|
||||||
|
|
||||||
var userPoolAuthProvider: MockUserPoolsAuthProvider!
|
|
||||||
var authInterceptor: OIDCAuthInterceptor!
|
var authInterceptor: OIDCAuthInterceptor!
|
||||||
|
|
||||||
override func setUp() {
|
override func setUp() {
|
||||||
userPoolAuthProvider = MockUserPoolsAuthProvider()
|
authInterceptor = OIDCAuthInterceptor(MockUserPoolsAuthProvider())
|
||||||
authInterceptor = OIDCAuthInterceptor(userPoolAuthProvider)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func testInterceptConnection() {
|
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 testInterceptConnectionWithInvalidToken() {
|
|
||||||
userPoolAuthProvider.hasError = true
|
|
||||||
let url = URL(string: "http://xxxc.appsync-api.ap-southeast-2.amazonaws.com/sd")!
|
let url = URL(string: "http://xxxc.appsync-api.ap-southeast-2.amazonaws.com/sd")!
|
||||||
let request = AppSyncConnectionRequest(url: url)
|
let request = AppSyncConnectionRequest(url: url)
|
||||||
let signedRequest = authInterceptor.interceptConnection(request, for: url)
|
let signedRequest = authInterceptor.interceptConnection(request, for: url)
|
||||||
|
@ -55,15 +39,7 @@ class OIDCAuthInterceptorTests: XCTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
class MockUserPoolsAuthProvider: OIDCAuthProvider {
|
class MockUserPoolsAuthProvider: OIDCAuthProvider {
|
||||||
struct AuthError: Error { }
|
|
||||||
|
|
||||||
var hasError: Bool = false
|
|
||||||
|
|
||||||
func getLatestAuthToken() -> Result<String, Error> {
|
func getLatestAuthToken() -> Result<String, Error> {
|
||||||
if hasError {
|
|
||||||
return .failure(AuthError())
|
|
||||||
}
|
|
||||||
|
|
||||||
return .success("jwtToken")
|
return .success("jwtToken")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2020 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
//
|
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
|
||||||
// All Rights Reserved.
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
@testable import AppSyncRealTimeClient
|
|
||||||
|
|
||||||
class MockConnectivityMonitor: AnyConnectivityMonitor {
|
|
||||||
|
|
||||||
private var connectivityUpdatesQueue: DispatchQueue?
|
|
||||||
private var onConnectivityUpdates: ConnectivityUpdates?
|
|
||||||
|
|
||||||
func start(
|
|
||||||
connectivityUpdatesQueue: DispatchQueue,
|
|
||||||
onConnectivityUpdates: @escaping ConnectivityUpdates
|
|
||||||
) {
|
|
||||||
self.connectivityUpdatesQueue = connectivityUpdatesQueue
|
|
||||||
self.onConnectivityUpdates = onConnectivityUpdates
|
|
||||||
}
|
|
||||||
|
|
||||||
func sendConnectivityUpdate(_ connectivityPath: ConnectivityPath) {
|
|
||||||
guard let onConnectivityUpdates = onConnectivityUpdates,
|
|
||||||
let connectivityUpdatesQueue = connectivityUpdatesQueue else {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
connectivityUpdatesQueue.async {
|
|
||||||
onConnectivityUpdates(connectivityPath)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func cancel() {
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2020 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
@ -9,20 +9,20 @@ import Foundation
|
||||||
import AppSyncRealTimeClient
|
import AppSyncRealTimeClient
|
||||||
|
|
||||||
class MockWebsocketProvider: AppSyncWebsocketProvider {
|
class MockWebsocketProvider: AppSyncWebsocketProvider {
|
||||||
typealias OnConnect = (URLRequest, [String], AppSyncWebsocketDelegate?) -> Void
|
typealias OnConnect = (URL, [String], AppSyncWebsocketDelegate?) -> Void
|
||||||
typealias OnDisconnect = () -> Void
|
typealias OnDisconnect = () -> Void
|
||||||
typealias OnWrite = (String) -> Void
|
typealias OnWrite = (String) -> Void
|
||||||
|
|
||||||
var isConnected: Bool
|
var isConnected: Bool
|
||||||
|
|
||||||
let onConnect: OnConnect?
|
let onConnect: OnConnect
|
||||||
let onDisconnect: OnDisconnect?
|
let onDisconnect: OnDisconnect
|
||||||
let onWrite: OnWrite?
|
let onWrite: OnWrite
|
||||||
|
|
||||||
init(
|
init(
|
||||||
onConnect: OnConnect? = nil,
|
onConnect: @escaping OnConnect,
|
||||||
onDisconnect: OnDisconnect? = nil,
|
onDisconnect: @escaping OnDisconnect,
|
||||||
onWrite: OnWrite? = nil
|
onWrite: @escaping OnWrite
|
||||||
) {
|
) {
|
||||||
self.isConnected = false
|
self.isConnected = false
|
||||||
self.onConnect = onConnect
|
self.onConnect = onConnect
|
||||||
|
@ -30,16 +30,16 @@ class MockWebsocketProvider: AppSyncWebsocketProvider {
|
||||||
self.onWrite = onWrite
|
self.onWrite = onWrite
|
||||||
}
|
}
|
||||||
|
|
||||||
func connect(urlRequest: URLRequest, protocols: [String], delegate: AppSyncWebsocketDelegate?) {
|
func connect(url: URL, protocols: [String], delegate: AppSyncWebsocketDelegate?) {
|
||||||
onConnect?(urlRequest, protocols, delegate)
|
onConnect(url, protocols, delegate)
|
||||||
}
|
}
|
||||||
|
|
||||||
func disconnect() {
|
func disconnect() {
|
||||||
onDisconnect?()
|
onDisconnect()
|
||||||
}
|
}
|
||||||
|
|
||||||
func write(message: String) {
|
func write(message: String) {
|
||||||
onWrite?(message)
|
onWrite(message)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2020 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
@ -19,8 +19,7 @@ class CountdownTimerTests: XCTestCase {
|
||||||
|
|
||||||
func testTimerFires() {
|
func testTimerFires() {
|
||||||
let timerFired = expectation(description: "timerFired")
|
let timerFired = expectation(description: "timerFired")
|
||||||
let timer = CountdownTimer()
|
let timer = CountdownTimer(interval: 0.1) { timerFired.fulfill() }
|
||||||
timer.start(interval: 0.1) { timerFired.fulfill() }
|
|
||||||
waitForExpectations(timeout: 1.0)
|
waitForExpectations(timeout: 1.0)
|
||||||
timer.invalidate()
|
timer.invalidate()
|
||||||
}
|
}
|
||||||
|
@ -28,8 +27,7 @@ class CountdownTimerTests: XCTestCase {
|
||||||
func testTimerDoesNotFireEarly() {
|
func testTimerDoesNotFireEarly() {
|
||||||
let timerFired = expectation(description: "timerFired")
|
let timerFired = expectation(description: "timerFired")
|
||||||
timerFired.isInverted = true
|
timerFired.isInverted = true
|
||||||
let timer = CountdownTimer()
|
let timer = CountdownTimer(interval: 0.5) { timerFired.fulfill() }
|
||||||
timer.start(interval: 0.5) { timerFired.fulfill() }
|
|
||||||
waitForExpectations(timeout: 0.1)
|
waitForExpectations(timeout: 0.1)
|
||||||
timer.invalidate()
|
timer.invalidate()
|
||||||
}
|
}
|
||||||
|
@ -38,8 +36,7 @@ class CountdownTimerTests: XCTestCase {
|
||||||
let timerFired = expectation(description: "timerFired")
|
let timerFired = expectation(description: "timerFired")
|
||||||
timerFired.isInverted = true
|
timerFired.isInverted = true
|
||||||
XCTAssert(Thread.isMainThread)
|
XCTAssert(Thread.isMainThread)
|
||||||
let timer = CountdownTimer()
|
let timer = CountdownTimer(interval: 1.0) {
|
||||||
timer.start(interval: 1.0) {
|
|
||||||
timerFired.fulfill()
|
timerFired.fulfill()
|
||||||
XCTAssertFalse(Thread.isMainThread)
|
XCTAssertFalse(Thread.isMainThread)
|
||||||
}
|
}
|
||||||
|
@ -50,10 +47,7 @@ class CountdownTimerTests: XCTestCase {
|
||||||
func testTimerDoesNotFireAfterInvalidate() {
|
func testTimerDoesNotFireAfterInvalidate() {
|
||||||
let timerFired = expectation(description: "timerFired")
|
let timerFired = expectation(description: "timerFired")
|
||||||
timerFired.isInverted = true
|
timerFired.isInverted = true
|
||||||
let timer = CountdownTimer()
|
let timer = CountdownTimer(interval: 0.1) { timerFired.fulfill() }
|
||||||
timer.start(interval: 0.1) {
|
|
||||||
timerFired.fulfill()
|
|
||||||
}
|
|
||||||
timer.invalidate()
|
timer.invalidate()
|
||||||
waitForExpectations(timeout: 0.2)
|
waitForExpectations(timeout: 0.2)
|
||||||
}
|
}
|
||||||
|
@ -72,13 +66,13 @@ class CountdownTimerTests: XCTestCase {
|
||||||
/// - 100: Ensure timer has not yet fired
|
/// - 100: Ensure timer has not yet fired
|
||||||
/// - 300: Ensure timer has fired
|
/// - 300: Ensure timer has fired
|
||||||
func testTimerFiresOnSchedule() {
|
func testTimerFiresOnSchedule() {
|
||||||
let timer = CountdownTimer()
|
var timer: CountdownTimer!
|
||||||
let timerHasFired = AtomicValue(initialValue: false)
|
let timerHasFired = AtomicValue(initialValue: false)
|
||||||
|
|
||||||
let timerShouldHaveFired = expectation(description: "the timer should have fired by now")
|
let timerShouldHaveFired = expectation(description: "the timer should have fired by now")
|
||||||
|
|
||||||
DispatchQueue.global().asyncAfter(deadline: .now() + .milliseconds(0)) {
|
DispatchQueue.global().asyncAfter(deadline: .now() + .milliseconds(0)) {
|
||||||
timer.start(interval: 0.200) {
|
timer = CountdownTimer(interval: 0.200) {
|
||||||
timerHasFired.set(true)
|
timerHasFired.set(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,20 +103,17 @@ class CountdownTimerTests: XCTestCase {
|
||||||
/// Test timing in ms:
|
/// Test timing in ms:
|
||||||
/// - 000: Set up a timer with a .3 sec interval
|
/// - 000: Set up a timer with a .3 sec interval
|
||||||
/// - 100: Ensure timer has not yet fired
|
/// - 100: Ensure timer has not yet fired
|
||||||
/// - 200: Issue a `reset` before timer would fire
|
/// - 200: Issue a `resetCountdown`
|
||||||
/// - 400: Ensure timer has not yet fired
|
/// - 400: Ensure timer has not yet fired
|
||||||
/// - 600: Timer should fire around this time
|
/// - 600: Ensure timer has yet fired
|
||||||
/// - 700: Ensure timer has fired
|
|
||||||
func testTimerResets() {
|
func testTimerResets() {
|
||||||
let timer = CountdownTimer()
|
var timer: CountdownTimer!
|
||||||
let timerHasFired = AtomicValue(initialValue: false)
|
let timerHasFired = AtomicValue(initialValue: false)
|
||||||
|
|
||||||
let timerShouldHaveFired = expectation(description: "the timer should have fired by now")
|
let timerShouldHaveFired = expectation(description: "the timer should have fired by now")
|
||||||
|
|
||||||
DispatchQueue.global().asyncAfter(deadline: .now() + .milliseconds(0)) {
|
DispatchQueue.global().asyncAfter(deadline: .now() + .milliseconds(0)) {
|
||||||
timer.start(interval: 0.300) {
|
timer = CountdownTimer(interval: 0.300) { timerHasFired.set(true) }
|
||||||
timerHasFired.set(true)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DispatchQueue.global().asyncAfter(deadline: .now() + .milliseconds(100)) {
|
DispatchQueue.global().asyncAfter(deadline: .now() + .milliseconds(100)) {
|
||||||
|
@ -130,14 +121,14 @@ class CountdownTimerTests: XCTestCase {
|
||||||
}
|
}
|
||||||
|
|
||||||
DispatchQueue.global().asyncAfter(deadline: .now() + .milliseconds(200)) {
|
DispatchQueue.global().asyncAfter(deadline: .now() + .milliseconds(200)) {
|
||||||
timer.reset()
|
timer.resetCountdown()
|
||||||
}
|
}
|
||||||
|
|
||||||
DispatchQueue.global().asyncAfter(deadline: .now() + .milliseconds(400)) {
|
DispatchQueue.global().asyncAfter(deadline: .now() + .milliseconds(400)) {
|
||||||
XCTAssertFalse(timerHasFired.get(), "The timer should not have fired yet")
|
XCTAssertFalse(timerHasFired.get(), "The timer should not have fired yet")
|
||||||
}
|
}
|
||||||
|
|
||||||
DispatchQueue.global().asyncAfter(deadline: .now() + .milliseconds(700)) {
|
DispatchQueue.global().asyncAfter(deadline: .now() + .milliseconds(600)) {
|
||||||
XCTAssert(timerHasFired.get(), "The timer should have fired by now")
|
XCTAssert(timerHasFired.get(), "The timer should have fired by now")
|
||||||
timerShouldHaveFired.fulfill()
|
timerShouldHaveFired.fulfill()
|
||||||
}
|
}
|
||||||
|
@ -149,21 +140,12 @@ class CountdownTimerTests: XCTestCase {
|
||||||
|
|
||||||
/// Test that concurrent operations on the timer do not result in data races
|
/// Test that concurrent operations on the timer do not result in data races
|
||||||
func testConcurrency() {
|
func testConcurrency() {
|
||||||
let concurrentCount = expectation(description: "timer fired at least once")
|
let timerShouldHaveFired = expectation(description: "the timer should have fired by now")
|
||||||
concurrentCount.expectedFulfillmentCount = 10_000
|
let timer = CountdownTimer(interval: 0.1) { timerShouldHaveFired.fulfill() }
|
||||||
let timer = CountdownTimer()
|
|
||||||
|
|
||||||
DispatchQueue.concurrentPerform(iterations: 10_000) { _ in
|
DispatchQueue.concurrentPerform(iterations: 10_000) { _ in
|
||||||
let randomInt = Int.random(in: 1 ... 3)
|
timer.resetCountdown()
|
||||||
if randomInt == 1 {
|
|
||||||
timer.start(interval: 0.01) { }
|
|
||||||
} else if randomInt == 2 {
|
|
||||||
timer.invalidate()
|
|
||||||
} else {
|
|
||||||
timer.reset()
|
|
||||||
}
|
}
|
||||||
concurrentCount.fulfill()
|
waitForExpectations(timeout: 1.0)
|
||||||
}
|
|
||||||
waitForExpectations(timeout: 10)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2020 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
@ -16,27 +16,14 @@ class RealtimeGatewayURLInterceptorTests: XCTestCase {
|
||||||
realtimeGatewayURLInterceptor = RealtimeGatewayURLInterceptor()
|
realtimeGatewayURLInterceptor = RealtimeGatewayURLInterceptor()
|
||||||
}
|
}
|
||||||
|
|
||||||
func testStandardDomainInterceptRequest() {
|
func testInterceptRequest() {
|
||||||
let url = URL(string: "https://abcdefghijklmnopqrstuvwxyz.appsync-api.us-west-2.amazonaws.com/graphql")!
|
let url = URL(string: "http://xxxc.appsync-api.ap-southeast-2.amazonaws.com/sd")!
|
||||||
let request = AppSyncConnectionRequest(url: url)
|
let request = AppSyncConnectionRequest(url: url)
|
||||||
let changedRequest = realtimeGatewayURLInterceptor.interceptConnection(request, for: url)
|
let changedRequest = realtimeGatewayURLInterceptor.interceptConnection(request, for: url)
|
||||||
XCTAssertEqual(changedRequest.url.scheme, "wss", "Scheme should be wss")
|
XCTAssertEqual(changedRequest.url.scheme, "wss", "Scheme should be wss")
|
||||||
XCTAssertEqual(
|
XCTAssertTrue(
|
||||||
changedRequest.url.absoluteString,
|
changedRequest.url.absoluteString.contains("appsync-realtime-api"),
|
||||||
"wss://abcdefghijklmnopqrstuvwxyz.appsync-realtime-api.us-west-2.amazonaws.com/graphql",
|
"URL should contain the realtime part"
|
||||||
"URL string should be wss://abcdefghijklmnopqrstuvwxyz.appsync-realtime-api.us-west-2.amazonaws.com/graphql"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func testCustomDomainInterceptRequest() {
|
|
||||||
let url = URL(string: "https://api.example.com/graphql")!
|
|
||||||
let request = AppSyncConnectionRequest(url: url)
|
|
||||||
let changedRequest = realtimeGatewayURLInterceptor.interceptConnection(request, for: url)
|
|
||||||
XCTAssertEqual(changedRequest.url.scheme, "wss", "Scheme should be wss")
|
|
||||||
XCTAssertEqual(
|
|
||||||
changedRequest.url.absoluteString,
|
|
||||||
"wss://api.example.com/graphql/realtime",
|
|
||||||
"URL string should be wss://api.example.com/graphql/realtime"
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2020 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
|
86
CHANGELOG.md
86
CHANGELOG.md
|
@ -3,92 +3,6 @@
|
||||||
## Unreleased
|
## Unreleased
|
||||||
*Changes on `main` branch that have not yet been released*
|
*Changes on `main` branch that have not yet been released*
|
||||||
|
|
||||||
## 3.1.0
|
|
||||||
|
|
||||||
### Features
|
|
||||||
- feat: Add async implementation and OIDCAuthProvider protocol ([#119](https://github.com/aws-amplify/aws-appsync-realtime-client-ios/pull/119))
|
|
||||||
- feat: Add asynchronous version of interceptor protocol ([#118](https://github.com/aws-amplify/aws-appsync-realtime-client-ios/pull/118))
|
|
||||||
|
|
||||||
### Misc
|
|
||||||
- chore: Update the pods ([#120](https://github.com/aws-amplify/aws-appsync-realtime-client-ios/pull/120))
|
|
||||||
- chore: fix build warnings ([#116](https://github.com/aws-amplify/aws-appsync-realtime-client-ios/pull/116))
|
|
||||||
|
|
||||||
## 3.0.0
|
|
||||||
|
|
||||||
**Breaking changes**: This is a major version release due to the changes made in [PR #110](https://github.com/aws-amplify/aws-appsync-realtime-client-ios/pull/110). The public interface for `ConnectionProviderFactory`, `RealtimeConnectionProviderAsync`, and `RealtimeConnectionProvider` has been modified to take in a `URLRequest` parameter instead of a `URL`.
|
|
||||||
|
|
||||||
### Features
|
|
||||||
- feat: pass URLRequest instead of URL to interfaces (See [PR #110](https://github.com/aws-amplify/aws-appsync-realtime-client-ios/pull/110))
|
|
||||||
|
|
||||||
### Fixes
|
|
||||||
- fix: fixed a bug that prevented TaskQueue sync method from waiting for the task to complete. (See [PR #107](https://github.com/aws-amplify/aws-appsync-realtime-client-ios/pull/107))
|
|
||||||
- chore: Add no store for the cache in urlsession (See [PR #109](https://github.com/aws-amplify/aws-appsync-realtime-client-ios/pull/109))
|
|
||||||
|
|
||||||
## 2.1.1
|
|
||||||
|
|
||||||
### Fixes
|
|
||||||
|
|
||||||
- fix: add runtime and compiler gates to OIDCAuthInterceptorAsync OIDCAuthProviderAsync and add files to workspace (See [PR #104](https://github.com/aws-amplify/aws-appsync-realtime-client-ios/pull/104))
|
|
||||||
|
|
||||||
## 2.1.0
|
|
||||||
|
|
||||||
### Features
|
|
||||||
- feat: added async oidc interceptor (See [PR #100](https://github.com/aws-amplify/aws-appsync-realtime-client-ios/pull/100))
|
|
||||||
|
|
||||||
### Fixes
|
|
||||||
- fix: sets OS versions for os_log (See [PR #99](https://github.com/aws-amplify/aws-appsync-realtime-client-ios/pull/99))
|
|
||||||
- fix: supports cross platform builds (See [PR #98](https://github.com/aws-amplify/aws-appsync-realtime-client-ios/pull/98))
|
|
||||||
|
|
||||||
## 2.0.0
|
|
||||||
|
|
||||||
- feat: Handle Unauthorized errors (See [PR #69](https://github.com/aws-amplify/aws-appsync-realtime-client-ios/pull/69))
|
|
||||||
- fix: rebase RTConnectionProvider+Websocket to Async version (See [PR #91](https://github.com/aws-amplify/aws-appsync-realtime-client-ios/pull/91))
|
|
||||||
- fix: create valid unauthorized request for odic/userpool connections (See [PR #93](https://github.com/aws-amplify/aws-appsync-realtime-client-ios/pull/93))
|
|
||||||
|
|
||||||
Breaking changes: `ConnectionProviderError.other` has been removed and `.unauthorized` and `.unknown` cases has been added.
|
|
||||||
|
|
||||||
## 1.10.0
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
- feat: Add Swift concurrency (async/await) support for async interceptors
|
|
||||||
|
|
||||||
## 1.9.1
|
|
||||||
|
|
||||||
### Bug fixes
|
|
||||||
|
|
||||||
- fix: Throttle AppSync LimitExceeded errors (See [PR #67](https://github.com/aws-amplify/aws-appsync-realtime-client-ios/pull/67))
|
|
||||||
|
|
||||||
## 1.9.0
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
- feat: Attempt to reconnect on connectivity (See [PR #58](https://github.com/aws-amplify/aws-appsync-realtime-client-ios/pull/58))
|
|
||||||
|
|
||||||
## 1.8.1
|
|
||||||
|
|
||||||
### Bug fixes
|
|
||||||
|
|
||||||
- fix: Subscription failed event should be terminal event (See [PR #74](https://github.com/aws-amplify/aws-appsync-realtime-client-ios/pull/74))
|
|
||||||
|
|
||||||
## 1.8.0
|
|
||||||
|
|
||||||
- feat: Allow setting log level (See [PR #71](https://github.com/aws-amplify/aws-appsync-realtime-client-ios/pull/71))
|
|
||||||
|
|
||||||
## 1.7.1
|
|
||||||
|
|
||||||
### Bug fixes
|
|
||||||
|
|
||||||
- fix: Retry on MaxSubscriptionReached (See [PR #66](https://github.com/aws-amplify/aws-appsync-realtime-client-ios/pull/66))
|
|
||||||
- fix: data race in CountdownTimer (See [PR #65](https://github.com/aws-amplify/aws-appsync-realtime-client-ios/pull/65))
|
|
||||||
|
|
||||||
## 1.7.0
|
|
||||||
|
|
||||||
- feat: Upgrade Starscream to 4.0.4 (See [PR #62](https://github.com/aws-amplify/aws-appsync-realtime-client-ios/pull/62))
|
|
||||||
|
|
||||||
## 1.6.0
|
|
||||||
|
|
||||||
- feat: Realtime interceptor changes for GraphQL subscriptions (See [PR #53](https://github.com/aws-amplify/aws-appsync-realtime-client-ios/pull/53))
|
|
||||||
|
|
||||||
## 1.5.0
|
## 1.5.0
|
||||||
|
|
||||||
|
|
2
Cartfile
2
Cartfile
|
@ -1 +1 @@
|
||||||
github "daltoniam/starscream" ~> 4.0.4
|
github "daltoniam/starscream" ~> 3.1.1
|
||||||
|
|
|
@ -9,7 +9,7 @@ if [[ -z $test_device_id ]] ; then
|
||||||
echo "Runtime: '${runtime}'"
|
echo "Runtime: '${runtime}'"
|
||||||
|
|
||||||
# Get the last alphabetical device (probably something in the iPhone X family, as of 2019-05-31)
|
# Get the last alphabetical device (probably something in the iPhone X family, as of 2019-05-31)
|
||||||
devicetype=$( xcrun simctl list devicetypes "iPhone " | sort | tail -1 | sed 's/.*(//' | sed 's/).*//' | tr -d '[:space:]' )
|
devicetype=$( xcrun simctl list devicetypes iPhone | sort | tail -1 | sed 's/.*(//' | sed 's/).*//' | tr -d '[:space:]' )
|
||||||
echo "Device type: '${devicetype}'"
|
echo "Device type: '${devicetype}'"
|
||||||
|
|
||||||
test_device_id=$( xcrun simctl create "circleci-test-device" "${devicetype}" "${runtime}" | tr -d '[:space:]' )
|
test_device_id=$( xcrun simctl create "circleci-test-device" "${devicetype}" "${runtime}" | tr -d '[:space:]' )
|
||||||
|
|
7
Gemfile
7
Gemfile
|
@ -1,7 +1,6 @@
|
||||||
# Gemfile
|
# Gemfile
|
||||||
|
|
||||||
source 'https://rubygems.org'
|
source 'https://rubygems.org'
|
||||||
gem 'xcpretty', '0.3.0'
|
gem 'xcpretty'
|
||||||
gem 'cocoapods', '1.11.3'
|
gem 'cocoapods'
|
||||||
gem 'cocoapods-downloader', '1.6.3'
|
gem 'fastlane'
|
||||||
gem 'fastlane', '2.205.1'
|
|
302
Gemfile.lock
302
Gemfile.lock
|
@ -1,286 +1,218 @@
|
||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
CFPropertyList (3.0.5)
|
CFPropertyList (3.0.1)
|
||||||
rexml
|
activesupport (4.2.11.1)
|
||||||
activesupport (6.1.7.3)
|
i18n (~> 0.7)
|
||||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
minitest (~> 5.1)
|
||||||
i18n (>= 1.6, < 2)
|
thread_safe (~> 0.3, >= 0.3.4)
|
||||||
minitest (>= 5.1)
|
tzinfo (~> 1.1)
|
||||||
tzinfo (~> 2.0)
|
|
||||||
zeitwerk (~> 2.3)
|
|
||||||
addressable (2.8.0)
|
addressable (2.8.0)
|
||||||
public_suffix (>= 2.0.2, < 5.0)
|
public_suffix (>= 2.0.2, < 5.0)
|
||||||
algoliasearch (1.27.5)
|
algoliasearch (1.27.1)
|
||||||
httpclient (~> 2.8, >= 2.8.3)
|
httpclient (~> 2.8, >= 2.8.3)
|
||||||
json (>= 1.5.1)
|
json (>= 1.5.1)
|
||||||
artifactory (3.0.15)
|
|
||||||
atomos (0.1.3)
|
atomos (0.1.3)
|
||||||
aws-eventstream (1.2.0)
|
babosa (1.0.3)
|
||||||
aws-partitions (1.573.0)
|
claide (1.0.3)
|
||||||
aws-sdk-core (3.130.0)
|
cocoapods (1.8.4)
|
||||||
aws-eventstream (~> 1, >= 1.0.2)
|
activesupport (>= 4.0.2, < 5)
|
||||||
aws-partitions (~> 1, >= 1.525.0)
|
|
||||||
aws-sigv4 (~> 1.1)
|
|
||||||
jmespath (~> 1.0)
|
|
||||||
aws-sdk-kms (1.55.0)
|
|
||||||
aws-sdk-core (~> 3, >= 3.127.0)
|
|
||||||
aws-sigv4 (~> 1.1)
|
|
||||||
aws-sdk-s3 (1.113.0)
|
|
||||||
aws-sdk-core (~> 3, >= 3.127.0)
|
|
||||||
aws-sdk-kms (~> 1)
|
|
||||||
aws-sigv4 (~> 1.4)
|
|
||||||
aws-sigv4 (1.4.0)
|
|
||||||
aws-eventstream (~> 1, >= 1.0.2)
|
|
||||||
babosa (1.0.4)
|
|
||||||
claide (1.1.0)
|
|
||||||
cocoapods (1.11.3)
|
|
||||||
addressable (~> 2.8)
|
|
||||||
claide (>= 1.0.2, < 2.0)
|
claide (>= 1.0.2, < 2.0)
|
||||||
cocoapods-core (= 1.11.3)
|
cocoapods-core (= 1.8.4)
|
||||||
cocoapods-deintegrate (>= 1.0.3, < 2.0)
|
cocoapods-deintegrate (>= 1.0.3, < 2.0)
|
||||||
cocoapods-downloader (>= 1.4.0, < 2.0)
|
cocoapods-downloader (>= 1.2.2, < 2.0)
|
||||||
cocoapods-plugins (>= 1.0.0, < 2.0)
|
cocoapods-plugins (>= 1.0.0, < 2.0)
|
||||||
cocoapods-search (>= 1.0.0, < 2.0)
|
cocoapods-search (>= 1.0.0, < 2.0)
|
||||||
|
cocoapods-stats (>= 1.0.0, < 2.0)
|
||||||
cocoapods-trunk (>= 1.4.0, < 2.0)
|
cocoapods-trunk (>= 1.4.0, < 2.0)
|
||||||
cocoapods-try (>= 1.1.0, < 2.0)
|
cocoapods-try (>= 1.1.0, < 2.0)
|
||||||
colored2 (~> 3.1)
|
colored2 (~> 3.1)
|
||||||
escape (~> 0.0.4)
|
escape (~> 0.0.4)
|
||||||
fourflusher (>= 2.3.0, < 3.0)
|
fourflusher (>= 2.3.0, < 3.0)
|
||||||
gh_inspector (~> 1.0)
|
gh_inspector (~> 1.0)
|
||||||
molinillo (~> 0.8.0)
|
molinillo (~> 0.6.6)
|
||||||
nap (~> 1.0)
|
nap (~> 1.0)
|
||||||
ruby-macho (>= 1.0, < 3.0)
|
ruby-macho (~> 1.4)
|
||||||
xcodeproj (>= 1.21.0, < 2.0)
|
xcodeproj (>= 1.11.1, < 2.0)
|
||||||
cocoapods-core (1.11.3)
|
cocoapods-core (1.8.4)
|
||||||
activesupport (>= 5.0, < 7)
|
activesupport (>= 4.0.2, < 6)
|
||||||
addressable (~> 2.8)
|
|
||||||
algoliasearch (~> 1.0)
|
algoliasearch (~> 1.0)
|
||||||
concurrent-ruby (~> 1.1)
|
concurrent-ruby (~> 1.1)
|
||||||
fuzzy_match (~> 2.0.4)
|
fuzzy_match (~> 2.0.4)
|
||||||
nap (~> 1.0)
|
nap (~> 1.0)
|
||||||
netrc (~> 0.11)
|
cocoapods-deintegrate (1.0.4)
|
||||||
public_suffix (~> 4.0)
|
cocoapods-downloader (1.3.0)
|
||||||
typhoeus (~> 1.0)
|
|
||||||
cocoapods-deintegrate (1.0.5)
|
|
||||||
cocoapods-downloader (1.6.3)
|
|
||||||
cocoapods-plugins (1.0.0)
|
cocoapods-plugins (1.0.0)
|
||||||
nap
|
nap
|
||||||
cocoapods-search (1.0.1)
|
cocoapods-search (1.0.0)
|
||||||
cocoapods-trunk (1.6.0)
|
cocoapods-stats (1.1.0)
|
||||||
|
cocoapods-trunk (1.4.1)
|
||||||
nap (>= 0.8, < 2.0)
|
nap (>= 0.8, < 2.0)
|
||||||
netrc (~> 0.11)
|
netrc (~> 0.11)
|
||||||
cocoapods-try (1.2.0)
|
cocoapods-try (1.1.0)
|
||||||
colored (1.2)
|
colored (1.2)
|
||||||
colored2 (3.1.2)
|
colored2 (3.1.2)
|
||||||
commander (4.6.0)
|
commander-fastlane (4.4.6)
|
||||||
highline (~> 2.0.0)
|
highline (~> 1.7.2)
|
||||||
concurrent-ruby (1.2.2)
|
concurrent-ruby (1.1.5)
|
||||||
declarative (0.0.20)
|
declarative (0.0.10)
|
||||||
digest-crc (0.6.4)
|
declarative-option (0.1.0)
|
||||||
rake (>= 12.0.0, < 14.0.0)
|
digest-crc (0.4.1)
|
||||||
domain_name (0.5.20190701)
|
domain_name (0.5.20190701)
|
||||||
unf (>= 0.0.5, < 1.0.0)
|
unf (>= 0.0.5, < 1.0.0)
|
||||||
dotenv (2.7.6)
|
dotenv (2.7.5)
|
||||||
emoji_regex (3.2.3)
|
emoji_regex (1.0.1)
|
||||||
escape (0.0.4)
|
escape (0.0.4)
|
||||||
ethon (0.15.0)
|
excon (0.71.0)
|
||||||
ffi (>= 1.15.0)
|
faraday (0.17.1)
|
||||||
excon (0.92.2)
|
|
||||||
faraday (1.10.0)
|
|
||||||
faraday-em_http (~> 1.0)
|
|
||||||
faraday-em_synchrony (~> 1.0)
|
|
||||||
faraday-excon (~> 1.1)
|
|
||||||
faraday-httpclient (~> 1.0)
|
|
||||||
faraday-multipart (~> 1.0)
|
|
||||||
faraday-net_http (~> 1.0)
|
|
||||||
faraday-net_http_persistent (~> 1.0)
|
|
||||||
faraday-patron (~> 1.0)
|
|
||||||
faraday-rack (~> 1.0)
|
|
||||||
faraday-retry (~> 1.0)
|
|
||||||
ruby2_keywords (>= 0.0.4)
|
|
||||||
faraday-cookie_jar (0.0.7)
|
|
||||||
faraday (>= 0.8.0)
|
|
||||||
http-cookie (~> 1.0.0)
|
|
||||||
faraday-em_http (1.0.0)
|
|
||||||
faraday-em_synchrony (1.0.0)
|
|
||||||
faraday-excon (1.1.0)
|
|
||||||
faraday-httpclient (1.0.1)
|
|
||||||
faraday-multipart (1.0.3)
|
|
||||||
multipart-post (>= 1.2, < 3)
|
multipart-post (>= 1.2, < 3)
|
||||||
faraday-net_http (1.0.1)
|
faraday-cookie_jar (0.0.6)
|
||||||
faraday-net_http_persistent (1.2.0)
|
faraday (>= 0.7.4)
|
||||||
faraday-patron (1.0.0)
|
http-cookie (~> 1.0.0)
|
||||||
faraday-rack (1.0.0)
|
faraday_middleware (0.13.1)
|
||||||
faraday-retry (1.0.3)
|
faraday (>= 0.7.4, < 1.0)
|
||||||
faraday_middleware (1.2.0)
|
fastimage (2.1.7)
|
||||||
faraday (~> 1.0)
|
fastlane (2.137.0)
|
||||||
fastimage (2.2.6)
|
|
||||||
fastlane (2.205.1)
|
|
||||||
CFPropertyList (>= 2.3, < 4.0.0)
|
CFPropertyList (>= 2.3, < 4.0.0)
|
||||||
addressable (>= 2.8, < 3.0.0)
|
addressable (>= 2.3, < 3.0.0)
|
||||||
artifactory (~> 3.0)
|
babosa (>= 1.0.2, < 2.0.0)
|
||||||
aws-sdk-s3 (~> 1.0)
|
|
||||||
babosa (>= 1.0.3, < 2.0.0)
|
|
||||||
bundler (>= 1.12.0, < 3.0.0)
|
bundler (>= 1.12.0, < 3.0.0)
|
||||||
colored
|
colored
|
||||||
commander (~> 4.6)
|
commander-fastlane (>= 4.4.6, < 5.0.0)
|
||||||
dotenv (>= 2.1.1, < 3.0.0)
|
dotenv (>= 2.1.1, < 3.0.0)
|
||||||
emoji_regex (>= 0.1, < 4.0)
|
emoji_regex (>= 0.1, < 2.0)
|
||||||
excon (>= 0.71.0, < 1.0.0)
|
excon (>= 0.45.0, < 1.0.0)
|
||||||
faraday (~> 1.0)
|
faraday (~> 0.17)
|
||||||
faraday-cookie_jar (~> 0.0.6)
|
faraday-cookie_jar (~> 0.0.6)
|
||||||
faraday_middleware (~> 1.0)
|
faraday_middleware (~> 0.13.1)
|
||||||
fastimage (>= 2.1.0, < 3.0.0)
|
fastimage (>= 2.1.0, < 3.0.0)
|
||||||
gh_inspector (>= 1.1.2, < 2.0.0)
|
gh_inspector (>= 1.1.2, < 2.0.0)
|
||||||
google-apis-androidpublisher_v3 (~> 0.3)
|
google-api-client (>= 0.21.2, < 0.24.0)
|
||||||
google-apis-playcustomapp_v1 (~> 0.1)
|
google-cloud-storage (>= 1.15.0, < 2.0.0)
|
||||||
google-cloud-storage (~> 1.31)
|
highline (>= 1.7.2, < 2.0.0)
|
||||||
highline (~> 2.0)
|
|
||||||
json (< 3.0.0)
|
json (< 3.0.0)
|
||||||
jwt (>= 2.1.0, < 3)
|
jwt (~> 2.1.0)
|
||||||
mini_magick (>= 4.9.4, < 5.0.0)
|
mini_magick (>= 4.9.4, < 5.0.0)
|
||||||
|
multi_xml (~> 0.5)
|
||||||
multipart-post (~> 2.0.0)
|
multipart-post (~> 2.0.0)
|
||||||
naturally (~> 2.2)
|
|
||||||
optparse (~> 0.1.1)
|
|
||||||
plist (>= 3.1.0, < 4.0.0)
|
plist (>= 3.1.0, < 4.0.0)
|
||||||
rubyzip (>= 2.0.0, < 3.0.0)
|
public_suffix (~> 2.0.0)
|
||||||
|
rubyzip (>= 1.3.0, < 2.0.0)
|
||||||
security (= 0.1.3)
|
security (= 0.1.3)
|
||||||
simctl (~> 1.6.3)
|
simctl (~> 1.6.3)
|
||||||
|
slack-notifier (>= 2.0.0, < 3.0.0)
|
||||||
terminal-notifier (>= 2.0.0, < 3.0.0)
|
terminal-notifier (>= 2.0.0, < 3.0.0)
|
||||||
terminal-table (>= 1.4.5, < 2.0.0)
|
terminal-table (>= 1.4.5, < 2.0.0)
|
||||||
tty-screen (>= 0.6.3, < 1.0.0)
|
tty-screen (>= 0.6.3, < 1.0.0)
|
||||||
tty-spinner (>= 0.8.0, < 1.0.0)
|
tty-spinner (>= 0.8.0, < 1.0.0)
|
||||||
word_wrap (~> 1.0.0)
|
word_wrap (~> 1.0.0)
|
||||||
xcodeproj (>= 1.13.0, < 2.0.0)
|
xcodeproj (>= 1.8.1, < 2.0.0)
|
||||||
xcpretty (~> 0.3.0)
|
xcpretty (~> 0.3.0)
|
||||||
xcpretty-travis-formatter (>= 0.0.3)
|
xcpretty-travis-formatter (>= 0.0.3)
|
||||||
ffi (1.15.5)
|
|
||||||
fourflusher (2.3.1)
|
fourflusher (2.3.1)
|
||||||
fuzzy_match (2.0.4)
|
fuzzy_match (2.0.4)
|
||||||
gh_inspector (1.1.3)
|
gh_inspector (1.1.3)
|
||||||
google-apis-androidpublisher_v3 (0.17.0)
|
google-api-client (0.23.9)
|
||||||
google-apis-core (>= 0.4, < 2.a)
|
|
||||||
google-apis-core (0.4.2)
|
|
||||||
addressable (~> 2.5, >= 2.5.1)
|
addressable (~> 2.5, >= 2.5.1)
|
||||||
googleauth (>= 0.16.2, < 2.a)
|
googleauth (>= 0.5, < 0.7.0)
|
||||||
httpclient (>= 2.8.1, < 3.a)
|
httpclient (>= 2.8.1, < 3.0)
|
||||||
mini_mime (~> 1.0)
|
mime-types (~> 3.0)
|
||||||
representable (~> 3.0)
|
representable (~> 3.0)
|
||||||
retriable (>= 2.0, < 4.a)
|
retriable (>= 2.0, < 4.0)
|
||||||
rexml
|
signet (~> 0.9)
|
||||||
webrick
|
google-cloud-core (1.4.1)
|
||||||
google-apis-iamcredentials_v1 (0.10.0)
|
|
||||||
google-apis-core (>= 0.4, < 2.a)
|
|
||||||
google-apis-playcustomapp_v1 (0.7.0)
|
|
||||||
google-apis-core (>= 0.4, < 2.a)
|
|
||||||
google-apis-storage_v1 (0.11.0)
|
|
||||||
google-apis-core (>= 0.4, < 2.a)
|
|
||||||
google-cloud-core (1.6.0)
|
|
||||||
google-cloud-env (~> 1.0)
|
google-cloud-env (~> 1.0)
|
||||||
google-cloud-errors (~> 1.0)
|
google-cloud-env (1.3.0)
|
||||||
google-cloud-env (1.6.0)
|
faraday (~> 0.11)
|
||||||
faraday (>= 0.17.3, < 3.0)
|
google-cloud-storage (1.16.0)
|
||||||
google-cloud-errors (1.2.0)
|
|
||||||
google-cloud-storage (1.36.1)
|
|
||||||
addressable (~> 2.8)
|
|
||||||
digest-crc (~> 0.4)
|
digest-crc (~> 0.4)
|
||||||
google-apis-iamcredentials_v1 (~> 0.1)
|
google-api-client (~> 0.23)
|
||||||
google-apis-storage_v1 (~> 0.1)
|
google-cloud-core (~> 1.2)
|
||||||
google-cloud-core (~> 1.6)
|
googleauth (>= 0.6.2, < 0.10.0)
|
||||||
googleauth (>= 0.16.2, < 2.a)
|
googleauth (0.6.7)
|
||||||
mini_mime (~> 1.0)
|
faraday (~> 0.12)
|
||||||
googleauth (1.1.2)
|
|
||||||
faraday (>= 0.17.3, < 3.a)
|
|
||||||
jwt (>= 1.4, < 3.0)
|
jwt (>= 1.4, < 3.0)
|
||||||
memoist (~> 0.16)
|
memoist (~> 0.16)
|
||||||
multi_json (~> 1.11)
|
multi_json (~> 1.11)
|
||||||
os (>= 0.9, < 2.0)
|
os (>= 0.9, < 2.0)
|
||||||
signet (>= 0.16, < 2.a)
|
signet (~> 0.7)
|
||||||
highline (2.0.3)
|
highline (1.7.10)
|
||||||
http-cookie (1.0.4)
|
http-cookie (1.0.3)
|
||||||
domain_name (~> 0.5)
|
domain_name (~> 0.5)
|
||||||
httpclient (2.8.3)
|
httpclient (2.8.3)
|
||||||
i18n (1.12.0)
|
i18n (0.9.5)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
jmespath (1.6.1)
|
json (2.3.1)
|
||||||
json (2.6.1)
|
jwt (2.1.0)
|
||||||
jwt (2.3.0)
|
memoist (0.16.1)
|
||||||
memoist (0.16.2)
|
mime-types (3.3)
|
||||||
mini_magick (4.11.0)
|
mime-types-data (~> 3.2015)
|
||||||
mini_mime (1.1.2)
|
mime-types-data (3.2019.1009)
|
||||||
minitest (5.18.0)
|
mini_magick (4.9.5)
|
||||||
molinillo (0.8.0)
|
minitest (5.13.0)
|
||||||
multi_json (1.15.0)
|
molinillo (0.6.6)
|
||||||
|
multi_json (1.14.1)
|
||||||
|
multi_xml (0.6.0)
|
||||||
multipart-post (2.0.0)
|
multipart-post (2.0.0)
|
||||||
nanaimo (0.3.0)
|
nanaimo (0.2.6)
|
||||||
nap (1.1.0)
|
nap (1.1.0)
|
||||||
naturally (2.2.1)
|
naturally (2.2.0)
|
||||||
netrc (0.11.0)
|
netrc (0.11.0)
|
||||||
optparse (0.1.1)
|
os (1.0.1)
|
||||||
os (1.1.4)
|
plist (3.5.0)
|
||||||
plist (3.6.0)
|
public_suffix (2.0.5)
|
||||||
public_suffix (4.0.6)
|
representable (3.0.4)
|
||||||
rake (13.0.6)
|
|
||||||
representable (3.1.1)
|
|
||||||
declarative (< 0.1.0)
|
declarative (< 0.1.0)
|
||||||
trailblazer-option (>= 0.1.1, < 0.2.0)
|
declarative-option (< 0.2.0)
|
||||||
uber (< 0.2.0)
|
uber (< 0.2.0)
|
||||||
retriable (3.1.2)
|
retriable (3.1.2)
|
||||||
rexml (3.2.5)
|
|
||||||
rouge (2.0.7)
|
rouge (2.0.7)
|
||||||
ruby-macho (2.5.1)
|
ruby-macho (1.4.0)
|
||||||
ruby2_keywords (0.0.5)
|
rubyzip (1.3.0)
|
||||||
rubyzip (2.3.2)
|
|
||||||
security (0.1.3)
|
security (0.1.3)
|
||||||
signet (0.16.1)
|
signet (0.12.0)
|
||||||
addressable (~> 2.8)
|
addressable (~> 2.3)
|
||||||
faraday (>= 0.17.5, < 3.0)
|
faraday (~> 0.9)
|
||||||
jwt (>= 1.5, < 3.0)
|
jwt (>= 1.5, < 3.0)
|
||||||
multi_json (~> 1.10)
|
multi_json (~> 1.10)
|
||||||
simctl (1.6.8)
|
simctl (1.6.6)
|
||||||
CFPropertyList
|
CFPropertyList
|
||||||
naturally
|
naturally
|
||||||
|
slack-notifier (2.3.2)
|
||||||
terminal-notifier (2.0.0)
|
terminal-notifier (2.0.0)
|
||||||
terminal-table (1.8.0)
|
terminal-table (1.8.0)
|
||||||
unicode-display_width (~> 1.1, >= 1.1.1)
|
unicode-display_width (~> 1.1, >= 1.1.1)
|
||||||
trailblazer-option (0.1.2)
|
thread_safe (0.3.6)
|
||||||
tty-cursor (0.7.1)
|
tty-cursor (0.7.0)
|
||||||
tty-screen (0.8.1)
|
tty-screen (0.7.0)
|
||||||
tty-spinner (0.9.3)
|
tty-spinner (0.9.1)
|
||||||
tty-cursor (~> 0.7)
|
tty-cursor (~> 0.7)
|
||||||
typhoeus (1.4.0)
|
tzinfo (1.2.5)
|
||||||
ethon (>= 0.9.0)
|
thread_safe (~> 0.1)
|
||||||
tzinfo (2.0.6)
|
|
||||||
concurrent-ruby (~> 1.0)
|
|
||||||
uber (0.1.0)
|
uber (0.1.0)
|
||||||
unf (0.1.4)
|
unf (0.1.4)
|
||||||
unf_ext
|
unf_ext
|
||||||
unf_ext (0.0.8.1)
|
unf_ext (0.0.7.6)
|
||||||
unicode-display_width (1.8.0)
|
unicode-display_width (1.6.0)
|
||||||
webrick (1.7.0)
|
|
||||||
word_wrap (1.0.0)
|
word_wrap (1.0.0)
|
||||||
xcodeproj (1.21.0)
|
xcodeproj (1.13.0)
|
||||||
CFPropertyList (>= 2.3.3, < 4.0)
|
CFPropertyList (>= 2.3.3, < 4.0)
|
||||||
atomos (~> 0.1.3)
|
atomos (~> 0.1.3)
|
||||||
claide (>= 1.0.2, < 2.0)
|
claide (>= 1.0.2, < 2.0)
|
||||||
colored2 (~> 3.1)
|
colored2 (~> 3.1)
|
||||||
nanaimo (~> 0.3.0)
|
nanaimo (~> 0.2.6)
|
||||||
rexml (~> 3.2.4)
|
|
||||||
xcpretty (0.3.0)
|
xcpretty (0.3.0)
|
||||||
rouge (~> 2.0.7)
|
rouge (~> 2.0.7)
|
||||||
xcpretty-travis-formatter (1.0.1)
|
xcpretty-travis-formatter (1.0.0)
|
||||||
xcpretty (~> 0.2, >= 0.0.7)
|
xcpretty (~> 0.2, >= 0.0.7)
|
||||||
zeitwerk (2.6.7)
|
|
||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
ruby
|
ruby
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
cocoapods (= 1.11.3)
|
cocoapods
|
||||||
cocoapods-downloader (= 1.6.3)
|
fastlane
|
||||||
fastlane (= 2.205.1)
|
xcpretty
|
||||||
xcpretty (= 0.3.0)
|
|
||||||
|
|
||||||
BUNDLED WITH
|
BUNDLED WITH
|
||||||
2.3.7
|
1.17.2
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2020 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2020 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//
|
//
|
||||||
// Copyright Amazon.com Inc. or its affiliates.
|
// Copyright 2018-2020 Amazon.com,
|
||||||
// All Rights Reserved.
|
// Inc. or its affiliates. All Rights Reserved.
|
||||||
//
|
//
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
//
|
//
|
||||||
|
|
|
@ -6,8 +6,17 @@
|
||||||
"repositoryURL": "https://github.com/daltoniam/Starscream",
|
"repositoryURL": "https://github.com/daltoniam/Starscream",
|
||||||
"state": {
|
"state": {
|
||||||
"branch": null,
|
"branch": null,
|
||||||
"revision": "df8d82047f6654d8e4b655d1b1525c64e1059d21",
|
"revision": "e6b65c6d9077ea48b4a7bdda8994a1d3c6969c8d",
|
||||||
"version": "4.0.4"
|
"version": "3.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"package": "swift-nio-zlib-support",
|
||||||
|
"repositoryURL": "https://github.com/apple/swift-nio-zlib-support.git",
|
||||||
|
"state": {
|
||||||
|
"branch": null,
|
||||||
|
"revision": "37760e9a52030bb9011972c5213c3350fa9d41fd",
|
||||||
|
"version": "1.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// swift-tools-version:5.5
|
// swift-tools-version:5.3
|
||||||
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
||||||
|
|
||||||
import PackageDescription
|
import PackageDescription
|
||||||
|
@ -11,7 +11,7 @@ let package = Package(
|
||||||
targets: ["AppSyncRealTimeClient"]),
|
targets: ["AppSyncRealTimeClient"]),
|
||||||
],
|
],
|
||||||
dependencies: [
|
dependencies: [
|
||||||
.package(url: "https://github.com/daltoniam/Starscream", .upToNextMinor(from: "4.0.4"))
|
.package(url: "https://github.com/daltoniam/Starscream", .upToNextMinor(from: "3.1.1"))
|
||||||
],
|
],
|
||||||
targets: [
|
targets: [
|
||||||
.target(
|
.target(
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue