├── .gitallowed
├── .github
├── CODEOWNERS
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ ├── feature_request.md
│ └── usage-question.md
├── PULL_REQUEST_TEMPLATE.md
├── stale.yml
└── workflows
│ ├── issue_closed.yml
│ ├── issue_comment.yml
│ ├── issue_opened.yml
│ └── notify_release.yml
├── .gitignore
├── .swiftlint.yml
├── .swiftpm
└── xcode
│ └── package.xcworkspace
│ └── contents.xcworkspacedata
├── .travis.yml
├── AWSAppSync.podspec
├── AWSAppSyncClient.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ ├── xcshareddata
│ │ ├── AWSDeepDishClient.xcscmblueprint
│ │ └── IDEWorkspaceChecks.plist
│ └── xcuserdata
│ │ └── rohandub.xcuserdatad
│ │ ├── IDEFindNavigatorScopes.plist
│ │ └── UserInterfaceState.xcuserstate
└── xcshareddata
│ └── xcschemes
│ ├── AWSAppSync-IntegTests.xcscheme
│ ├── AWSAppSync.xcscheme
│ └── AWSAppSyncTestApp.xcscheme
├── AWSAppSyncClient.xcworkspace
├── contents.xcworkspacedata
└── xcshareddata
│ └── IDETemplateMacros.plist
├── AWSAppSyncClient
├── AWSAppSync.h
├── AWSAppSyncAuthProvider.swift
├── AWSAppSyncAuthType.swift
├── AWSAppSyncCache.swift
├── AWSAppSyncCacheConfigurationMigration.swift
├── AWSAppSyncClient.swift
├── AWSAppSyncClientConfiguration.swift
├── AWSAppSyncClientConfigurationError.swift
├── AWSAppSyncClientConflictResolutionExtensions.swift
├── AWSAppSyncClientError.swift
├── AWSAppSyncClientInfo.swift
├── AWSAppSyncClientInfoError.swift
├── AWSAppSyncClientLogFormatter.swift
├── AWSAppSyncClientS3ObjectsExtensions.swift
├── AWSAppSyncConnection.swift
├── AWSAppSyncHTTPNetworkTransport.swift
├── AWSAppSyncMutations.swift
├── AWSAppSyncReachability.swift
├── AWSAppSyncRetryStrategy.swift
├── AWSAppSyncServiceConfig.swift
├── AWSAppSyncServiceConfigError.swift
├── AWSAppSyncSubscriptionError.swift
├── AWSAppSyncSubscriptionWatcher.swift
├── AWSAppSyncSubscriptionWatcherStatus.swift
├── AWSNetworkTransport.swift
├── AWSS3ObjectProtocol.swift
├── Apollo
│ └── Sources
│ │ └── Apollo
│ │ ├── AWSGraphQLSubscriptionResponse.swift
│ │ ├── Apollo.h
│ │ ├── ApolloClient.swift
│ │ ├── ApolloStore.swift
│ │ ├── AsynchronousOperation.swift
│ │ ├── Collections.swift
│ │ ├── DataLoader.swift
│ │ ├── GraphQLDependencyTracker.swift
│ │ ├── GraphQLError.swift
│ │ ├── GraphQLExecutor.swift
│ │ ├── GraphQLInputValue.swift
│ │ ├── GraphQLOperation.swift
│ │ ├── GraphQLQueryWatcher.swift
│ │ ├── GraphQLResponse.swift
│ │ ├── GraphQLResponseGenerator.swift
│ │ ├── GraphQLResult.swift
│ │ ├── GraphQLResultAccumulator.swift
│ │ ├── GraphQLResultNormalizer.swift
│ │ ├── GraphQLSelectionSet.swift
│ │ ├── GraphQLSelectionSetMapper.swift
│ │ ├── HTTPNetworkTransport.swift
│ │ ├── InMemoryNormalizedCache.swift
│ │ ├── Info.plist
│ │ ├── JSON.swift
│ │ ├── JSONSerializationFormat.swift
│ │ ├── JSONStandardTypeConversions.swift
│ │ ├── Locking.swift
│ │ ├── NetworkTransport.swift
│ │ ├── NormalizedCache.swift
│ │ ├── Promise.swift
│ │ ├── Record.swift
│ │ ├── RecordSet.swift
│ │ ├── Result.swift
│ │ ├── ResultOrPromise.swift
│ │ └── Utilities.swift
├── Info.plist
├── Internal
│ ├── AWSAppSyncRetryHandler.swift
│ ├── AWSAppSyncSubscriptionMetadataCache.swift
│ ├── AWSConfigurationFile.swift
│ ├── AWSMutationCache.swift
│ ├── AWSMutationRetryAdviceHelper.swift
│ ├── AWSMutationRetryNotifier.swift
│ ├── AWSMutationState.swift
│ ├── AWSOfflineMutation.swift
│ ├── AWSPerformMutationQueue.swift
│ ├── AWSRequestBuilder.swift
│ ├── AWSSQLiteNormalizedCache.swift
│ ├── AppSyncLogHelper.swift
│ ├── AppSyncLogWrapper.swift
│ ├── AppSyncSubscriptionWithSync.swift
│ ├── AuthInterceptor
│ │ ├── IAMAuthInterceptor.swift
│ │ └── LambdaAuthInterceptor.swift
│ ├── BasicAWSAPIKeyAuthProvider.swift
│ ├── CachedMutationOperation.swift
│ ├── Foundation+Utils.swift
│ ├── InternalS3ObjectDetails.swift
│ ├── NetworkReachabilityNotifier.swift
│ ├── SQLiteSerialization.swift
│ ├── SessionMutationOperation.swift
│ ├── SubscriptionFactory
│ │ ├── APIKeyBasedConnectionPool.swift
│ │ ├── AppSyncRealTimeClientOIDCAuthProvider.swift
│ │ ├── ConnectionPool
│ │ │ └── BasicSubscriptionConnectionFactory.swift
│ │ ├── IAMBasedConnectionPool.swift
│ │ ├── LambdaBasedConnectionPool.swift
│ │ ├── OIDCBasedConnectionPool.swift
│ │ ├── SubscriptionConnectionFactory.swift
│ │ └── UserPoolsBasedConnectionPool.swift
│ ├── SubscriptionMessageQueue.swift
│ ├── SyncConfiguration.swift
│ └── SyncStrategy.swift
└── NetworkReachability.swift
├── AWSAppSyncIntegrationTests
├── AWSAppSyncAPIKeyAuthTests.swift
├── AWSAppSyncCognitoAuthTests.swift
├── AWSAppSyncLambdaAuthTests.swift
├── AWSAppSyncMultiAuthClientsTests.swift
├── AuthProviderTests.swift
├── ConsoleResources
│ ├── appsync-console-operations.graphql
│ ├── appsync-console-query-variables.json
│ ├── appsync-integrationtests-cloudformation.yaml
│ └── appsync-lambda-authorizer.js
├── Helpers
│ └── SubscriptionStressTestHelper.swift
├── Info.plist
├── MutationQueuePerformanceTests.swift
├── SubscriptionMessagesQueueStressTests.swift
├── SubscriptionTests.swift
├── appsync_test_credentials.json.enc
└── testS3Object.jpg
├── AWSAppSyncTestApp
├── AppDelegate.swift
├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ ├── Contents.json
│ └── Image.imageset
│ │ └── Contents.json
├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
├── Info.plist
├── ViewController.swift
└── awsconfiguration.json
├── AWSAppSyncTestAppUITests
├── AWSAppSyncTestAppUITests.swift
└── Info.plist
├── AWSAppSyncTestCommon
├── AWSAppSyncTestCommon.h
├── AppSyncClientTestConfiguration.swift
├── AppSyncClientTestConfigurationDefaults.swift
├── AppSyncClientTestHelper.swift
├── AppSyncTestAPI+S3Object.swift
├── AppSyncTestAPI.swift
├── DefaultTestPostData.swift
├── Foundation+TestUtils.swift
├── Info.plist
├── MockAWSAppSyncServiceConfigProvider.swift
├── MockAWSNetworkTransport.swift
├── MockAuthProvider
│ ├── MockAPIKeyAuthProvider.swift
│ ├── MockIAMAuthProvider.swift
│ └── MockUserPoolsAuthProvider.swift
├── MockAuthProviders.swift
├── MockCancellable.swift
├── MockConnectionProvider
│ └── MockConnectionProvider.swift
├── MockReachability.swift
├── MockS3ObjectManager.swift
├── MockSubscriptionFactory
│ ├── MockSubscriptionConnection.swift
│ └── MockSubscriptionFactory.swift
├── Object+Swizzling.swift
├── S3ObjectWrapper.swift
└── UnitTestHelpers.swift
├── AWSAppSyncTestHostApp
├── AppDelegate.swift
├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ └── Contents.json
├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
├── Info.plist
├── ViewController.swift
├── awsconfiguration.json
├── cacheConfigUnitTests-allTables.db
├── cacheConfigUnitTests-noMutationRecords.db
├── cacheConfigUnitTests-noRecords.db
├── cacheConfigUnitTests-noSubscriptionMetadata.db
└── cacheConfigUnitTests-noTables.db
├── AWSAppSyncUnitTests
├── AWSAppSyncAuthTypeTests.swift
├── AWSAppSyncCacheConfigurationMigrationTests.swift
├── AWSAppSyncCacheConfigurationTests.swift
├── AWSAppSyncClientConfigurationTests.swift
├── AWSAppSyncClientInfoTests.swift
├── AWSAppSyncHTTPNetworkTransportTests.swift
├── AWSAppSyncRetryHandlerTests.swift
├── AWSAppSyncServiceConfigTests.swift
├── AWSSQLiteNormalizedCacheTests.swift
├── AppSyncApolloCustomizationTests.swift
├── AppSyncClientComplexObjectMutationUnitTests.swift
├── AppSyncSubscriptionWithSyncTests.swift
├── CacheKeyTests.swift
├── Foundation+UtilsTests.swift
├── Info.plist
├── JSONValueSerializationTests.swift
├── Logging
│ └── AppSyncLogHelperTests.swift
├── MockAWSAppSyncServiceConfig.swift
├── MutationOptimisticUpdateTests.swift
├── MutationQueueTests.swift
├── ReachabilityChangeNotifierTests.swift
├── Subscription
│ ├── AuthInterceptor
│ │ └── IAMAuthInterceptorTests.swift
│ └── Connection
│ │ └── ConnectionPool
│ │ ├── APIKeyBasedConnectionPoolTests.swift
│ │ ├── IAMBasedConnectionPoolTests.swift
│ │ ├── LambdaBasedConnectionPoolTests.swift
│ │ ├── OIDCBasedConnectionPoolTests.swift
│ │ ├── SubscriptionConnectionFactoryTests.swift
│ │ └── UserPoolsBasedConnectionPoolTests.swift
├── SubscriptionMessagesQueueTests.swift
├── SubscriptionMiscTests.swift
└── SyncStrategyTests.swift
├── ApolloTests
├── BatchedLoadTests.swift
├── CacheKeyForFieldTests.swift
├── CachePersistenceTests.swift
├── DataLoaderTests.swift
├── FetchQueryTests.swift
├── FetchQueryTestsComplex.swift
├── FetchQueryTestsSimple.swift
├── FragmentConstructionAndConversionTests.swift
├── Info.plist
├── InputValueEncodingTests.swift
├── LoadQueryFromStoreTests.swift
├── MockNetworkTransport.swift
├── MutatingResultsTests.swift
├── NormalizeQueryResults.swift
├── ParseQueryResponseTests.swift
├── PromiseTests.swift
├── ReadFieldValueTests.swift
├── ReadWriteFromStoreTests.swift
├── ResultOrPromiseTests.swift
├── TestCacheProvider.swift
├── WatchQueryTests.swift
├── XCTAssertHelpers.swift
└── XCTestCase+Promise.swift
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── Cartfile
├── Gemfile
├── Gemfile.lock
├── LICENSE
├── NOTICE
├── Package.resolved
├── Package.swift
├── Podfile
├── Podfile.lock
├── README.md
├── StarWarsAPI
├── API.swift
├── CharacterAndSubTypesFragments.graphql
├── CreateReviewForEpisode.graphql
├── HeroAndFriendsNames.graphql
├── HeroAppearsIn.graphql
├── HeroConditional.graphql
├── HeroDetails.graphql
├── HeroFriendsOfFriends.graphql
├── HeroName.graphql
├── HeroNameAndAppearsIn.graphql
├── HeroParentTypeDependentField.graphql
├── HeroTypeDependentAliasedField.graphql
├── Human.graphql
├── HumanFriendsFilteredById.graphql
├── Info.plist
├── SameHeroTwice.graphql
├── StarWarsAPI.h
├── Starship.graphql
├── SubscribeReview.graphql
├── TwoHeroes.graphql
└── schema.json
└── build-support
├── build-xcframeworks.sh
├── carthage-build.sh
├── cocoapods_release.sh
├── stage_sdk_release.sh
└── update_sdk_deps.sh
/.gitallowed:
--------------------------------------------------------------------------------
1 | Apollo
2 | apollo
3 | CocoaLumberjack/blob/master
4 | customer master key
5 | CustomerMasterKey
6 | EXAMPLEKEY
7 | master encryption key
8 | Pods
9 |
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @awslabs/amplify-ios
2 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug Report
3 | about: Create a report to help us improve
4 |
5 | ---
6 |
7 | **Describe the bug**
8 | A clear and concise description of what the bug is.
9 |
10 | **To Reproduce**
11 | Steps to reproduce the behavior:
12 | 1. Go to '...'
13 | 2. Click on '....'
14 | 3. Scroll down to '....'
15 | 4. See error
16 |
17 | **Expected behavior**
18 | A clear and concise description of what you expected to happen.
19 |
20 | **Screenshots**
21 | If applicable, add screenshots to help explain your problem.
22 |
23 | **Environment(please complete the following information):**
24 | - AppSync SDK Version: [e.g. 2.6.21]
25 | - Dependency Manager: [e.g. Cocoapods, Carthage]
26 | - Swift Version : [e.g. 4.0]
27 |
28 | **Device Information (please complete the following information):**
29 | - Device: [e.g. iPhone6, Simulator]
30 | - iOS Version: [e.g. iOS 11.4]
31 | - Specific to simulators:
32 |
33 | **Additional context**
34 | Add any other context about the problem here.
35 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature Request
3 | about: Suggest an idea for this project
4 |
5 | ---
6 |
7 | **Is your feature request related to a problem? Please describe.**
8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
9 |
10 | **Describe the solution you'd like**
11 | A clear and concise description of what you want to happen.
12 |
13 | **Describe alternatives you've considered**
14 | A clear and concise description of any alternative solutions or features you've considered.
15 |
16 | **Additional context**
17 | Add any other context or screenshots about the feature request here.
18 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/usage-question.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Usage Question
3 | about: Ask a question about AWS AppSync usage
4 |
5 | ---
6 |
7 | **State your question**
8 |
9 | **Provide code snippets (if applicable)**
10 |
11 | **Environment(please complete the following information):**
12 | - AppSync SDK Version: [e.g. 2.6.21]
13 | - Dependency Manager: [e.g. Cocoapods, Carthage]
14 | - Swift Version : [e.g. 4.0]
15 |
16 | **Device Information (please complete the following information):**
17 | - Device: [e.g. iPhone6, Simulator]
18 | - iOS Version: [e.g. iOS 11.4]
19 | - Specific to simulators:
20 |
21 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | *Issue #, if available:*
2 |
3 | *Description of changes:*
4 |
5 |
6 | By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.
7 |
--------------------------------------------------------------------------------
/.github/stale.yml:
--------------------------------------------------------------------------------
1 | # Number of days of inactivity before an issue becomes stale
2 | # Setting this to 100 years, because we do not want to automatically mark issues as stale
3 | daysUntilStale: 36500
4 | # Number of days of inactivity before a stale issue is closed
5 | daysUntilClose: 7
6 | # Label to use when marking an issue as stale
7 | staleLabel: closing-soon-if-no-response
8 | # Comment to post when marking an issue as stale. Set to `false` to disable
9 | markComment: >
10 | This issue has been automatically marked as stale because it has not had
11 | recent activity. It will be closed if no further activity occurs. Thank you
12 | for your contributions.
13 | # Comment to post when closing a stale issue. Set to `false` to disable
14 | closeComment: >
15 | This issue has been automatically closed because of inactivity.
16 | Please open a new issue if are still encountering problems.
17 | # Limit to only `issues` or `pulls`
18 | only: issues
19 |
--------------------------------------------------------------------------------
/.github/workflows/issue_closed.yml:
--------------------------------------------------------------------------------
1 | name: Issue Closed
2 |
3 | on:
4 | issues:
5 | types: [closed]
6 |
7 | permissions:
8 | issues: write
9 |
10 | jobs:
11 | cleanup-labels:
12 | runs-on: ubuntu-latest
13 | if: ${{ (contains(github.event.issue.labels.*.name, 'pending-response') || contains(github.event.issue.labels.*.name, 'closing soon') || contains(github.event.issue.labels.*.name, 'pending-release')) }}
14 | steps:
15 | - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 #v4.1.1
16 | - name: remove unnecessary labels after closing
17 | shell: bash
18 | env:
19 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
20 | ISSUE_NUMBER: ${{ github.event.issue.number }}
21 | run: |
22 | gh issue edit $ISSUE_NUMBER --remove-label "closing soon" --remove-label "pending-response" --remove-label "pending-release"
23 |
24 | comment-visibility-warning:
25 | runs-on: ubuntu-latest
26 | steps:
27 | - uses: aws-actions/closed-issue-message@v1
28 | with:
29 | repo-token: "${{ secrets.GITHUB_TOKEN }}"
30 | message: |
31 | This issue is now closed. Comments on closed issues are hard for our team to see.
32 | If you need more assistance, please open a new issue that references this one.
33 | If you wish to keep having a conversation with other community members under this issue feel free to do so.
34 |
--------------------------------------------------------------------------------
/.github/workflows/issue_comment.yml:
--------------------------------------------------------------------------------
1 | name: Issue Comment
2 |
3 | on:
4 | issue_comment:
5 | types: [created]
6 |
7 | jobs:
8 | notify:
9 | runs-on: ubuntu-latest
10 | permissions: {}
11 | if: ${{ !github.event.issue.pull_request && !contains(fromJSON('["MEMBER", "OWNER"]'), github.event.comment.author_association) }}
12 | steps:
13 | - name: Run webhook curl command
14 | env:
15 | WEBHOOK_URL: ${{ secrets.SLACK_COMMENT_WEBHOOK_URL }}
16 | COMMENT: ${{toJson(github.event.comment.body)}}
17 | USER: ${{github.event.comment.user.login}}
18 | COMMENT_URL: ${{github.event.comment.html_url}}
19 | shell: bash
20 | run: echo $COMMENT | sed "s/\\\n/. /g; s/\\\r//g; s/[^a-zA-Z0-9 &().,:]//g" | xargs -I {} curl -s POST "$WEBHOOK_URL" -H "Content-Type:application/json" --data '{"comment":"{}", "commentUrl":"'$COMMENT_URL'", "user":"'$USER'"}'
21 |
22 | remove-pending-response-label:
23 | runs-on: ubuntu-latest
24 | permissions:
25 | issues: write
26 | if: ${{ !github.event.issue.pull_request && contains(github.event.issue.labels.*.name, 'pending-response') }}
27 | steps:
28 | - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 #v4.1.1
29 | - name: remove unnecessary labels after closing
30 | shell: bash
31 | env:
32 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
33 | ISSUE_NUMBER: ${{ github.event.issue.number }}
34 | run: |
35 | gh issue edit $ISSUE_NUMBER --remove-label "pending-response"
--------------------------------------------------------------------------------
/.github/workflows/issue_opened.yml:
--------------------------------------------------------------------------------
1 | name: Issue Opened
2 | on:
3 | issues:
4 | types: [opened]
5 |
6 | jobs:
7 | notify:
8 | runs-on: ubuntu-latest
9 | permissions: {}
10 | if: ${{ !contains(fromJSON('["MEMBER", "OWNER"]'), github.event.issue.author_association) }}
11 | steps:
12 | - name: Run webhook curl command
13 | env:
14 | WEBHOOK_URL: ${{ secrets.SLACK_ISSUE_WEBHOOK_URL }}
15 | ISSUE: ${{toJson(github.event.issue.title)}}
16 | ISSUE_URL: ${{github.event.issue.html_url}}
17 | USER: ${{github.event.issue.user.login}}
18 | shell: bash
19 | run: echo $ISSUE | sed 's/[^a-zA-Z0-9 &().,:]//g' | xargs -I {} curl -s POST "$WEBHOOK_URL" -H "Content-Type:application/json" --data '{"issue":"{}", "issueUrl":"'$ISSUE_URL'", "user":"'$USER'"}'
20 |
21 | maintainer-opened:
22 | runs-on: ubuntu-latest
23 | permissions:
24 | issues: write
25 | if: ${{ contains(fromJSON('["MEMBER", "OWNER"]'), github.event.issue.author_association) }}
26 | steps:
27 | - name: Post comment if maintainer opened.
28 | shell: bash
29 | env:
30 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
31 | ISSUE_NUMBER: ${{ github.event.issue.number }}
32 | run: |
33 | gh issue comment $ISSUE_NUMBER --repo awslabs/aws-mobile-appsync-sdk-ios -b "This issue was opened by a maintainer of this repository; updates will be posted here. If you are also experiencing this issue, please comment here with any relevant information so that we're aware and can prioritize accordingly."
34 |
--------------------------------------------------------------------------------
/.github/workflows/notify_release.yml:
--------------------------------------------------------------------------------
1 | name: Notify Release
2 |
3 | on:
4 | release:
5 | types: [released]
6 |
7 | permissions: {}
8 |
9 | jobs:
10 | notify:
11 | runs-on: ubuntu-latest
12 | steps:
13 | - name: Run webhook curl command
14 | env:
15 | WEBHOOK_URL: ${{ secrets.SLACK_RELEASE_WEBHOOK_URL }}
16 | VERSION: ${{github.event.release.html_url}}
17 | REPO_NAME: ${{github.event.repository.name}}
18 | ACTION_NAME: ${{github.event.action}}
19 | shell: bash
20 | run: echo $VERSION | xargs -I {} curl -s POST "$WEBHOOK_URL" -H "Content-Type:application/json" --data '{"action":"'$ACTION_NAME'", "repo":"'$REPO_NAME'", "version":"{}"}'
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | AWSAppSyncClient.xcworkspace/
3 | docs/_site/
4 | xcuserdata
5 | */appsync_test_credentials.json
6 | Carthage
7 | Pods
8 | .build/
9 | build/
10 |
--------------------------------------------------------------------------------
/.swiftlint.yml:
--------------------------------------------------------------------------------
1 | # Do not specify an `included` section at this top-level file. Specify the
2 | # `--config` option pointing to this file, and the `--path` option to the files
3 | # you wish to lint
4 |
5 | excluded:
6 | - AWSAppSyncClient/Apollo
7 |
8 |
9 | # Note: This feature is experimental. As of this writing (4-Dec-2018) warnings
10 | # triggered by these rules should not be used to fail builds. Known issues:
11 | # - unused_private_declaration incorrectly triggers on
12 | # AWSAppSyncClientConfiguration.oidcAuthProvider
13 | analyzer_rules:
14 | - unused_import
15 |
16 | disabled_rules: # rule identifiers to exclude from running
17 | - file_length
18 | - force_cast
19 | - force_try
20 | - function_parameter_count
21 | - large_tuple
22 | - line_length
23 | - nesting
24 | - redundant_optional_initialization
25 | - trailing_whitespace
26 | - type_body_length
27 | - type_name
28 | - identifier_name
29 | - unused_closure_parameter
30 | - weak_delegate
31 |
32 | # configurable rules can be customized from this configuration file
33 | closing_brace: error
34 | comma: error
35 | colon:
36 | severity: error
37 | empty_enum_arguments: error
38 | function_body_length:
39 | warning: 100
40 | error: 150
41 | opening_brace: error
42 | return_arrow_whitespace: error
43 | statement_position:
44 | severity: error
45 | todo: warning
46 | trailing_semicolon: error
47 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: swift
2 | osx_image: xcode13.2
3 | env:
4 | global:
5 | - FRAMEWORK_NAME=AWSAppSync
6 | - BUILD_FOLDER=./build
7 | xcode_workspace: AWSAppSyncClient.xcworkspace
8 | xcode_scheme: AWSAppSync
9 | before_install:
10 | - gem update bundler
11 | - openssl aes-256-cbc -K $encrypted_6919a533707f_key -iv $encrypted_6919a533707f_iv
12 | -in AWSAppSyncIntegrationTests/appsync_test_credentials.json.enc
13 | -out AWSAppSyncIntegrationTests/appsync_test_credentials.json
14 | -d
15 | - brew update
16 | - brew outdated carthage || brew upgrade carthage
17 |
18 | before_deploy:
19 | - bash ./build-support/carthage-build.sh build --no-skip-current
20 | - bash ./build-support/carthage-build.sh archive $FRAMEWORK_NAME
21 | - bash ./build-support/build-xcframeworks.sh $FRAMEWORK_NAME
22 | script:
23 | - xcodebuild -quiet -workspace AWSAppSyncClient.xcworkspace -scheme AWSAppSync build test -destination 'platform=iOS Simulator,name=iPhone 11,OS=latest'
24 | deploy:
25 | - provider: releases
26 | api_key:
27 | secure: "grCqWY+k3n5skvqToIQ6oC+5KPZGvrW0/YV5BghmtXA5QC+N2wadEBr/8rzdHnFq4WDDrlpHSMKVe/X0zgvuAjbGdSRWAynKpuN7frHVewzAA0q06v/zW8UMpCT3T1Oaz+Yn4jNH2GHAZy05onbZfXKQbQlgJaHl9LLBUYCqWekc/LNQ1+YxreyGxROTOWoQgPBEqrkcHss2Ol1N2D3i4/eTI2CvAhLpJWOm2TlttZHfW8x1vVcCGMx/URcnqP08lJoHhMG25rTLQRABRycf473HMnR9fzhYGTGvlY0cykuvdFRrOAS1FcFxiNkDrs5m3+VvvDzZwL7+iExd9Uz+TZJehhMUb5uXcxNXkf4dIReFaDkN/Jj6uQQYhZJPi5hJjciTWqaGsdZ2dJtJpx1oVGI7pm2OwhVqYeh+AH8tmshmZK4swMnSvo+cZXvc9Pn59GdrUkt1XhfQmRLeCWxZgpG7jEShpBg7J7ky3iQj8xVfcnsA9xks+WQ4qt9DXdhS1Xou6UItDFBW8VB7zNU4SobKCMJDF4ZHWLPsR5jmU8jolSE4gO9+vNmhcAEbaW8gCsFOBsoQUcZwuxuSBwHDp+TDmKRtlTXhHVeOwR1H+Cij29zUiK1+Pr8NFv6448Kn0dIlgGkhPFq7c+5lZQtT9C2aelg0hwyPinCYVw5XpHY="
28 | file:
29 | - "$FRAMEWORK_NAME.framework.zip"
30 | - "$BUILD_FOLDER/$FRAMEWORK_NAME.xcframework.zip"
31 | skip_cleanup: true
32 | on:
33 | repo: awslabs/aws-mobile-appsync-sdk-ios
34 | tags: true
35 | - provider: script
36 | script: bash ./build-support/cocoapods_release.sh
37 | on:
38 | repo: awslabs/aws-mobile-appsync-sdk-ios
39 | tags: true
40 |
--------------------------------------------------------------------------------
/AWSAppSync.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |s|
2 | s.name = 'AWSAppSync'
3 | s.version = '3.7.1'
4 | s.author = 'AWS'
5 | s.homepage = 'http://aws.amazon.com/mobile/sdk'
6 | s.license = { :type => 'Amazon Software License', :file => 'LICENSE' }
7 | s.summary = "iOS client to access AWSAppSync backend."
8 | s.source = { :git => 'https://github.com/awslabs/aws-mobile-appsync-sdk-ios.git',
9 | :tag => s.version }
10 | s.requires_arc = true
11 | s.ios.deployment_target = '12.0'
12 | s.swift_version = '5.5.2'
13 |
14 | s.dependency 'AWSCore', '~> 2.36.0'
15 | s.dependency 'SQLite.swift', '~> 0.12.2'
16 | s.dependency 'AppSyncRealTimeClient', '~> 3.2.0'
17 |
18 | s.source_files = 'AWSAppSyncClient/AWSAppSync.h', 'AWSAppSyncClient/*.swift', 'AWSAppSyncClient/Internal/**/*.{h,m,swift}', 'AWSAppSyncClient/Apollo/Sources/Apollo/*.swift'
19 | s.public_header_files = ['AWSAppSyncClient/AWSAppSync.h', 'AWSAppSyncClient/AWSAppSync-Swift.h', 'AWSAppSyncClient/Internal/AppSyncLogHelper.h']
20 | end
21 |
--------------------------------------------------------------------------------
/AWSAppSyncClient.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/AWSAppSyncClient.xcodeproj/project.xcworkspace/xcshareddata/AWSDeepDishClient.xcscmblueprint:
--------------------------------------------------------------------------------
1 | {
2 | "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "6EF152C53E8F39FB6C48AF9285DD3D14B4A81CFE",
3 | "DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : {
4 |
5 | },
6 | "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : {
7 | "C4040456399D4D4B43992C78F2AD702EE726E16A" : 9223372036854775807,
8 | "6EF152C53E8F39FB6C48AF9285DD3D14B4A81CFE" : 9223372036854775807
9 | },
10 | "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "749CC790-83D4-46F6-96D7-7BC2CD204228",
11 | "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : {
12 | "C4040456399D4D4B43992C78F2AD702EE726E16A" : "AWSiOSSDKv2\/",
13 | "6EF152C53E8F39FB6C48AF9285DD3D14B4A81CFE" : "AWSDeepDishiOSClient\/"
14 | },
15 | "DVTSourceControlWorkspaceBlueprintNameKey" : "AWSDeepDishClient",
16 | "DVTSourceControlWorkspaceBlueprintVersion" : 204,
17 | "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "AWSDeepDishClient.xcodeproj",
18 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [
19 | {
20 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "ssh:\/\/git.amazon.com:2222\/pkg\/AWSDeepDishiOSClient",
21 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
22 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "6EF152C53E8F39FB6C48AF9285DD3D14B4A81CFE"
23 | },
24 | {
25 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "ssh:\/\/git.amazon.com:2222\/pkg\/AWSiOSSDKv2",
26 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
27 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "C4040456399D4D4B43992C78F2AD702EE726E16A"
28 | }
29 | ]
30 | }
--------------------------------------------------------------------------------
/AWSAppSyncClient.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/AWSAppSyncClient.xcodeproj/project.xcworkspace/xcuserdata/rohandub.xcuserdatad/IDEFindNavigatorScopes.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/AWSAppSyncClient.xcodeproj/project.xcworkspace/xcuserdata/rohandub.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/awslabs/aws-mobile-appsync-sdk-ios/589d58ba0e2ae7eac0058235f27bf2b41f66fb85/AWSAppSyncClient.xcodeproj/project.xcworkspace/xcuserdata/rohandub.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/AWSAppSyncClient.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/AWSAppSyncClient.xcworkspace/xcshareddata/IDETemplateMacros.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | FILEHEADER
6 |
7 | // Copyright ___YEAR___ Amazon.com, Inc. or its affiliates. All Rights Reserved.
8 | // Licensed under the Amazon Software License
9 | // http://aws.amazon.com/asl/
10 | //
11 |
12 |
13 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/AWSAppSync.h:
--------------------------------------------------------------------------------
1 | //
2 | // AWSAppSync.h
3 | // AWSAppSync
4 | //
5 |
6 | #import
7 |
8 | //! Project version number for AWSAppSyncClient.
9 | FOUNDATION_EXPORT double AWSAppSyncClientVersionNumber;
10 |
11 | //! Project version string for AWSAppSyncClient.
12 | FOUNDATION_EXPORT const unsigned char AWSAppSyncClientVersionString[];
13 |
14 | // In this header, you should import all the public headers of your framework using statements like #import
15 |
16 | #if __has_include("AWSAppSync/AWSAppSync-umbrella.h")
17 | #import "AWSAppSync/AWSAppSync-umbrella.h"
18 | #endif
19 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/AWSAppSyncAuthProvider.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AWSAppSyncAuthProvider.swift
3 | // AWSAppSync
4 | //
5 |
6 | // MARK: AWSOIDCAuthProvider
7 | // For using OIDC based authorization, this protocol needs to be implemented and passed to configuration object.
8 | // Use this for cases where the OIDC token needs to be fetched asynchronously and requires a callback
9 | public protocol AWSOIDCAuthProviderAsync: AWSOIDCAuthProvider {
10 | func getLatestAuthToken(_ callback: @escaping (String?, Error?) -> Void)
11 | }
12 |
13 | // For AuthProviders that use a callback, the getLatestAuthToken is defaulted to return an empty string
14 | extension AWSOIDCAuthProviderAsync {
15 | public func getLatestAuthToken() -> String { fatalError("Callback method required") }
16 | }
17 |
18 | // For using OIDC based authorization, this protocol needs to be implemented and passed to configuration object.
19 | public protocol AWSOIDCAuthProvider {
20 | /// The method should fetch the token and return it to the client for using in header request.
21 | func getLatestAuthToken() -> String
22 | }
23 |
24 | // MARK: - AWSCognitoUserPoolsProvider
25 | // For using User Pool based authorization, this protocol needs to be implemented and passed to configuration object.
26 | // Use this for cases where the UserPool auth token needs to be fetched asynchronously and requires a callback
27 | public protocol AWSCognitoUserPoolsAuthProviderAsync: AWSCognitoUserPoolsAuthProvider {
28 | func getLatestAuthToken(_ callback: @escaping (String?, Error?) -> Void)
29 | }
30 |
31 | // For CognitoUserPoolAuthProviders that use a callback, the getLatestAuthToken is defaulted to return an empty string
32 | extension AWSCognitoUserPoolsAuthProviderAsync {
33 | public func getLatestAuthToken() -> String { fatalError("Callback method required") }
34 | }
35 |
36 | // For using Cognito User Pools based authorization, this protocol needs to be implemented and passed to configuration object.
37 | public protocol AWSCognitoUserPoolsAuthProvider: AWSOIDCAuthProvider {
38 |
39 | }
40 |
41 | // MARK: - AWSLambdaAuthProvider
42 | // For using Lambda based authorization, this protocol needs to be implemented and passed to configuration object.
43 | // Use this for cases where the authorization token needs to be fetched asynchronously and requires a callback
44 | public protocol AWSLambdaAuthProviderAsync: AWSLambdaAuthProvider {
45 | func getLatestAuthToken(_ callback: @escaping (String?, Error?) -> Void)
46 | }
47 |
48 | // For AWSLambdaAuthProvider that use a callback, the getLatestAuthToken is defaulted to return an empty string
49 | extension AWSLambdaAuthProviderAsync {
50 | public func getLatestAuthToken() -> String { fatalError("Callback method required") }
51 | }
52 |
53 | // For using AWS Lambda based authorization, this protocol needs to be implemented and passed to configuration object.
54 | public protocol AWSLambdaAuthProvider {
55 | /// The method should fetch the token and return it to the client for using in header request.
56 | func getLatestAuthToken() -> String
57 | }
58 |
59 | // MARK: - AWSAPIKeyAuthProvider
60 | // For using API Key based authorization, this protocol needs to be implemented and passed to configuration object.
61 | public protocol AWSAPIKeyAuthProvider {
62 | func getAPIKey() -> String
63 | }
64 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/AWSAppSyncAuthType.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | /// Supported authentication types for the AppSyncClient
8 | public enum AWSAppSyncAuthType: String, Codable, Hashable {
9 | /// AWS Identity and Access Management (IAM), for role-based authentication
10 | case awsIAM = "AWS_IAM"
11 |
12 | /// A single API key for all app users
13 | case apiKey = "API_KEY"
14 |
15 | /// OpenID Connect
16 | case oidcToken = "OPENID_CONNECT"
17 |
18 | /// User directory based authentication
19 | case amazonCognitoUserPools = "AMAZON_COGNITO_USER_POOLS"
20 |
21 | case awsLambda = "AWS_LAMBDA"
22 |
23 | /// Convenience method to use instead of `AuthType(rawValue:)`
24 | public static func getAuthType(rawValue: String) throws -> AWSAppSyncAuthType {
25 | guard let authType = AWSAppSyncAuthType(rawValue: rawValue) else {
26 | throw AWSAppSyncClientConfigurationError.invalidAuthConfiguration("AuthType not recognized. Pass in a valid AuthType.")
27 | }
28 | return authType
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/AWSAppSyncClientConfigurationError.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | import Foundation
8 |
9 | public enum AWSAppSyncClientConfigurationError {
10 | case invalidAuthConfiguration(String)
11 | case cacheConfigurationAlreadyInUse(String)
12 | }
13 |
14 | extension AWSAppSyncClientConfigurationError: Error {
15 | var localizedDescription: String {
16 | return errorDescription ?? String(describing: self)
17 | }
18 | }
19 |
20 | extension AWSAppSyncClientConfigurationError: LocalizedError {
21 | public var errorDescription: String? {
22 | switch self {
23 | case .invalidAuthConfiguration(let message):
24 | return "Invalid Auth Configuration: \(message)"
25 | case .cacheConfigurationAlreadyInUse(let message):
26 | return "Cache Configuration Already In Use: \(message)"
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/AWSAppSyncClientError.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | import Foundation
8 |
9 | public enum AWSAppSyncClientError: Error {
10 | case requestFailed(Data?, HTTPURLResponse?, Error?)
11 | case noData(HTTPURLResponse)
12 | case parseError(Data, HTTPURLResponse, Error?)
13 | case authenticationError(Error)
14 | }
15 |
16 | // MARK: - LocalizedError
17 |
18 | extension AWSAppSyncClientError: LocalizedError {
19 |
20 | public var errorDescription: String? {
21 | let underlyingError: Error?
22 | var message: String
23 | let errorResponse: HTTPURLResponse?
24 |
25 | switch self {
26 | case .requestFailed(_, let response, let error):
27 | errorResponse = response
28 | underlyingError = error
29 | message = "Did not receive a successful HTTP code."
30 | case .noData(let response):
31 | errorResponse = response
32 | underlyingError = nil
33 | message = "No Data received in response."
34 | case .parseError(_, let response, let error):
35 | underlyingError = error
36 | errorResponse = response
37 | message = "Could not parse response data."
38 | case .authenticationError(let error):
39 | underlyingError = error
40 | errorResponse = nil
41 | message = "Failed to authenticate request."
42 | }
43 |
44 | if let error = underlyingError {
45 | message += " Error: \(error)"
46 | }
47 |
48 | if let unwrappedResponse = errorResponse {
49 | return "(\(unwrappedResponse.statusCode) \(unwrappedResponse.statusCodeDescription)) \(message)"
50 | } else {
51 | return "\(message)"
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/AWSAppSyncClientInfoError.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | import Foundation
8 |
9 | public struct AWSAppSyncClientInfoError {
10 | public let errorMessage: String?
11 | }
12 |
13 | extension AWSAppSyncClientInfoError: Error {
14 | public var localizedDescription: String {
15 | return errorDescription ?? errorMessage ?? String(describing: self)
16 | }
17 | }
18 |
19 | extension AWSAppSyncClientInfoError: LocalizedError {
20 | public var errorDescription: String? {
21 | return errorMessage
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/AWSAppSyncClientLogFormatter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | import Foundation
8 | import AWSCore
9 |
10 | public final class AWSAppSyncClientLogFormatter: NSObject, AWSDDLogFormatter {
11 |
12 | static let dateFormatter: DateFormatter = {
13 | let dateFormatter = DateFormatter()
14 | // 2019-02-27 15:09:34.624-0800
15 | dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSSZ"
16 | return dateFormatter
17 | }()
18 |
19 | public func format(message logMessage: AWSDDLogMessage) -> String? {
20 | let logLevelPrefix: String
21 | switch logMessage.flag {
22 | case .error:
23 | logLevelPrefix = "E"
24 | case .warning:
25 | logLevelPrefix = "W"
26 | case .info:
27 | logLevelPrefix = "I"
28 | case .debug:
29 | logLevelPrefix = "D"
30 | default:
31 | logLevelPrefix = "V"
32 | }
33 |
34 | let date = AWSAppSyncClientLogFormatter.dateFormatter.string(from: logMessage.timestamp)
35 | let file = AWSDDExtractFileNameWithoutExtension(logMessage.file, false) ?? "(no file)"
36 | let line = String(describing: logMessage.line)
37 |
38 | var sourceSection = file
39 | if let function = logMessage.function {
40 | sourceSection += ".\(function)"
41 | }
42 | sourceSection += ", L\(line)"
43 |
44 | let message = "\(date) [\(logLevelPrefix) \(sourceSection)] \(logMessage.message)"
45 | return message
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/AWSAppSyncClientS3ObjectsExtensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AWSAppSyncS3ObjectsExtensions.swift
3 | // AWSAppSync
4 | //
5 |
6 | import Foundation
7 |
8 | extension AWSAppSyncClient {
9 |
10 | func performS3ObjectUploadForMutation(
11 | operation: Operation,
12 | s3Object: InternalS3ObjectDetails,
13 | resultHandler: @escaping (Error?) -> Void) {
14 |
15 | s3ObjectManager!.upload(s3Object: s3Object) { _, error in
16 | resultHandler(error)
17 | }
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/AWSAppSyncConnection.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | import Foundation
8 |
9 | public struct AppSyncConnectionInfo {
10 | public let isConnectionAvailable: Bool
11 | public let isInitialConnection: Bool
12 | }
13 |
14 | public enum ClientNetworkAccessState {
15 | case Online
16 | case Offline
17 | }
18 |
19 | public protocol ConnectionStateChangeHandler {
20 | func stateChanged(networkState: ClientNetworkAccessState)
21 | }
22 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/AWSAppSyncMutations.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | import Foundation
8 |
9 | public enum MutationRecordState: String {
10 | case inProgress
11 | case inQueue
12 | case isDone
13 | }
14 |
15 | public enum MutationType: String {
16 | case graphQLMutation
17 | case graphQLMutationWithS3Object
18 | }
19 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/AWSAppSyncRetryStrategy.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | import Foundation
8 |
9 | /// The retry strategy to be used by the `AWSAppSyncClient`.
10 | /// You can specify this in the `AWSAppSyncClientConfiguration`.
11 | ///
12 | /// - exponential: Backs off exponentially before retrying a HTTP request. Starts from 400ms and grows exponentially w/ jitter; stops the retries after the back off reaches 5 minutes.
13 | /// - aggressive: Aggressively retries every 1s w/ jitter for up to 12 attempts.
14 | public enum AWSAppSyncRetryStrategy {
15 | case exponential, aggressive
16 | }
17 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/AWSAppSyncServiceConfigError.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | import Foundation
8 |
9 | public enum AWSAppSyncServiceConfigError: String {
10 | /// An error occurred reading the configuration file, or the file did not contain a properly configured "AppSync" section
11 | case invalidConfigFile
12 |
13 | /// An error occurred loading the API endpoing URL
14 | case invalidAPIURL
15 |
16 | /// An error occurred loading the API region
17 | case invalidRegion
18 |
19 | /// An error occurred loading the auth mode
20 | case invalidAuthMode
21 |
22 | /// AuthMode was set to "API_KEY" but a valid value for API_KEY was not found
23 | case invalidAPIKey
24 | }
25 |
26 | extension AWSAppSyncServiceConfigError: Error {
27 | public var localizedDescription: String {
28 | return errorDescription ?? self.rawValue
29 | }
30 | }
31 |
32 | extension AWSAppSyncServiceConfigError: LocalizedError {
33 | public var errorDescription: String? {
34 | return self.rawValue
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/AWSAppSyncSubscriptionError.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | import Foundation
8 |
9 | public enum AWSAppSyncSubscriptionError: Error, LocalizedError {
10 | /// The underlying connection reported a status of "connectionError"
11 | case connectionError
12 |
13 | /// The underlying connection reported a status of "connectionRefused"
14 | case connectionRefused
15 |
16 | /// The underlying connection reported a status of "disconnected"
17 | case disconnected
18 |
19 | /// An error occurred parsing the subscription message received from the service
20 | case messageCallbackError(String)
21 |
22 | /// Some other error occurred. See associated value for details
23 | case other(Error)
24 |
25 | /// An error occurred parsing the published subscription message
26 | case parseError(Error)
27 |
28 | /// The underlying connection reported a status of "protocolError"
29 | case protocolError
30 |
31 | /// An error occurred while making the initial subscription request to AppSync, parsing its response, or
32 | /// evaluating the response's subscription info payload
33 | case setupError(String)
34 |
35 | /// The underlying MQTT client reported a status of "unknown"
36 | @available(*, deprecated, message: "Subscription is not tied with mqtt connection anymore")
37 | case unknownMQTTConnectionStatus
38 |
39 | public var errorDescription: String? {
40 | switch self {
41 | case .messageCallbackError(let message):
42 | return message
43 | case .other(let error):
44 | return error.localizedDescription
45 | case .parseError(let error):
46 | return error.localizedDescription
47 | case .setupError(let message):
48 | return message
49 | case .unknownMQTTConnectionStatus:
50 | return "MQTT status unknown"
51 | default:
52 | return "Subscription Terminated."
53 | }
54 | }
55 |
56 | public var recoverySuggestion: String? {
57 | switch self {
58 | case .other(let error as NSError):
59 | return error.localizedRecoverySuggestion
60 | case .parseError, .unknownMQTTConnectionStatus:
61 | return nil
62 | default:
63 | return "Restart subscription request."
64 | }
65 | }
66 |
67 | public var failureReason: String? {
68 | switch self {
69 | case .other(let error as NSError):
70 | return error.localizedFailureReason
71 | case .parseError, .unknownMQTTConnectionStatus:
72 | return nil
73 | case .setupError(let message):
74 | return message
75 | default:
76 | return "Disconnected from service."
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/AWSAppSyncSubscriptionWatcherStatus.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | import Foundation
8 |
9 | /// The status of a SubscriptionWatcher
10 | public enum AWSAppSyncSubscriptionWatcherStatus {
11 | /// The subscription is in process of connecting
12 | case connecting
13 |
14 | /// The subscription has connected and is receiving events from the service
15 | case connected
16 |
17 | /// The subscription has been disconnected because of a lifecycle event or manual disconnect request
18 | case disconnected
19 |
20 | /// The subscription is in an error state. The enum's associated value will provide more details, including recovery options if available.
21 | case error(AWSAppSyncSubscriptionError)
22 | }
23 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/AWSNetworkTransport.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AWSNetworkTransport.swift
3 | // AWSAppSyncClient
4 | //
5 |
6 | import Foundation
7 |
8 | public protocol AWSNetworkTransport: AnyObject, NetworkTransport {
9 | func send(data: Data, completionHandler: ((JSONObject?, Error?) -> Void)?)
10 | func sendSubscriptionRequest(operation: Operation, completionHandler: @escaping (JSONObject?, Error?) -> Void) throws -> Cancellable
11 | }
12 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/Apollo/Sources/Apollo/Apollo.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | //! Project version number for Apollo.
4 | FOUNDATION_EXPORT double ApolloVersionNumber;
5 |
6 | //! Project version string for Apollo.
7 | FOUNDATION_EXPORT const unsigned char ApolloVersionString[];
8 |
9 | // In this header, you should import all the public headers of your framework using statements like #import
10 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/Apollo/Sources/Apollo/AsynchronousOperation.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | class AsynchronousOperation: Operation {
4 | @objc class func keyPathsForValuesAffectingIsExecuting() -> Set {
5 | return ["state"]
6 | }
7 |
8 | @objc class func keyPathsForValuesAffectingIsFinished() -> Set {
9 | return ["state"]
10 | }
11 |
12 | enum State {
13 | case initialized
14 | case ready
15 | case executing
16 | case finished
17 | }
18 |
19 | var state: State = .initialized {
20 | willSet {
21 | willChangeValue(forKey: "state")
22 | }
23 | didSet {
24 | didChangeValue(forKey: "state")
25 | }
26 | }
27 |
28 | override var isAsynchronous: Bool {
29 | return true
30 | }
31 |
32 | override var isReady: Bool {
33 | let ready = super.isReady
34 | if ready {
35 | state = .ready
36 | }
37 | return ready
38 | }
39 |
40 | override var isExecuting: Bool {
41 | return state == .executing
42 | }
43 |
44 | override var isFinished: Bool {
45 | return state == .finished
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/Apollo/Sources/Apollo/Collections.swift:
--------------------------------------------------------------------------------
1 | public extension Dictionary {
2 | static func += (lhs: inout Dictionary, rhs: Dictionary) {
3 | #if swift(>=3.2)
4 | lhs.merge(rhs) { (_, new) in new }
5 | #else
6 | for (key, value) in rhs {
7 | lhs[key] = value
8 | }
9 | #endif
10 | }
11 | }
12 |
13 | extension Dictionary {
14 | init(_ entries: S) where S.Iterator.Element == Element {
15 | self = Dictionary(minimumCapacity: entries.underestimatedCount)
16 | for (key, value) in entries {
17 | self[key] = value
18 | }
19 | }
20 | }
21 |
22 | struct GroupedSequence {
23 | private(set) var keys: [Key] = []
24 | fileprivate var groupsForKeys: [[Value]] = []
25 |
26 | mutating func append(value: Value, forKey key: Key) -> (Int, Int) {
27 | if let index = keys.firstIndex(where: { $0 == key }) {
28 | groupsForKeys[index].append(value)
29 | return (index, groupsForKeys[index].endIndex - 1)
30 | } else {
31 | keys.append(key)
32 | groupsForKeys.append([value])
33 | return (keys.endIndex - 1, 0)
34 | }
35 | }
36 | }
37 |
38 | extension GroupedSequence: Sequence {
39 | func makeIterator() -> GroupedSequenceIterator {
40 | return GroupedSequenceIterator(base: self)
41 | }
42 | }
43 |
44 | struct GroupedSequenceIterator: IteratorProtocol {
45 | private var base: GroupedSequence
46 |
47 | private var keyIterator: EnumeratedSequence>.Iterator
48 |
49 | init(base: GroupedSequence) {
50 | self.base = base
51 | keyIterator = base.keys.enumerated().makeIterator()
52 | }
53 |
54 | mutating func next() -> (Key, [Value])? {
55 | if let (index, key) = keyIterator.next() {
56 | let values = base.groupsForKeys[index]
57 | return (key, values)
58 | } else {
59 | return nil
60 | }
61 | }
62 | }
63 |
64 | public func unzip(_ array: [(Element1, Element2)]) -> ([Element1], [Element2]) {
65 | var array1: [Element1] = []
66 | var array2: [Element2] = []
67 |
68 | for element in array {
69 | array1.append(element.0)
70 | array2.append(element.1)
71 | }
72 |
73 | return (array1, array2)
74 | }
75 |
76 | public func unzip(_ array: [(Element1, Element2, Element3)]) -> ([Element1], [Element2], [Element3]) {
77 | var array1: [Element1] = []
78 | var array2: [Element2] = []
79 | var array3: [Element3] = []
80 |
81 | for element in array {
82 | array1.append(element.0)
83 | array2.append(element.1)
84 | array3.append(element.2)
85 | }
86 |
87 | return (array1, array2, array3)
88 | }
89 |
90 | public func unzip(_ array: [[Element]], count: Int) -> [[Element]] {
91 | var unzippedArray: [[Element]] = Array(repeating: [], count: count)
92 |
93 | for valuesForElement in array {
94 | for (index, value) in valuesForElement.enumerated() {
95 | unzippedArray[index].append(value)
96 | }
97 | }
98 |
99 | return unzippedArray
100 | }
101 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/Apollo/Sources/Apollo/DataLoader.swift:
--------------------------------------------------------------------------------
1 | import Dispatch
2 |
3 | public final class DataLoader {
4 | public typealias BatchLoad = ([Key]) -> Promise<[Value]>
5 | typealias Load = (key: Key, fulfill: (Value) -> Void, reject: (Error) -> Void)
6 |
7 | private let queue: DispatchQueue
8 |
9 | private var batchLoad: BatchLoad
10 |
11 | private var cache: [Key: Promise] = [:]
12 | private var loads: [Load] = []
13 |
14 | public init(_ batchLoad: @escaping BatchLoad) {
15 | queue = DispatchQueue(label: "com.apollographql.DataLoader")
16 |
17 | self.batchLoad = batchLoad
18 | }
19 |
20 | subscript(key: Key) -> Promise {
21 | if let promise = cache[key] {
22 | return promise
23 | }
24 |
25 | let promise = Promise { fulfill, reject in
26 | enqueue(load: (key, fulfill, reject))
27 | }
28 |
29 | cache[key] = promise
30 |
31 | return promise
32 | }
33 |
34 | private func enqueue(load: Load) {
35 | queue.async {
36 | self.loads.append(load)
37 | }
38 | }
39 |
40 | func dispatch() {
41 | queue.async {
42 | let loads = self.loads
43 |
44 | if loads.isEmpty { return }
45 |
46 | self.loads = []
47 |
48 | let keys = loads.map { $0.key }
49 |
50 | self.batchLoad(keys).andThen { values in
51 | for (load, value) in zip(loads, values) {
52 | load.fulfill(value)
53 | }
54 | }
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/Apollo/Sources/Apollo/GraphQLDependencyTracker.swift:
--------------------------------------------------------------------------------
1 | final class GraphQLDependencyTracker: GraphQLResultAccumulator {
2 | private var dependentKeys: Set = Set()
3 |
4 | func accept(scalar: JSONValue, info: GraphQLResolveInfo) {
5 | dependentKeys.insert(joined(path: info.cachePath))
6 | }
7 |
8 | func acceptNullValue(info: GraphQLResolveInfo) {
9 | dependentKeys.insert(joined(path: info.cachePath))
10 | }
11 |
12 | func accept(list: [Void], info: GraphQLResolveInfo) {
13 | dependentKeys.insert(joined(path: info.cachePath))
14 | }
15 |
16 | func accept(fieldEntry: Void, info: GraphQLResolveInfo) {
17 | }
18 |
19 | func accept(fieldEntries: [Void], info: GraphQLResolveInfo) {
20 | }
21 |
22 | func finish(rootValue: Void, info: GraphQLResolveInfo) -> Set {
23 | return dependentKeys
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/Apollo/Sources/Apollo/GraphQLError.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | /// Represents an error encountered during the execution of a GraphQL operation.
4 | ///
5 | /// - SeeAlso: [The Response Format section in the GraphQL specification](https://facebook.github.io/graphql/#sec-Response-Format)
6 | public struct GraphQLError: Error {
7 | private let object: JSONObject
8 |
9 | init(_ object: JSONObject) {
10 | self.object = object
11 | }
12 |
13 | init(_ message: String) {
14 | self.init(["message": message])
15 | }
16 |
17 | /// GraphQL servers may provide additional entries as they choose to produce more helpful or machine‐readable errors.
18 | public subscript(key: String) -> Any? {
19 | return object[key]
20 | }
21 |
22 | /// A description of the error.
23 | public var message: String {
24 | return self["message"] as! String
25 | }
26 |
27 | /// A list of locations in the requested GraphQL document associated with the error.
28 | public var locations: [Location]? {
29 | return (self["locations"] as? [JSONObject])?.map(Location.init)
30 | }
31 |
32 | /// Represents a location in a GraphQL document.
33 | public struct Location {
34 | /// The line number of a syntax element.
35 | public let line: Int
36 | /// The column number of a syntax element.
37 | public let column: Int
38 |
39 | init(_ object: JSONObject) {
40 | line = object["line"] as! Int
41 | column = object["column"] as! Int
42 | }
43 | }
44 | }
45 |
46 | extension GraphQLError: CustomStringConvertible {
47 | public var description: String {
48 | return self.message
49 | }
50 | }
51 |
52 | extension GraphQLError: LocalizedError {
53 | public var errorDescription: String? {
54 | return description
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/Apollo/Sources/Apollo/GraphQLOperation.swift:
--------------------------------------------------------------------------------
1 | public protocol GraphQLOperation: AnyObject {
2 | static var rootCacheKey: String { get }
3 |
4 | static var operationString: String { get }
5 | static var requestString: String { get }
6 | static var operationIdentifier: String? { get }
7 |
8 | var variables: GraphQLMap? { get }
9 |
10 | associatedtype Data: GraphQLSelectionSet
11 | }
12 |
13 | public extension GraphQLOperation {
14 | static var requestString: String {
15 | return operationString
16 | }
17 |
18 | static var operationIdentifier: String? {
19 | return nil
20 | }
21 |
22 | var variables: GraphQLMap? {
23 | return nil
24 | }
25 | }
26 |
27 | public protocol GraphQLQuery: GraphQLOperation {}
28 | public extension GraphQLQuery {
29 | static var rootCacheKey: String { return "QUERY_ROOT" }
30 | }
31 |
32 | public protocol GraphQLMutation: GraphQLOperation {}
33 | public extension GraphQLMutation {
34 | static var rootCacheKey: String { return "MUTATION_ROOT" }
35 | }
36 |
37 | public protocol GraphQLSubscription: GraphQLOperation {}
38 |
39 | public extension GraphQLSubscription {
40 | static var rootCacheKey: String { return "SUBSCRIPTION_ROOT" }
41 | }
42 |
43 | public protocol GraphQLFragment: GraphQLSelectionSet {
44 | static var possibleTypes: [String] { get }
45 | }
46 |
47 | public extension GraphQLOperation {
48 | static func getResponseGraphQLSelections() -> [GraphQLSelection] {
49 | return Data.selections
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/Apollo/Sources/Apollo/GraphQLQueryWatcher.swift:
--------------------------------------------------------------------------------
1 | import Dispatch
2 |
3 | /// A `GraphQLQueryWatcher` is responsible for watching the store, and calling the result handler with a new result whenever any of the data the previous result depends on changes.
4 | public final class GraphQLQueryWatcher: Cancellable, ApolloStoreSubscriber {
5 | weak var client: ApolloClient?
6 | let query: Query
7 | let handlerQueue: DispatchQueue
8 | let resultHandler: OperationResultHandler
9 |
10 | private var context = 0
11 |
12 | private weak var fetching: Cancellable?
13 |
14 | private var dependentKeys: Set?
15 |
16 | init(client: ApolloClient, query: Query, handlerQueue: DispatchQueue, resultHandler: @escaping OperationResultHandler) {
17 | self.client = client
18 | self.query = query
19 | self.handlerQueue = handlerQueue
20 | self.resultHandler = resultHandler
21 |
22 | client.store.subscribe(self)
23 | }
24 |
25 | /// Refetch a query from the server.
26 | public func refetch() {
27 | fetch(cachePolicy: .fetchIgnoringCacheData)
28 | }
29 |
30 | func fetch(cachePolicy: CachePolicy) {
31 | fetching = client?._fetch(query: query, cachePolicy: cachePolicy, context: &context, queue: handlerQueue) { (result, error) in
32 | self.dependentKeys = result?.dependentKeys
33 | self.resultHandler(result, error)
34 | }
35 | }
36 |
37 | /// Cancel any in progress fetching operations and unsubscribe from the store.
38 | public func cancel() {
39 | fetching?.cancel()
40 | client?.store.unsubscribe(self)
41 | }
42 |
43 | func store(_ store: ApolloStore, didChangeKeys changedKeys: Set, context: UnsafeMutableRawPointer?) {
44 | if context == &self.context { return }
45 |
46 | guard let dependentKeys = dependentKeys else { return }
47 |
48 | if !dependentKeys.isDisjoint(with: changedKeys) {
49 | fetch(cachePolicy: .returnCacheDataElseFetch)
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/Apollo/Sources/Apollo/GraphQLResponse.swift:
--------------------------------------------------------------------------------
1 | /// Represents a GraphQL response received from a server.
2 | public final class GraphQLResponse {
3 | public let operation: Operation
4 | public let body: JSONObject
5 |
6 | public init(operation: Operation, body: JSONObject) {
7 | self.operation = operation
8 | self.body = body
9 | }
10 |
11 | public func parseResult(cacheKeyForObject: CacheKeyForObject? = nil) throws -> Promise<(GraphQLResult, RecordSet?)> {
12 | let errors: [GraphQLError]?
13 |
14 | if let errorsEntry = body["errors"] as? [JSONObject] {
15 | errors = errorsEntry.map(GraphQLError.init)
16 | } else {
17 | errors = nil
18 | }
19 |
20 | if let dataEntry = body["data"] as? JSONObject {
21 | let executor = GraphQLExecutor { object, info in
22 | return .result(.success(object[info.responseKeyForField]))
23 | }
24 |
25 | executor.cacheKeyForObject = cacheKeyForObject
26 |
27 | let mapper = GraphQLSelectionSetMapper()
28 | let normalizer = GraphQLResultNormalizer()
29 | let dependencyTracker = GraphQLDependencyTracker()
30 |
31 | return firstly {
32 | try executor.execute(selections: Operation.Data.selections, on: dataEntry, withKey: Operation.rootCacheKey, variables: operation.variables, accumulator: zip(mapper, normalizer, dependencyTracker))
33 | }.map { (data, records, dependentKeys) in
34 | (GraphQLResult(data: data, errors: errors, source: .server, dependentKeys: dependentKeys), records)
35 | }
36 | } else {
37 | return Promise(fulfilled: (GraphQLResult(data: nil, errors: errors, source: .server, dependentKeys: nil), nil))
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/Apollo/Sources/Apollo/GraphQLResponseGenerator.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | final class GraphQLResponseGenerator: GraphQLResultAccumulator {
4 | func accept(scalar: JSONValue, info: GraphQLResolveInfo) -> JSONValue {
5 | return scalar
6 | }
7 |
8 | func acceptNullValue(info: GraphQLResolveInfo) -> JSONValue {
9 | return NSNull()
10 | }
11 |
12 | func accept(list: [JSONValue], info: GraphQLResolveInfo) -> JSONValue {
13 | return list
14 | }
15 |
16 | func accept(fieldEntry: JSONValue, info: GraphQLResolveInfo) -> (key: String, value: JSONValue) {
17 | return (info.responseKeyForField, fieldEntry)
18 | }
19 |
20 | func accept(fieldEntries: [(key: String, value: JSONValue)], info: GraphQLResolveInfo) -> JSONValue {
21 | return JSONObject(fieldEntries)
22 | }
23 |
24 | func finish(rootValue: JSONValue, info: GraphQLResolveInfo) throws -> JSONObject {
25 | return rootValue as! JSONObject
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/Apollo/Sources/Apollo/GraphQLResult.swift:
--------------------------------------------------------------------------------
1 | /// Represents the result of a GraphQL operation.
2 | public struct GraphQLResult {
3 | /// Represents source of data
4 | public enum Source {
5 | case cache
6 | case server
7 | }
8 |
9 | /// The typed result data, or `nil` if an error was encountered that prevented a valid response.
10 | public let data: Data?
11 | /// A list of errors, or `nil` if the operation completed without encountering any errors.
12 | public let errors: [GraphQLError]?
13 | /// Source of data
14 | public let source: Source
15 |
16 | let dependentKeys: Set?
17 |
18 | init(data: Data?, errors: [GraphQLError]?, source:Source, dependentKeys: Set?) {
19 | self.data = data
20 | self.errors = errors
21 | self.dependentKeys = dependentKeys
22 | self.source = source
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/Apollo/Sources/Apollo/GraphQLResultNormalizer.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | final class GraphQLResultNormalizer: GraphQLResultAccumulator {
4 | private var records: RecordSet = [:]
5 |
6 | func accept(scalar: JSONValue, info: GraphQLResolveInfo) -> JSONValue {
7 | return scalar
8 | }
9 |
10 | func acceptNullValue(info: GraphQLResolveInfo) -> JSONValue {
11 | return NSNull()
12 | }
13 |
14 | func accept(list: [JSONValue], info: GraphQLResolveInfo) -> JSONValue {
15 | return list
16 | }
17 |
18 | func accept(fieldEntry: JSONValue, info: GraphQLResolveInfo) -> (key: String, value: JSONValue) {
19 | return (info.cacheKeyForField, fieldEntry)
20 | }
21 |
22 | func accept(fieldEntries: [(key: String, value: JSONValue)], info: GraphQLResolveInfo) throws -> JSONValue {
23 | let cachePath = joined(path: info.cachePath)
24 |
25 | let object = JSONObject(fieldEntries)
26 | records.merge(record: Record(key: cachePath, object))
27 |
28 | return Reference(key: cachePath)
29 | }
30 |
31 | func finish(rootValue: JSONValue, info: GraphQLResolveInfo) throws -> RecordSet {
32 | return records
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/Apollo/Sources/Apollo/GraphQLSelectionSetMapper.swift:
--------------------------------------------------------------------------------
1 | final class GraphQLSelectionSetMapper: GraphQLResultAccumulator {
2 | func accept(scalar: JSONValue, info: GraphQLResolveInfo) throws -> Any? {
3 | guard case .scalar(let decodable) = info.fields[0].type.namedType else { preconditionFailure() }
4 | // This will convert a JSON value to the expected value type, which could be a custom scalar or an enum.
5 | return try decodable.init(jsonValue: scalar)
6 | }
7 |
8 | func acceptNullValue(info: GraphQLResolveInfo) -> Any? {
9 | return nil
10 | }
11 |
12 | func accept(list: [Any?], info: GraphQLResolveInfo) -> Any? {
13 | return list
14 | }
15 |
16 | func accept(fieldEntry: Any?, info: GraphQLResolveInfo) -> (key: String, value: Any?) {
17 | return (info.responseKeyForField, fieldEntry)
18 | }
19 |
20 | func accept(fieldEntries: [(key: String, value: Any?)], info: GraphQLResolveInfo) throws -> Snapshot {
21 | return Snapshot(fieldEntries)
22 | }
23 |
24 | func finish(rootValue: Snapshot, info: GraphQLResolveInfo) -> SelectionSet {
25 | return SelectionSet.init(snapshot: rootValue)
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/Apollo/Sources/Apollo/InMemoryNormalizedCache.swift:
--------------------------------------------------------------------------------
1 | public final class InMemoryNormalizedCache: NormalizedCache {
2 | private var records: RecordSet
3 |
4 | public init(records: RecordSet = RecordSet()) {
5 | if records.isEmpty {
6 | self.records = InMemoryNormalizedCache.emptyQueryRootRecords()
7 | } else {
8 | self.records = records
9 | }
10 | }
11 |
12 | public func loadRecords(forKeys keys: [CacheKey]) -> Promise<[Record?]> {
13 | let records = keys.map { self.records[$0] }
14 | return Promise(fulfilled: records)
15 | }
16 |
17 | public func merge(records: RecordSet) -> Promise> {
18 | return Promise(fulfilled: self.records.merge(records: records))
19 | }
20 |
21 | public func clear() -> Promise {
22 | records.clear()
23 | self.records = InMemoryNormalizedCache.emptyQueryRootRecords()
24 | return Promise(fulfilled: ())
25 | }
26 |
27 | private static func emptyQueryRootRecords() -> RecordSet {
28 | // Prepopulate the InMemoryNormalizedCache record set with an empty QUERY_ROOT, to allow optimistic
29 | // updates against empty caches to succeed. Otherwise, such an operation will fail with a "missingValue"
30 | // error (#92)
31 | let emptyQueryRootRecord = Record(key: AWSAppSyncClient.EmptyQuery.rootCacheKey, [:])
32 | return RecordSet(records: [emptyQueryRootRecord])
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/Apollo/Sources/Apollo/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | $(CURRENT_PROJECT_VERSION)
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(CURRENT_PROJECT_VERSION)
23 | NSPrincipalClass
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/Apollo/Sources/Apollo/JSON.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | public typealias JSONValue = Any
4 |
5 | public typealias JSONObject = [String: JSONValue]
6 |
7 | public protocol JSONDecodable {
8 | init(jsonValue value: JSONValue) throws
9 | }
10 |
11 | public protocol JSONEncodable: GraphQLInputValue {
12 | var jsonValue: JSONValue { get }
13 | }
14 |
15 | public enum JSONDecodingError: Error, LocalizedError {
16 | case missingValue
17 | case nullValue
18 | case wrongType
19 | case couldNotConvert(value: Any, to: Any.Type)
20 |
21 | public var errorDescription: String? {
22 | switch self {
23 | case .missingValue:
24 | return "Missing value"
25 | case .nullValue:
26 | return "Unexpected null value"
27 | case .wrongType:
28 | return "Wrong type"
29 | case .couldNotConvert(let value, let expectedType):
30 | return "Could not convert \"\(value)\" to \(expectedType)"
31 | }
32 | }
33 | }
34 |
35 | extension JSONDecodingError: Matchable {
36 | public typealias Base = Error
37 | public static func ~=(pattern: JSONDecodingError, value: Error) -> Bool {
38 | guard let value = value as? JSONDecodingError else {
39 | return false
40 | }
41 |
42 | switch (value, pattern) {
43 | case (.missingValue, .missingValue), (.nullValue, .nullValue), (.couldNotConvert, .couldNotConvert):
44 | return true
45 | default:
46 | return false
47 | }
48 | }
49 | }
50 |
51 | // MARK: Helpers
52 |
53 | func equals(_ lhs: Any, _ rhs: Any) -> Bool {
54 | if let lhs = lhs as? Reference, let rhs = rhs as? Reference {
55 | return lhs == rhs
56 | }
57 |
58 | let lhs = lhs as AnyObject, rhs = rhs as AnyObject
59 | return lhs.isEqual(rhs)
60 | }
61 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/Apollo/Sources/Apollo/JSONSerializationFormat.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | public final class JSONSerializationFormat {
4 | public class func serialize(value: JSONEncodable) throws -> Data {
5 | return try JSONSerialization.data(withJSONObject: value.jsonValue, options: [])
6 | }
7 |
8 | public class func deserialize(data: Data) throws -> JSONValue {
9 | return try JSONSerialization.jsonObject(with: data, options: [])
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/Apollo/Sources/Apollo/Locking.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | final class Mutex {
4 | private var _lock = pthread_mutex_t()
5 |
6 | init() {
7 | let result = pthread_mutex_init(&_lock, nil)
8 | assert(result == 0)
9 | }
10 |
11 | deinit {
12 | let result = pthread_mutex_destroy(&_lock)
13 | assert(result == 0)
14 | }
15 |
16 | func lock() {
17 | let result = pthread_mutex_lock(&_lock)
18 | assert(result == 0)
19 | }
20 |
21 | func unlock() {
22 | let result = pthread_mutex_unlock(&_lock)
23 | assert(result == 0)
24 | }
25 |
26 | func withLock(_ body: () throws -> T) rethrows -> T {
27 | lock()
28 | defer { unlock() }
29 | return try body()
30 | }
31 | }
32 |
33 | final class ReadWriteLock {
34 | private var _lock = pthread_rwlock_t()
35 |
36 | init() {
37 | let status = pthread_rwlock_init(&_lock, nil)
38 | assert(status == 0)
39 | }
40 |
41 | deinit {
42 | let status = pthread_rwlock_destroy(&_lock)
43 | assert(status == 0)
44 | }
45 |
46 | func lockForReading() {
47 | pthread_rwlock_rdlock(&_lock)
48 | }
49 |
50 | func lockForWriting() {
51 | pthread_rwlock_wrlock(&_lock)
52 | }
53 |
54 | func unlock() {
55 | pthread_rwlock_unlock(&_lock)
56 | }
57 |
58 | func withReadLock(_ body: () throws -> T) rethrows -> T {
59 | lockForReading()
60 | defer { unlock() }
61 | return try body()
62 | }
63 |
64 | func withWriteLock(_ body: () throws -> T) rethrows -> T {
65 | lockForWriting()
66 | defer { unlock() }
67 | return try body()
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/Apollo/Sources/Apollo/NetworkTransport.swift:
--------------------------------------------------------------------------------
1 | /// A network transport is responsible for sending GraphQL operations to a server.
2 | public protocol NetworkTransport {
3 | /// Send a GraphQL operation to a server and return a response.
4 | ///
5 | /// - Parameters:
6 | /// - operation: The operation to send.
7 | /// - completionHandler: A closure to call when a request completes.
8 | /// - response: The response received from the server, or `nil` if an error occurred.
9 | /// - error: An error that indicates why a request failed, or `nil` if the request was succesful.
10 | /// - Returns: An object that can be used to cancel an in progress request.
11 | func send(operation: Operation, completionHandler: @escaping (_ response: GraphQLResponse?, _ error: Error?) -> Void) -> Cancellable
12 | }
13 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/Apollo/Sources/Apollo/NormalizedCache.swift:
--------------------------------------------------------------------------------
1 | public protocol NormalizedCache {
2 |
3 | /// Loads records corresponding to the given keys.
4 | /// - returns: A promise that fulfills with an array, with each index containing either the
5 | /// record corresponding to the key at that index or nil if not found.
6 | func loadRecords(forKeys keys: [CacheKey]) -> Promise<[Record?]>
7 |
8 | /// Merges a set of records into the cache.
9 | /// - returns: A promise that fulfills with a set of keys corresponding to *fields* that have
10 | /// changed (i.e. QUERY_ROOT.Foo.myField). These are the same type of keys as are
11 | /// returned by RecordSet.merge(records:).
12 | func merge(records: RecordSet) -> Promise>
13 |
14 | // Clears all records
15 | func clear() -> Promise
16 | }
17 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/Apollo/Sources/Apollo/Record.swift:
--------------------------------------------------------------------------------
1 | /// A cache key for a record.
2 | public typealias CacheKey = String
3 |
4 | /// A cache record.
5 | public struct Record {
6 | public let key: CacheKey
7 |
8 | public typealias Value = Any
9 | public typealias Fields = [CacheKey: Value]
10 | public private(set) var fields: Fields
11 |
12 | public init(key: CacheKey, _ fields: Fields = [:]) {
13 | self.key = key
14 | self.fields = fields
15 | }
16 |
17 | public subscript(key: CacheKey) -> Value? {
18 | get {
19 | return fields[key]
20 | }
21 | set {
22 | fields[key] = newValue
23 | }
24 | }
25 | }
26 |
27 | extension Record: CustomStringConvertible {
28 | public var description: String {
29 | return "#\(key) -> \(fields)"
30 | }
31 | }
32 |
33 | /// A reference to a cache record.
34 | public struct Reference {
35 | public let key: CacheKey
36 |
37 | public init(key: CacheKey) {
38 | self.key = key
39 | }
40 | }
41 |
42 | extension Reference: Equatable {
43 | public static func ==(lhs: Reference, rhs: Reference) -> Bool {
44 | return lhs.key == rhs.key
45 | }
46 | }
47 |
48 | extension Reference: CustomStringConvertible {
49 | public var description: String {
50 | return "-> #\(key)"
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/Apollo/Sources/Apollo/RecordSet.swift:
--------------------------------------------------------------------------------
1 | /// A set of cache records.
2 | public struct RecordSet {
3 | public private(set) var storage: [CacheKey: Record] = [:]
4 |
5 | public init(records: S) where S.Iterator.Element == Record {
6 | insert(contentsOf: records)
7 | }
8 |
9 | public mutating func insert(_ record: Record) {
10 | storage[record.key] = record
11 | }
12 |
13 | public mutating func clear() {
14 | storage.removeAll()
15 | }
16 |
17 | public mutating func insert(contentsOf records: S) where S.Iterator.Element == Record {
18 | for record in records {
19 | insert(record)
20 | }
21 | }
22 |
23 | public subscript(key: CacheKey) -> Record? {
24 | return storage[key]
25 | }
26 |
27 | public var isEmpty: Bool {
28 | return storage.isEmpty
29 | }
30 |
31 | public var keys: [CacheKey] {
32 | return Array(storage.keys)
33 | }
34 |
35 | @discardableResult public mutating func merge(records: RecordSet) -> Set {
36 | var changedKeys: Set = Set()
37 |
38 | for (_, record) in records.storage {
39 | changedKeys.formUnion(merge(record: record))
40 | }
41 |
42 | return changedKeys
43 | }
44 |
45 | @discardableResult public mutating func merge(record: Record) -> Set {
46 | if var oldRecord = storage.removeValue(forKey: record.key) {
47 | var changedKeys: Set = Set()
48 |
49 | for (key, value) in record.fields {
50 | if let oldValue = oldRecord.fields[key], equals(oldValue, value) {
51 | continue
52 | }
53 | oldRecord[key] = value
54 | changedKeys.insert([record.key, key].joined(separator: "."))
55 | }
56 | storage[record.key] = oldRecord
57 | return changedKeys
58 | } else {
59 | storage[record.key] = record
60 | return Set(record.fields.keys.map { [record.key, $0].joined(separator: ".") })
61 | }
62 | }
63 | }
64 |
65 | extension RecordSet: ExpressibleByDictionaryLiteral {
66 | public init(dictionaryLiteral elements: (CacheKey, Record.Fields)...) {
67 | self.init(records: elements.map { Record(key: $0.0, $0.1) })
68 | }
69 | }
70 |
71 | extension RecordSet: CustomStringConvertible {
72 | public var description: String {
73 | return String(describing: Array(storage.values))
74 | }
75 | }
76 |
77 | extension RecordSet: CustomPlaygroundDisplayConvertible {
78 | public var playgroundDescription: Any {
79 | return description
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/Apollo/Sources/Apollo/Result.swift:
--------------------------------------------------------------------------------
1 | public enum Result {
2 | case success(Value)
3 | case failure(Error)
4 | }
5 |
6 | extension Result: CustomStringConvertible {
7 | public var description: String {
8 | switch self {
9 | case .success(let value):
10 | return "Success(\(value))"
11 | case .failure(let error):
12 | return "Error(\(error))"
13 | }
14 | }
15 | }
16 |
17 | extension Result {
18 | var value: Value? {
19 | switch self {
20 | case .success(let value):
21 | return value
22 | case .failure(_):
23 | return nil
24 | }
25 | }
26 |
27 | var error: Error? {
28 | switch self {
29 | case .success(_):
30 | return nil
31 | case .failure(let error):
32 | return error
33 | }
34 | }
35 |
36 | func valueOrError() throws -> Value {
37 | switch self {
38 | case .success(let value):
39 | return value
40 | case .failure(let error):
41 | throw error
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/Apollo/Sources/Apollo/Utilities.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | extension HTTPURLResponse {
4 | @objc var isSuccessful: Bool {
5 | return (200..<300).contains(statusCode)
6 | }
7 |
8 | @objc var statusCodeDescription: String {
9 | return HTTPURLResponse.localizedString(forStatusCode: statusCode)
10 | }
11 |
12 | var textEncoding: String.Encoding? {
13 | guard let encodingName = textEncodingName else { return nil }
14 |
15 | return String.Encoding(rawValue: CFStringConvertEncodingToNSStringEncoding(CFStringConvertIANACharSetNameToEncoding(encodingName as CFString)))
16 | }
17 | }
18 |
19 | public protocol Matchable {
20 | associatedtype Base
21 | static func ~=(pattern: Self, value: Base) -> Bool
22 | }
23 |
24 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 3.7.1
19 | CFBundleVersion
20 | $(CURRENT_PROJECT_VERSION)
21 | NSPrincipalClass
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/Internal/AWSAppSyncSubscriptionMetadataCache.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | import Foundation
8 | import SQLite
9 | import struct SQLite.Expression
10 |
11 | final class AWSSubscriptionMetaDataCache {
12 |
13 | private let db: Connection
14 | private let subscriptionMetadataRecords = Table("subscription_metadata")
15 | private let operationHash = Expression("operationHash")
16 | private let lastSyncDate = Expression("lastSyncDate")
17 |
18 | init(fileURL: URL) throws {
19 | AppSyncLog.verbose("Initializing subscription metadata cache at \(fileURL.absoluteString)")
20 | db = try Connection(.uri(fileURL.absoluteString), readonly: false)
21 | db.busyTimeout = sqlBusyTimeoutConstant
22 | try createTableIfNeeded()
23 | }
24 |
25 | private func createTableIfNeeded() throws {
26 | try db.run(subscriptionMetadataRecords.create(ifNotExists: true) { table in
27 | table.column(operationHash, primaryKey: true)
28 | table.column(lastSyncDate)
29 | })
30 | }
31 |
32 | internal func updateLastSyncTime(for operationHash: String, with lastSyncTime: Date) throws {
33 | let sqlRecord = subscriptionMetadataRecords.filter(self.operationHash == operationHash)
34 | let recordCount = try db.scalar(sqlRecord.count)
35 |
36 | guard recordCount == 0 else {
37 | try db.run(sqlRecord.update(self.operationHash <- operationHash, self.lastSyncDate <- lastSyncTime))
38 | return
39 | }
40 |
41 | let insert = subscriptionMetadataRecords.insert(self.lastSyncDate <- lastSyncTime,
42 | self.operationHash <- operationHash)
43 | try db.run(insert)
44 | }
45 |
46 | internal func getLastSyncTime(operationHash: String) throws -> Date? {
47 | let sqlRecord = subscriptionMetadataRecords.filter(self.operationHash == operationHash)
48 | let recordCount = try db.scalar(sqlRecord.count)
49 |
50 | guard recordCount != 0 else {
51 | return nil
52 | }
53 |
54 | var syncDate: Date?
55 | for record in try db.prepare(sqlRecord) {
56 | syncDate = try record.get(lastSyncDate)
57 | }
58 | return syncDate
59 | }
60 |
61 | internal func clear() throws {
62 | try db.run("DELETE FROM subscription_metadata")
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/Internal/AWSConfigurationFile.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | import Foundation
8 |
9 | /// Describes the shape of the 'AppSync' section of the `awsconfiguration.json` file
10 | struct AWSConfigurationFile {
11 | static let fileName = "awsconfiguration.json"
12 |
13 | struct Keys {
14 | /// The root of the AppSync configuration section
15 | static let root = "AppSync"
16 |
17 | /// The endpoint URL for the AppSync API described by this configuration
18 | static let apiURL = "ApiUrl"
19 |
20 | /// The AWS region of the AppSync API. The value must be a string that can be resolved to an `AWSRegionType`
21 | static let region = "Region"
22 |
23 | /// The Auth mode of the API. The value must be a string that is a raw value of the `AWSAppSyncAuthType` enum
24 | static let authMode = "AuthMode"
25 |
26 | /// If the `authMode` value is "API_KEY", this value should be filled in with a valid value. If not, then the AppSync
27 | /// constructors must be provided with an already-configured apiKeyProvider.
28 | static let apiKey = "ApiKey"
29 |
30 | /// This prefix is used to partition the caches and on-disk stores used by the client. Changing this value will
31 | /// orphan resources created by previous instances of the client.
32 | static let clientDatabasePrefix = "ClientDatabasePrefix"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/Internal/AWSMutationRetryAdviceHelper.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | import Foundation
8 |
9 | final class AWSMutationRetryAdviceHelper {
10 |
11 | /// This method is a special retry evaluator currently only used for mutations.It is responsible to identify if the error is caused due to internet
12 | /// not being available or appsync hosts not reachable from the client. It evaluates it by checking for error codes in NSURLErrorDomain.
13 | /// We have an additional HTTP layer retry handleer which is responsible to parse and retry errors which occur at HTTP layer(5XX, 429 status codes.)
14 | /// We would ideally want to have a single retry layer which can account for these multiple use-cases and suggest retry advice.
15 | /// See [PR #223](https://github.com/awslabs/aws-mobile-appsync-sdk-ios/pull/233) for more details on the discussion.
16 | ///
17 | /// - Parameter error: error being evaluated
18 | /// - Returns: true if the error is retriable.
19 | static func isRetriableNetworkError(error: Error) -> Bool {
20 | if let appsyncError = error as? AWSAppSyncClientError {
21 | switch appsyncError {
22 | case .authenticationError(let authError):
23 | // We are currently checking for this error due to IAM auth.
24 | // If Cognito Identity SDK does not have an identity id available,
25 | // It tries to get one before giving the callback to appsync SDK.
26 | // If Cognito Identity SDK cannot reach the service to fetch identityd id,
27 | // it will propogate the error it encoutered to AppSync. We specifically
28 | // check if the error is of type internet not available and then retry.
29 | return isErrorURLDomainError(error: authError)
30 | case .requestFailed(_, _, let urlError):
31 | if let urlError = urlError {
32 | return isErrorURLDomainError(error: urlError)
33 | }
34 | default:
35 | break
36 | }
37 | } else {
38 | return isErrorURLDomainError(error: error)
39 | }
40 | return false
41 | }
42 |
43 | /// We evaluate the error against known error codes which could result due to unavailable internet or spotty network connection.
44 | private static func isErrorURLDomainError(error: Error) -> Bool {
45 | let nsError = error as NSError
46 | guard nsError.domain == NSURLErrorDomain else {
47 | return false
48 | }
49 | switch nsError.code {
50 | case NSURLErrorNotConnectedToInternet,
51 | NSURLErrorDNSLookupFailed,
52 | NSURLErrorCannotConnectToHost,
53 | NSURLErrorCannotFindHost,
54 | NSURLErrorTimedOut:
55 | return true
56 | default:
57 | break
58 | }
59 | return false
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/Internal/AWSMutationState.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | import Foundation
8 |
9 | /// Determines the next step in a mutation operation.
10 | ///
11 | /// - unknown: when the next step of mutation is not determined yet
12 | /// - s3Upload: the mutation is required to do a s3 upload before the graphql call
13 | /// - graphqlOperation: the mutation to complete needs to make a graphql call
14 | enum MutationState {
15 | case unknown, s3Upload, graphqlOperation
16 | }
17 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/Internal/AWSOfflineMutation.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | import Foundation
8 |
9 | final class AWSAppSyncMutationRecord {
10 | var jsonRecord: JSONObject?
11 | var data: Data?
12 | var contentMap: GraphQLMap?
13 | var recordIdentifier: String
14 | var recordState: MutationRecordState = .inQueue
15 | var timestamp: Date
16 | var type: MutationType
17 | var s3ObjectInput: InternalS3ObjectDetails?
18 | var operationString: String?
19 |
20 | init(
21 | recordIdentifier: String = UUID().uuidString,
22 | timestamp: Date = Date(),
23 | type: MutationType = .graphQLMutation) {
24 | self.recordIdentifier = recordIdentifier
25 | self.timestamp = timestamp
26 | self.type = type
27 | }
28 | }
29 |
30 | // MARK: - CustomStringConvertible
31 |
32 | extension AWSAppSyncMutationRecord: CustomStringConvertible {
33 |
34 | var description: String {
35 | var desc: String = "<\(self):\(recordIdentifier)"
36 | desc.append("\tID: \(recordIdentifier)")
37 | desc.append("\ttimestamp: \(timestamp)")
38 | desc.append("\thasS3Object: \(s3ObjectInput != nil ? true : false)")
39 | desc.append(">")
40 |
41 | return desc
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/Internal/AWSRequestBuilder.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | import Foundation
8 |
9 | final class AWSRequestBuilder {
10 |
11 | /// Given a `GraphQLMap` (e.g., parameters to a mutation, or an input type for a mutation), inspects the graph
12 | /// to find a set of variables that can be cast to an S3InputObject. This currently only supports one S3Object per
13 | /// GraphQLMap. The behavior of maps containing multiple S3 objects is undefined.
14 | static func s3Object(from variables: GraphQLMap?) -> InternalS3ObjectDetails? {
15 | guard let variables = variables else {
16 | return nil
17 | }
18 |
19 | var builder = InternalS3ObjectDetailsBuilder()
20 |
21 | for (key, value) in variables {
22 | guard let value = value else {
23 | continue
24 | }
25 |
26 | if let nestedMap = value as? GraphQLMapConvertible {
27 | if let s3Object = s3Object(from: nestedMap.graphQLMap) {
28 | return s3Object
29 | }
30 | }
31 |
32 | builder.offer(key: key, value: value)
33 | }
34 |
35 | return builder.build()
36 | }
37 |
38 | static func requestBody(
39 | from operation: Operation) -> GraphQLMap {
40 | return [
41 | "query": type(of: operation).requestString,
42 | "variables": operation.variables]
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/Internal/AppSyncLogHelper.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | import Foundation
8 | import AWSCore
9 | import AppSyncRealTimeClient
10 |
11 | struct AppSyncLogHelper {
12 |
13 | public static func shouldLog(flag: AWSDDLogFlag) -> Bool {
14 | let shouldLog = flag.rawValue & AWSDDLog.sharedInstance.logLevel.rawValue
15 | return shouldLog != 0
16 | }
17 |
18 | public static func log(_ message: String,
19 | flag: AWSDDLogFlag,
20 | file: String,
21 | function: String,
22 | line: UInt) {
23 | AWSDDLog.log(asynchronous: true,
24 | level: AWSDDLog.sharedInstance.logLevel,
25 | flag: flag,
26 | context: 0,
27 | file: file.cString(using: .utf8)!,
28 | function: function.cString(using: .utf8)!,
29 | line: line,
30 | tag: nil,
31 | format: message,
32 | arguments: getVaList([]))
33 | }
34 |
35 | static var subscriptionLogLevel: AppSyncRealTimeClient.LogLevel {
36 | switch AWSDDLog.sharedInstance.logLevel {
37 | case .off, .error:
38 | return .error
39 | case .warning:
40 | return .warn
41 | case .info:
42 | return .info
43 | case .debug:
44 | return .debug
45 | case .verbose, .all:
46 | return .verbose
47 | @unknown default:
48 | return .error
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/Internal/AppSyncLogWrapper.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 | import AWSCore
7 |
8 | final class AppSyncLog {
9 | class func verbose(_ message: @autoclosure () -> String, file: String = #file, function: String = #function, line: Int = #line) {
10 | log(message(), flag: .verbose, file: file, function: function, line: line)
11 | }
12 |
13 | class func debug(_ message: @autoclosure () -> String, file: String = #file, function: String = #function, line: Int = #line) {
14 | log(message(), flag: .debug, file: file, function: function, line: line)
15 | }
16 |
17 | class func info(_ message: @autoclosure () -> String, file: String = #file, function: String = #function, line: Int = #line) {
18 | log(message(), flag: .info, file: file, function: function, line: line)
19 | }
20 |
21 | class func warn(_ message: @autoclosure () -> String, file: String = #file, function: String = #function, line: Int = #line) {
22 | log(message(), flag: .warning, file: file, function: function, line: line)
23 | }
24 |
25 | class func error(_ message: @autoclosure () -> String, file: String = #file, function: String = #function, line: Int = #line) {
26 | log(message(), flag: .error, file: file, function: function, line: line)
27 | }
28 |
29 | class func error(_ error: Error, file: String = #file, function: String = #function, line: Int = #line) {
30 | log(error.localizedDescription, flag: .error, file: file, function: function, line: line)
31 | }
32 |
33 | private class func log(_ message: @autoclosure () -> String, flag: AWSDDLogFlag, file: String, function: String, line: Int) {
34 | if AppSyncLogHelper.shouldLog(flag: flag) {
35 | AppSyncLogHelper.log(message(),
36 | flag: flag,
37 | file: file,
38 | function: function,
39 | line: UInt(line))
40 | }
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/Internal/BasicAWSAPIKeyAuthProvider.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | class BasicAWSAPIKeyAuthProvider: AWSAPIKeyAuthProvider {
8 | var apiKey: String
9 |
10 | init(key: String) {
11 | apiKey = key
12 | }
13 |
14 | func getAPIKey() -> String {
15 | return apiKey
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/Internal/Foundation+Utils.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | import Foundation
8 |
9 | /// Allows use of `isEmpty` on optional `Collection`s:
10 | /// let optionalString: String? = getSomeOptionalString()
11 | /// guard optionalString.isEmpty else { return }
12 | ///
13 | /// `Collection` provides the `isEmpty` property to declare whether an instance has any members. But it’s also pretty common to
14 | /// expand the definition of “empty” to include nil. Unfortunately, the standard library doesn't include an extension mapping
15 | /// the Collection.isEmpty property, so testing Optional collections means you have to unwrap:
16 | ///
17 | /// var optionalString: String?
18 | /// // Do some work
19 | /// if let s = optionalString where s != "" {
20 | /// // s is not empty or nil
21 | /// }
22 | ///
23 | /// Or slightly more succinctly, use the nil coalescing operator “??”:
24 | ///
25 | /// if !(optionalString ?? "").isEmpty {
26 | /// // optionalString is not empty or nil
27 | /// }
28 | ///
29 | /// This extension simply unwraps the `Optional` and returns the value of `isEmpty` for non-nil collections, and returns `true`
30 | /// if the collection is nil.
31 | extension Optional where Wrapped: Collection {
32 | /// Returns `true` for nil values, or `value.isEmpty` for non-nil values.
33 | var isEmpty: Bool {
34 | switch self {
35 | case .some(let val):
36 | return val.isEmpty
37 | case .none:
38 | return true
39 | }
40 | }
41 | }
42 |
43 | extension DispatchSource {
44 | /// Convenience function to encapsulate creation of a one-off DispatchSourceTimer for different versions of Swift
45 | ///
46 | /// - Parameters:
47 | /// - interval: The future DispatchInterval at which to fire the timer
48 | /// - queue: The queue on which the timer should perform its block
49 | /// - block: The block to invoke when the timer is fired
50 | /// - Returns: The unstarted timer
51 | static func makeOneOffDispatchSourceTimer(interval: DispatchTimeInterval, queue: DispatchQueue, block: @escaping () -> Void ) -> DispatchSourceTimer {
52 | let deadline = DispatchTime.now() + interval
53 | return makeOneOffDispatchSourceTimer(deadline: deadline, queue: queue, block: block)
54 | }
55 |
56 | /// Convenience function to encapsulate creation of a one-off DispatchSourceTimer for different versions of Swift
57 | /// - Parameters:
58 | /// - deadline: The time to fire the timer
59 | /// - queue: The queue on which the timer should perform its block
60 | /// - block: The block to invoke when the timer is fired
61 | static func makeOneOffDispatchSourceTimer(deadline: DispatchTime, queue: DispatchQueue, block: @escaping () -> Void ) -> DispatchSourceTimer {
62 | let timer = DispatchSource.makeTimerSource(flags: DispatchSource.TimerFlags(rawValue: 0), queue: queue)
63 | #if swift(>=4)
64 | timer.schedule(deadline: deadline)
65 | #else
66 | timer.scheduleOneshot(deadline: deadline)
67 | #endif
68 | timer.setEventHandler(handler: block)
69 | return timer
70 | }
71 |
72 | }
73 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/Internal/SQLiteSerialization.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | import Foundation
8 | import SQLite
9 |
10 | final class SQLiteSerialization {
11 | private static let serializedReferenceKey = "$reference"
12 |
13 | static func serialize(fields: Record.Fields) throws -> Data {
14 | var objectToSerialize = JSONObject()
15 | for (key, value) in fields {
16 | objectToSerialize[key] = try serialize(fieldValue: value)
17 | }
18 | return try JSONSerialization.data(withJSONObject: objectToSerialize, options: [])
19 | }
20 |
21 | private static func serialize(fieldValue: Record.Value) throws -> JSONValue {
22 | switch fieldValue {
23 | case let reference as Reference:
24 | return [serializedReferenceKey: reference.key]
25 | case let array as [Record.Value]:
26 | return try array.map { try serialize(fieldValue: $0) }
27 | default:
28 | return fieldValue
29 | }
30 | }
31 |
32 | static func deserialize(data: Data) throws -> Record.Fields {
33 | let object = try JSONSerialization.jsonObject(with: data, options: [])
34 | guard let jsonObject = object as? JSONObject else {
35 | throw AWSAppSyncQueriesCacheError.invalidRecordShape(object: object)
36 | }
37 | var fields = Record.Fields()
38 | for (key, value) in jsonObject {
39 | fields[key] = try deserialize(fieldJSONValue: value)
40 | }
41 | return fields
42 | }
43 |
44 | private static func deserialize(fieldJSONValue: JSONValue) throws -> Record.Value {
45 | switch fieldJSONValue {
46 | case let dictionary as JSONObject:
47 | guard let reference = dictionary[serializedReferenceKey] as? String else {
48 | return fieldJSONValue
49 | }
50 | return Reference(key: reference)
51 | case let array as [JSONValue]:
52 | return try array.map { try deserialize(fieldJSONValue: $0) }
53 | default:
54 | return fieldJSONValue
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/Internal/SubscriptionFactory/APIKeyBasedConnectionPool.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | import Foundation
8 | import AppSyncRealTimeClient
9 |
10 | class APIKeyBasedConnectionPool: SubscriptionConnectionPool {
11 |
12 | private let apiKeyProvider: AWSAPIKeyAuthProvider
13 | var endPointToProvider: [String: ConnectionProvider]
14 |
15 | private let queue = DispatchQueue(
16 | label: "com.amazonaws.connectionPool.APIKeyBased.concurrentQueue",
17 | attributes: .concurrent,
18 | target: .global(
19 | qos: .userInitiated
20 | )
21 | )
22 |
23 | init(_ apiKeyProvider: AWSAPIKeyAuthProvider) {
24 | self.apiKeyProvider = apiKeyProvider
25 | self.endPointToProvider = [:]
26 | }
27 |
28 | func connection(for url: URL, connectionType: SubscriptionConnectionType) -> SubscriptionConnection {
29 | queue.sync(flags: .barrier) {
30 | let connectionProvider = endPointToProvider[url.absoluteString] ??
31 | ConnectionProviderFactory.createConnectionProvider(for: URLRequest(url: url),
32 | authInterceptor: APIKeyAuthInterceptor(apiKeyProvider.getAPIKey()),
33 | connectionType: connectionType)
34 | endPointToProvider[url.absoluteString] = connectionProvider
35 | let connection = AppSyncSubscriptionConnection(provider: connectionProvider)
36 | return connection
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/Internal/SubscriptionFactory/AppSyncRealTimeClientOIDCAuthProvider.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | import Foundation
8 | import AppSyncRealTimeClient
9 |
10 | class AppSyncRealTimeClientOIDCAuthProvider: OIDCAuthProvider {
11 |
12 | let authProvider: AWSOIDCAuthProvider
13 | init(authProvider: AWSOIDCAuthProvider) {
14 | self.authProvider = authProvider
15 | }
16 |
17 | func getLatestAuthToken() -> Swift.Result {
18 | var jwtToken: String?
19 | var authError: Error?
20 |
21 | if let asyncAuthProvider = authProvider as? AWSCognitoUserPoolsAuthProviderAsync {
22 | let semaphore = DispatchSemaphore(value: 0)
23 | asyncAuthProvider.getLatestAuthToken { (token, error) in
24 | jwtToken = token
25 | authError = error
26 | semaphore.signal()
27 | }
28 | semaphore.wait()
29 |
30 | if let error = authError {
31 | return .failure(error)
32 | }
33 |
34 | if let token = jwtToken {
35 | return .success(token)
36 | }
37 | }
38 |
39 | if let asyncAuthProvider = authProvider as? AWSOIDCAuthProviderAsync {
40 | let semaphore = DispatchSemaphore(value: 0)
41 | asyncAuthProvider.getLatestAuthToken { (token, error) in
42 | jwtToken = token
43 | authError = error
44 | semaphore.signal()
45 | }
46 | semaphore.wait()
47 | if let error = authError {
48 | return .failure(error)
49 | }
50 |
51 | if let token = jwtToken {
52 | return .success(token)
53 | }
54 | }
55 |
56 | return .success(authProvider.getLatestAuthToken())
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/Internal/SubscriptionFactory/IAMBasedConnectionPool.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | import Foundation
8 | import AppSyncRealTimeClient
9 | import AWSCore
10 |
11 | class IAMBasedConnectionPool: SubscriptionConnectionPool {
12 |
13 | private let credentialProvider: AWSCredentialsProvider
14 | private let regionType: AWSRegionType
15 | var endPointToProvider: [String: ConnectionProvider]
16 |
17 | private let queue = DispatchQueue(label: "com.amazonaws.connectionPool.IAMBased.concurrentQueue",
18 | attributes: .concurrent,
19 | target: .global(qos: .userInitiated))
20 |
21 | init(_ credentialProvider: AWSCredentialsProvider, region: AWSRegionType) {
22 | self.credentialProvider = credentialProvider
23 | self.regionType = region
24 | self.endPointToProvider = [:]
25 | }
26 |
27 | func connection(for url: URL, connectionType: SubscriptionConnectionType) -> SubscriptionConnection {
28 | queue.sync(flags: .barrier) {
29 | let connectionProvider = endPointToProvider[url.absoluteString] ??
30 | ConnectionProviderFactory.createConnectionProvider(for: URLRequest(url: url),
31 | authInterceptor: IAMAuthInterceptor(credentialProvider,
32 | region: regionType),
33 | connectionType: connectionType)
34 |
35 | endPointToProvider[url.absoluteString] = connectionProvider
36 | let connection = AppSyncSubscriptionConnection(provider: connectionProvider)
37 | return connection
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/Internal/SubscriptionFactory/LambdaBasedConnectionPool.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | import Foundation
8 | import AppSyncRealTimeClient
9 |
10 | class LambdaBasedConnectionPool: SubscriptionConnectionPool {
11 |
12 | private let tokenProvider: AWSLambdaAuthProvider
13 | var endPointToProvider: [String: ConnectionProvider]
14 |
15 | init(_ tokenProvider: AWSLambdaAuthProvider) {
16 | self.tokenProvider = tokenProvider
17 | self.endPointToProvider = [:]
18 | }
19 |
20 | func connection(for url: URL, connectionType: SubscriptionConnectionType) -> SubscriptionConnection {
21 | if let connectionProvider = endPointToProvider[url.absoluteString] {
22 | return AppSyncSubscriptionConnection(provider: connectionProvider)
23 | }
24 |
25 | let authInterceptor = LambdaAuthInterceptor(authTokenProvider: tokenProvider)
26 | let connectionProvider = ConnectionProviderFactory.createConnectionProvider(for: URLRequest(url: url),
27 | authInterceptor: authInterceptor,
28 | connectionType: connectionType)
29 | endPointToProvider[url.absoluteString] = connectionProvider
30 |
31 | return AppSyncSubscriptionConnection(provider: connectionProvider)
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/Internal/SubscriptionFactory/OIDCBasedConnectionPool.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | import Foundation
8 | import AppSyncRealTimeClient
9 |
10 | class OIDCBasedConnectionPool: SubscriptionConnectionPool {
11 |
12 | private let tokenProvider: AWSOIDCAuthProvider
13 | var endPointToProvider: [String: ConnectionProvider]
14 |
15 | init(_ tokenProvider: AWSOIDCAuthProvider) {
16 | self.tokenProvider = tokenProvider
17 | self.endPointToProvider = [:]
18 | }
19 |
20 | func connection(for url: URL, connectionType: SubscriptionConnectionType) -> SubscriptionConnection {
21 | if let connectionProvider = endPointToProvider[url.absoluteString] {
22 | return AppSyncSubscriptionConnection(provider: connectionProvider)
23 | }
24 |
25 | let authProvider = AppSyncRealTimeClientOIDCAuthProvider(authProvider: tokenProvider)
26 | let authInterceptor = OIDCAuthInterceptor(authProvider)
27 | let connectionProvider = ConnectionProviderFactory.createConnectionProvider(for: URLRequest(url: url),
28 | authInterceptor: authInterceptor,
29 | connectionType: connectionType)
30 | endPointToProvider[url.absoluteString] = connectionProvider
31 |
32 | return AppSyncSubscriptionConnection(provider: connectionProvider)
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/Internal/SubscriptionFactory/SubscriptionConnectionFactory.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | import Foundation
8 | import AppSyncRealTimeClient
9 |
10 | /// Protocol for the subscription factory
11 | protocol SubscriptionConnectionFactory {
12 |
13 | /// Get connection based on the connection type
14 | /// - Parameter connectionType:
15 | func connection(connectionType: SubscriptionConnectionType) -> SubscriptionConnection?
16 | }
17 |
18 | /// Protocol for the different connection pool
19 | protocol SubscriptionConnectionPool {
20 |
21 | /// Get Connection based on the url and connection type
22 | /// - Parameter url: url to connect to
23 | /// - Parameter connectionType:
24 | func connection(for url: URL, connectionType: SubscriptionConnectionType) -> SubscriptionConnection
25 | }
26 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/Internal/SubscriptionFactory/UserPoolsBasedConnectionPool.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | import Foundation
8 | import AppSyncRealTimeClient
9 |
10 | class UserPoolsBasedConnectionPool: SubscriptionConnectionPool {
11 |
12 | private let tokenProvider: AWSCognitoUserPoolsAuthProvider
13 | var endPointToProvider: [String: ConnectionProvider]
14 |
15 | init(_ tokenProvider: AWSCognitoUserPoolsAuthProvider) {
16 | self.tokenProvider = tokenProvider
17 | self.endPointToProvider = [:]
18 | }
19 |
20 | func connection(for url: URL, connectionType: SubscriptionConnectionType) -> SubscriptionConnection {
21 | if let connectionProvider = endPointToProvider[url.absoluteString] {
22 | return AppSyncSubscriptionConnection(provider: connectionProvider)
23 | }
24 |
25 | let authProvider = AppSyncRealTimeClientOIDCAuthProvider(authProvider: tokenProvider)
26 | let authInterceptor = OIDCAuthInterceptor(authProvider)
27 | let connectionProvider = ConnectionProviderFactory.createConnectionProvider(for: URLRequest(url: url),
28 | authInterceptor: authInterceptor,
29 | connectionType: connectionType)
30 | endPointToProvider[url.absoluteString] = connectionProvider
31 |
32 | return AppSyncSubscriptionConnection(provider: connectionProvider)
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/Internal/SyncConfiguration.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | /// Configuration values for an AppSyncSubscriptionWithSync instance.
8 | public struct SyncConfiguration {
9 | /// The interval, in whole seconds, at which the subscription will be refreshed using the `deltaQuery`. If more time has
10 | /// elapsed since the last sync, then local data will be refreshed using `baseQuery` instead.
11 | let baseRefreshIntervalInSeconds: Int
12 |
13 | /// Creates a new SyncConfiguration with the specified sync interval.
14 | ///
15 | /// - Parameters:
16 | /// - baseRefreshIntervalInSeconds: The sync interval. Defaults to one day (86,400 seconds)
17 | public init(baseRefreshIntervalInSeconds: Int = 86_400) {
18 | self.baseRefreshIntervalInSeconds = baseRefreshIntervalInSeconds
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/Internal/SyncStrategy.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | import Foundation
8 |
9 | /// The method for syncing local data with the service
10 | enum SyncMethod {
11 | /// Sync data by performing a full (base) query to retrieve all data from the service
12 | case full
13 |
14 | /// Sync data by performing a partial (delta) query to retrieve only data since the last successful sync
15 | case partial
16 | }
17 |
18 | /// Logic that determines which SyncMethod to use to refresh local data for a given subscription. Tracks the last
19 | /// sync time to compare it against the specified refresh interval.
20 | struct SyncStrategy {
21 | var lastSyncTime: Date?
22 | let baseRefreshIntervalInSeconds: TimeInterval
23 | private let hasDeltaQuery: Bool
24 |
25 | var methodToUseForSync: SyncMethod {
26 | guard let lastSyncTime = lastSyncTime else {
27 | return .full
28 | }
29 |
30 | let timeIntervalSinceLastSync = Date().timeIntervalSince(lastSyncTime)
31 |
32 | if timeIntervalSinceLastSync <= baseRefreshIntervalInSeconds {
33 | return .partial
34 | } else {
35 | return .full
36 | }
37 | }
38 |
39 | init(hasDeltaQuery: Bool, baseRefreshIntervalInSeconds: Int) {
40 | self.hasDeltaQuery = hasDeltaQuery
41 | self.baseRefreshIntervalInSeconds = TimeInterval(exactly: baseRefreshIntervalInSeconds)!
42 | }
43 |
44 | }
45 |
46 | internal extension TimeInterval {
47 | var asDispatchTimeInterval: DispatchTimeInterval {
48 | return DispatchTimeInterval.seconds(Int(exactly: self)!)
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/AWSAppSyncClient/NetworkReachability.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | import Foundation
8 |
9 | /// Defines a factory to return a NetworkReachabilityProviding instance
10 | public protocol NetworkReachabilityProvidingFactory {
11 | /// Abstracting the only of Reachability's initializers that we care about into a factory method. Since Reachability isn't
12 | /// final, we'd have to add a lot of code to conform its initializers otherwise.
13 | static func make(for hostname: String) -> NetworkReachabilityProviding?
14 | }
15 |
16 | /// Wraps methods and properties of Reachability
17 | public protocol NetworkReachabilityProviding: AnyObject {
18 | /// If `true`, device can attempt to reach the host using a cellular connection (WAN). If `false`, host is only considered
19 | /// reachable if it can be accessed via WiFi
20 | var allowsCellularConnection: Bool { get set }
21 |
22 | var connection: AWSAppSyncReachability.Connection { get }
23 |
24 | /// The notification center on which "reachability changed" events are being posted
25 | var notificationCenter: NotificationCenter { get set }
26 |
27 | /// Starts notifications for reachability changes
28 | func startNotifier() throws
29 |
30 | /// Pauses notifications for reachability changes
31 | func stopNotifier()
32 | }
33 |
--------------------------------------------------------------------------------
/AWSAppSyncIntegrationTests/ConsoleResources/appsync-console-query-variables.json:
--------------------------------------------------------------------------------
1 | {
2 | "getPost": "a-post-id",
3 |
4 | "createPostWithFileUsingInputType": {
5 | "author": "Console, createPostWithFileUsingInputType",
6 | "title": "createPostWithFileUsingInputType",
7 | "content": "New content",
8 | "url": "http://www.amazon.com",
9 | "file": {
10 | "bucket": "my-bucket",
11 | "key": "public/new-image.jpg",
12 | "region": "us-east-1",
13 | "localUri": "/path/to/new-image.jpg",
14 | "mimeType": "image/jpeg"
15 | }
16 | },
17 |
18 | "createPostWithFileUsingParameters_author": "Console, createPostWithFileUsingParameters",
19 | "createPostWithFileUsingParameters_title": "createPostWithFileUsingParameters",
20 | "createPostWithFileUsingParameters_content": "New content",
21 | "createPostWithFileUsingParameters_url": "http://www.amazon.com/",
22 | "createPostWithFileUsingParameters_file_bucket": "my-bucket",
23 | "createPostWithFileUsingParameters_file_key": "public/new-image.jpg",
24 | "createPostWithFileUsingParameters_file_region": "us-east-1",
25 | "createPostWithFileUsingParameters_file_localUri": "/path/to/new-image.jpg",
26 | "createPostWithFileUsingParameters_file_mimeType": "image/jpeg",
27 |
28 | "createPostWithoutFileUsingInputType": {
29 | "author": "Console, createPostWithoutFileUsingInputType",
30 | "title": "createPostWithoutFileUsingInputType",
31 | "content": "New content",
32 | "url": "http://www.amazon.com"
33 | },
34 |
35 | "createPostWithoutFileUsingParameters_author": "Console, createPostWithoutFileUsingParameters",
36 | "createPostWithoutFileUsingParameters_title": "createPostWithoutFileUsingParameters",
37 | "createPostWithoutFileUsingParameters_content": "New content",
38 | "createPostWithoutFileUsingParameters_url": "http://www.amazon.com/",
39 |
40 | "updatePostWithFileUsingInputType": {
41 | "id": "a-post-id",
42 | "content": "updatePostWithFileUsingInputType",
43 | "file": {
44 | "bucket": "my-updated-bucket",
45 | "key": "public/updated-image.png",
46 | "region": "us-east-1",
47 | "localUri": "/path/to/updated-image.png",
48 | "mimeType": "image/png"
49 | }
50 | },
51 |
52 | "updatePostWithFileUsingParameters_id": "a-post-id",
53 | "updatePostWithFileUsingParameters_content": "updatePostWithFileUsingParameters",
54 | "updatePostWithFileUsingParameters_file_bucket": "my-updated-bucket",
55 | "updatePostWithFileUsingParameters_file_key": "public/updated-image.png",
56 | "updatePostWithFileUsingParameters_file_region": "us-east-1",
57 | "updatePostWithFileUsingParameters_file_localUri": "/path/to/updated-image.png",
58 | "updatePostWithFileUsingParameters_file_mimeType": "image/png",
59 |
60 | "updatePostWithoutFileUsingInputType": {
61 | "id": "a-post-id",
62 | "content": "updatePostWithoutFileUsingInputType"
63 | },
64 |
65 | "updatePostWithoutFileUsingParameters_id": "a-post-id",
66 | "updatePostWithoutFileUsingParameters_content": "updatePostWithoutFileUsingParameters",
67 |
68 | "upvotePost": "a-post-id",
69 |
70 | "downvotePost": "a-post-id",
71 |
72 | "onUpvotePost": "a-post-id",
73 |
74 | "onDownvotePost": "a-post-id",
75 |
76 | "deletePostUsingInputType": {
77 | "id": "a-post-id"
78 | },
79 |
80 | "deletePostUsingParameters": "a-post-id"
81 | }
--------------------------------------------------------------------------------
/AWSAppSyncIntegrationTests/ConsoleResources/appsync-lambda-authorizer.js:
--------------------------------------------------------------------------------
1 | exports.handler = async (event) => {
2 | console.log(`auth event >`, JSON.stringify(event, null, 2))
3 | const {
4 | authorizationToken,
5 | requestContext: { apiId, accountId },
6 | } = event
7 | const response = {
8 | isAuthorized: authorizationToken === 'custom-lambda-token',
9 | ttlOverride: 10,
10 | }
11 | console.log(`response >`, JSON.stringify(response, null, 2))
12 | return response
13 | };
14 |
--------------------------------------------------------------------------------
/AWSAppSyncIntegrationTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/AWSAppSyncIntegrationTests/appsync_test_credentials.json.enc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/awslabs/aws-mobile-appsync-sdk-ios/589d58ba0e2ae7eac0058235f27bf2b41f66fb85/AWSAppSyncIntegrationTests/appsync_test_credentials.json.enc
--------------------------------------------------------------------------------
/AWSAppSyncIntegrationTests/testS3Object.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/awslabs/aws-mobile-appsync-sdk-ios/589d58ba0e2ae7eac0058235f27bf2b41f66fb85/AWSAppSyncIntegrationTests/testS3Object.jpg
--------------------------------------------------------------------------------
/AWSAppSyncTestApp/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // AWSAppSyncTestApp
4 | //
5 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
6 | // Licensed under the Amazon Software License
7 | // http://aws.amazon.com/asl/
8 | //
9 |
10 | import UIKit
11 |
12 | @UIApplicationMain
13 | class AppDelegate: UIResponder, UIApplicationDelegate {
14 |
15 | var window: UIWindow?
16 |
17 |
18 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
19 | // Override point for customization after application launch.
20 | return true
21 | }
22 |
23 | func applicationWillResignActive(_ application: UIApplication) {
24 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
25 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
26 | }
27 |
28 | func applicationDidEnterBackground(_ application: UIApplication) {
29 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
30 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
31 | }
32 |
33 | func applicationWillEnterForeground(_ application: UIApplication) {
34 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
35 | }
36 |
37 | func applicationDidBecomeActive(_ application: UIApplication) {
38 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
39 | }
40 |
41 | func applicationWillTerminate(_ application: UIApplication) {
42 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
43 | }
44 |
45 |
46 | }
47 |
48 |
--------------------------------------------------------------------------------
/AWSAppSyncTestApp/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "20x20",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "20x20",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "29x29",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "29x29",
61 | "scale" : "2x"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "size" : "40x40",
66 | "scale" : "1x"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "size" : "40x40",
71 | "scale" : "2x"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "size" : "76x76",
76 | "scale" : "1x"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "size" : "76x76",
81 | "scale" : "2x"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "size" : "83.5x83.5",
86 | "scale" : "2x"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "size" : "1024x1024",
91 | "scale" : "1x"
92 | }
93 | ],
94 | "info" : {
95 | "version" : 1,
96 | "author" : "xcode"
97 | }
98 | }
--------------------------------------------------------------------------------
/AWSAppSyncTestApp/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/AWSAppSyncTestApp/Assets.xcassets/Image.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x"
10 | },
11 | {
12 | "idiom" : "universal",
13 | "scale" : "3x"
14 | }
15 | ],
16 | "info" : {
17 | "version" : 1,
18 | "author" : "xcode"
19 | }
20 | }
--------------------------------------------------------------------------------
/AWSAppSyncTestApp/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/AWSAppSyncTestApp/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UILaunchStoryboardName
24 | LaunchScreen
25 | UIMainStoryboardFile
26 | Main
27 | UIRequiredDeviceCapabilities
28 |
29 | armv7
30 |
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 |
37 | UISupportedInterfaceOrientations~ipad
38 |
39 | UIInterfaceOrientationPortrait
40 | UIInterfaceOrientationPortraitUpsideDown
41 | UIInterfaceOrientationLandscapeLeft
42 | UIInterfaceOrientationLandscapeRight
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/AWSAppSyncTestApp/awsconfiguration.json:
--------------------------------------------------------------------------------
1 | {
2 | "UserAgent": "aws-amplify/cli",
3 | "Version": "0.1.0",
4 | "IdentityManager": {
5 | "Default": {}
6 | },
7 | "CognitoUserPool": {
8 | "Default": {
9 | "PoolId": "us-west-2_replace_me",
10 | "AppClientId": "replace_me",
11 | "AppClientSecret": "replace_me",
12 | "Region": "us-west-2"
13 | }
14 | },
15 | "AppSync": {
16 | "Default": {
17 | "ApiUrl": "https://replace_me.appsync-api.us-west-2.amazonaws.com/graphql",
18 | "Region": "us-west-2",
19 | "AuthMode": "AMAZON_COGNITO_USER_POOLS"
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/AWSAppSyncTestAppUITests/AWSAppSyncTestAppUITests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | import XCTest
8 |
9 | class AWSAppSyncTestAppUITests: XCTestCase {
10 |
11 | override func setUp() {
12 | // Put setup code here. This method is called before the invocation of each test method in the class.
13 |
14 | // In UI tests it is usually best to stop immediately when a failure occurs.
15 | continueAfterFailure = false
16 |
17 | // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method.
18 | XCUIApplication().launch()
19 |
20 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
21 | }
22 |
23 | override func tearDown() {
24 | // Put teardown code here. This method is called after the invocation of each test method in the class.
25 | }
26 |
27 | func testExample() {
28 | // Use recording to get started writing UI tests.
29 | // Use XCTAssert and related functions to verify your tests produce the correct results.
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/AWSAppSyncTestAppUITests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/AWSAppSyncTestCommon/AWSAppSyncTestCommon.h:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | #import
8 |
9 | //! Project version number for AWSAppSyncTestCommon.
10 | FOUNDATION_EXPORT double AWSAppSyncTestCommonVersionNumber;
11 |
12 | //! Project version string for AWSAppSyncTestCommon.
13 | FOUNDATION_EXPORT const unsigned char AWSAppSyncTestCommonVersionString[];
14 |
15 | // In this header, you should import all the public headers of your framework using statements like #import
16 |
17 |
18 |
--------------------------------------------------------------------------------
/AWSAppSyncTestCommon/AppSyncClientTestConfigurationDefaults.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppSyncClientTestConfigurationDefaults.swift
3 | // AWSAppSyncTests
4 | //
5 | // Created by Schmelter, Tim on 12/11/18.
6 | // Copyright © 2018 Amazon Web Services. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import AWSCore
11 |
12 | // Override these defaults if you are not using the `AppSyncIntegrationTests/appsync_test_credentials.json` file to manage your
13 | // test client configuration.
14 | //
15 | // Note: You must either provide all values in the `AppSyncIntegrationTests/appsync_test_credentials.json` or in this
16 | // structure. There is no mechanism to handle partial overrides of one source with the other. All values must be
17 | // specified before running the functional tests.
18 | struct AppSyncClientTestConfigurationDefaults {
19 |
20 | // MARK: - Values used for API Key-based tests
21 |
22 | // Equivalent to the JSON key "AppSyncAPIKey"
23 | static let apiKey = "YOUR_API_KEY"
24 |
25 | // Equivalent to the JSON key "AppSyncEndpointAPIKey"
26 | static let apiKeyEndpointURL = URL(string: "https://localhost")!
27 |
28 | // Equivalent to the JSON key "AppSyncEndpointAPIKeyRegion"
29 | static let apiKeyEndpointRegion = AWSRegionType.USEast1
30 |
31 | static let apiKeyForCognitoPoolEndpoint = "YOUR_API_KEY"
32 |
33 | // MARK: - Values used for IAM-based tests
34 |
35 | // Equivalent to the JSON key "CognitoIdentityPoolId"
36 | static let cognitoPoolId = "YOUR_POOL_ID"
37 |
38 | // Equivalent to the JSON key "CognitoIdentityPoolRegion"
39 | static let cognitoPoolRegion = AWSRegionType.USEast1
40 |
41 | // Equivalent to the JSON key "AppSyncEndpoint"
42 | static let cognitoPoolEndpointURL = URL(string: "https://localhost")!
43 |
44 | // Equivalent to the JSON key "AppSyncRegion"
45 | static let cognitoPoolEndpointRegion = AWSRegionType.USEast1
46 |
47 | // Equivalent to the JSON key "BucketName"
48 | static let bucketName = "YOUR_BUCKET_NAME"
49 |
50 | // Equivalent to the JSON key "BucketRegion"
51 | static let bucketRegion = AWSRegionType.USEast1
52 |
53 | // Equivalent to the JSON key "AppSyncEndpoint"
54 | static let lambdaEndpointURL = URL(string: "https://localhost")!
55 |
56 | // Equivalent to the JSON key "AppSyncRegion"
57 | static let lambdaEndpointRegion = AWSRegionType.USEast1
58 | }
59 |
--------------------------------------------------------------------------------
/AWSAppSyncTestCommon/AppSyncTestAPI+S3Object.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | import Foundation
8 | import AWSAppSync
9 |
10 | // Extensions to allow Various `File` fields of the GraphQL API to work with S3ObjectManager
11 |
12 | extension GetPostQuery.Data.GetPost.File: AWSS3ObjectProtocol {
13 | public func getBucketName() -> String {
14 | return bucket
15 | }
16 |
17 | public func getKeyName() -> String {
18 | return key
19 | }
20 |
21 | public func getRegion() -> String {
22 | return region
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/AWSAppSyncTestCommon/DefaultTestPostData.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | @testable import AWSAppSync
8 |
9 | struct DefaultTestPostData {
10 | static let author = "Test author"
11 | static let title = "Test title"
12 | static let content = "Test content"
13 | static let url: String? = "https://aws.amazon.com/"
14 | static let ups = 0
15 | static let downs = 0
16 |
17 | static var defaultCreatePostWithoutFileUsingParametersMutation: CreatePostWithoutFileUsingParametersMutation {
18 | let mutation = CreatePostWithoutFileUsingParametersMutation(
19 | author: author,
20 | title: title,
21 | content: content,
22 | url: url,
23 | ups: ups,
24 | downs: downs
25 | )
26 | return mutation
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/AWSAppSyncTestCommon/Foundation+TestUtils.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | import Foundation
8 |
9 | // Conform String to error module so we can easily use bare strings in Result failures
10 | extension String: Error, LocalizedError {
11 | public var errorDescription: String? {
12 | return self
13 | }
14 |
15 | public var localizedDescription: String {
16 | return self
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/AWSAppSyncTestCommon/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | $(CURRENT_PROJECT_VERSION)
21 |
22 |
23 |
--------------------------------------------------------------------------------
/AWSAppSyncTestCommon/MockAWSAppSyncServiceConfigProvider.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | import Foundation
8 |
9 | @testable import AWSAppSync
10 | import AWSCore
11 |
12 | struct MockAWSAppSyncServiceConfigProvider: AWSAppSyncServiceConfigProvider {
13 | public let endpoint: URL
14 | public let region: AWSRegionType
15 | public let authType: AWSAppSyncAuthType
16 | public let apiKey: String?
17 | public let clientDatabasePrefix: String?
18 |
19 | init(endpoint: URL,
20 | region: AWSRegionType,
21 | authType: AWSAppSyncAuthType,
22 | apiKey: String?,
23 | clientDatabasePrefix: String?) {
24 | self.endpoint = endpoint
25 | self.region = region
26 | self.authType = authType
27 | self.apiKey = apiKey
28 | self.clientDatabasePrefix = clientDatabasePrefix
29 | }
30 |
31 | /// Convenience initializer to return a service config for APIKey auth
32 | init(with testConfiguration: AppSyncClientTestConfiguration) {
33 | self.endpoint = testConfiguration.apiKeyEndpointURL
34 | self.region = testConfiguration.apiKeyEndpointRegion
35 | self.authType = .apiKey
36 | self.apiKey = testConfiguration.apiKey
37 | self.clientDatabasePrefix = testConfiguration.clientDatabasePrefix
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/AWSAppSyncTestCommon/MockAuthProvider/MockAPIKeyAuthProvider.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | import Foundation
8 | @testable import AWSAppSync
9 |
10 | class MockAPIKeyAuthProvider: AWSAPIKeyAuthProvider {
11 |
12 | func getAPIKey() -> String {
13 | return "mock_api_key"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/AWSAppSyncTestCommon/MockAuthProvider/MockIAMAuthProvider.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | import Foundation
8 | @testable import AWSAppSync
9 | import AWSCore
10 |
11 | class MockIAMAuthProvider: NSObject, AWSCredentialsProvider {
12 |
13 | func credentials() -> AWSTask {
14 | let credentials = AWSCredentials(accessKey: "accessKey",
15 | secretKey: "secretKey",
16 | sessionKey: "sessionKey",
17 | expiration: Date())
18 | return AWSTask(result: credentials)
19 | }
20 |
21 | func invalidateCachedTemporaryCredentials() {
22 |
23 | }
24 |
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/AWSAppSyncTestCommon/MockAuthProvider/MockUserPoolsAuthProvider.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | import Foundation
8 | @testable import AWSAppSync
9 |
10 | class MockUserPoolsAuthProvider: AWSCognitoUserPoolsAuthProviderAsync {
11 |
12 | func getLatestAuthToken(_ callback: @escaping (String?, Error?) -> Void) {
13 | callback("jwtToken", nil)
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/AWSAppSyncTestCommon/MockCancellable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | @testable import AWSAppSync
8 |
9 | typealias BasicClosure = () -> Void
10 |
11 | /// A mock that invokes a handler when `cancel` is invoked on it
12 | class MockCancellable: Cancellable {
13 | private var handler: BasicClosure?
14 |
15 | /// Makes a MockCancellable with the supplied optional handler
16 | ///
17 | /// - Parameter handler: handler to call when `cancel` is invoked
18 | init(handler: BasicClosure? = nil) {
19 | self.handler = handler
20 | }
21 |
22 | /// Immediately invokes handler
23 | func cancel() {
24 | handler?()
25 | }
26 | }
27 |
28 |
--------------------------------------------------------------------------------
/AWSAppSyncTestCommon/MockConnectionProvider/MockConnectionProvider.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | import Foundation
8 | @testable import AWSAppSync
9 | @testable import AppSyncRealTimeClient
10 |
11 | class MockConnectionProvider: ConnectionProvider {
12 |
13 | /// If this boolean is set, all the request to this provider will return a connection error
14 | let validConnection: Bool
15 |
16 | /// If this boolean is set, all the request to this provider will return `notConnected` event
17 | var isConnected: Bool = true
18 |
19 | init (validConnection: Bool = true) {
20 | self.validConnection = validConnection
21 | }
22 |
23 | var listener: ConnectionProviderCallback?
24 |
25 | func connect() {
26 | guard validConnection else {
27 | listener?(.error(ConnectionProviderError.connection))
28 | return
29 | }
30 |
31 | guard isConnected else {
32 | listener?(.connection(.notConnected))
33 | return
34 | }
35 |
36 | listener?(.connection(.connected))
37 | }
38 |
39 | func write(_ message: AppSyncMessage) {
40 |
41 | guard validConnection else {
42 | listener?(.error(ConnectionProviderError.connection))
43 | return
44 | }
45 |
46 | guard isConnected else {
47 | listener?(.connection(.notConnected))
48 | return
49 | }
50 |
51 | switch message.messageType {
52 | case .connectionInit:
53 | print("")
54 | case .subscribe:
55 | let response = AppSyncResponse(id: message.id,
56 | payload: [:],
57 | type: .subscriptionAck)
58 | listener?(.data(response))
59 | case .unsubscribe:
60 | let response = AppSyncResponse(id: message.id,
61 | payload: [:],
62 | type: .unsubscriptionAck)
63 | listener?(.data(response))
64 | }
65 | }
66 |
67 | func disconnect() {
68 | guard validConnection else {
69 | listener?(.error(ConnectionProviderError.connection))
70 | return
71 | }
72 |
73 | guard isConnected else {
74 | listener?(.connection(.notConnected))
75 | return
76 | }
77 |
78 | listener?(.connection(.notConnected))
79 | }
80 |
81 | func addListener(identifier: String, callback: @escaping ConnectionProviderCallback) {
82 | listener = callback
83 | }
84 |
85 | func removeListener(identifier: String) {
86 | listener = nil
87 | }
88 | func sendDataResponse(_ response: AppSyncResponse) {
89 | listener?(.data(response))
90 | }
91 | }
92 |
93 | class MockConnectionProviderAlwaysConnect: MockConnectionProvider {
94 |
95 | override func connect() {
96 | listener?(.connection(.connected))
97 | }
98 |
99 | }
100 |
--------------------------------------------------------------------------------
/AWSAppSyncTestCommon/MockReachability.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | import Foundation
8 | @testable import AWSAppSync
9 |
10 | /// The factory will always return the same instance. Make sure to clear the instance between tests by calling `clearShared`
11 | /// in the test's `tearDown` method.
12 | struct MockReachabilityProvidingFactory: NetworkReachabilityProvidingFactory {
13 | static var instance = MockReachabilityProviding()
14 |
15 | static func clearShared() {
16 | instance = MockReachabilityProviding()
17 | }
18 |
19 | static func make(for hostname: String) -> NetworkReachabilityProviding? {
20 | return instance
21 | }
22 | }
23 |
24 | /// The instance class vended by ReachabilityProvidingTestFactory
25 | class MockReachabilityProviding: NetworkReachabilityProviding {
26 |
27 | var allowsCellularConnection = false
28 | var notificationCenter = NotificationCenter.default
29 |
30 | var connection = AWSAppSyncReachability.Connection.wifi {
31 | didSet {
32 | guard isNotifierStarted else {
33 | return
34 | }
35 |
36 | DispatchQueue.main.async { [weak self] in
37 | guard let self = self else {
38 | return
39 | }
40 | self.notificationCenter.post(name: .reachabilityChanged, object: self)
41 | }
42 | }
43 | }
44 |
45 | private var isNotifierStarted = false
46 |
47 | func startNotifier() throws {
48 | isNotifierStarted = true
49 | }
50 |
51 | func stopNotifier() {
52 | isNotifierStarted = false
53 | }
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/AWSAppSyncTestCommon/MockS3ObjectManager.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | @testable import AWSAppSync
8 |
9 | typealias S3ResultHandler = (Bool, Error?) -> Void
10 |
11 | /// A mock to allow interception of calls to upload and download S3 objects
12 | class MockS3ObjectManager: AWSS3ObjectManager {
13 | /// A handler to call when `upload` is invoked. Handler is invoked with same arguments
14 | /// to `upload`
15 | var uploadHandler: (AWSS3ObjectProtocol & AWSS3InputObjectProtocol, @escaping S3ResultHandler) -> Void = { _, _ in }
16 | func upload(s3Object: AWSS3ObjectProtocol & AWSS3InputObjectProtocol, completion: @escaping S3ResultHandler) {
17 | uploadHandler(s3Object, completion)
18 | }
19 |
20 | /// A handler to call when `download` is invoked. Handler is invoked with the same arguments
21 | /// to `download`
22 | var downloadHandler: (AWSS3ObjectProtocol, URL, @escaping S3ResultHandler) -> Void = { _, _, _ in }
23 | func download(s3Object: AWSS3ObjectProtocol, toURL: URL, completion: @escaping S3ResultHandler) {
24 | downloadHandler(s3Object, toURL, completion)
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/AWSAppSyncTestCommon/MockSubscriptionFactory/MockSubscriptionConnection.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | import Foundation
8 | @testable import AWSAppSync
9 | @testable import AppSyncRealTimeClient
10 |
11 | class MockSubscriptionConnection: SubscriptionConnection {
12 |
13 | /// Current item that is subscriped
14 | var subscriptionItem: SubscriptionItem!
15 |
16 | func subscribe(requestString: String,
17 | variables: [String : Any?]?,
18 | eventHandler: @escaping SubscriptionEventHandler) -> SubscriptionItem {
19 | subscriptionItem = SubscriptionItem(requestString: requestString,
20 | variables: variables,
21 | eventHandler: eventHandler)
22 |
23 | return subscriptionItem
24 | }
25 |
26 | func unsubscribe(item: SubscriptionItem) {
27 |
28 | }
29 |
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/AWSAppSyncTestCommon/MockSubscriptionFactory/MockSubscriptionFactory.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | import Foundation
8 | @testable import AWSAppSync
9 | import AppSyncRealTimeClient
10 |
11 | class MockSubscriptionFactory: SubscriptionConnectionFactory {
12 |
13 | func connection(connectionType: SubscriptionConnectionType) -> SubscriptionConnection? {
14 | let connection = MockSubscriptionConnection()
15 | return connection
16 | }
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/AWSAppSyncTestCommon/Object+Swizzling.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Object+Swizzling.swift
3 | // AWSAppSync
4 | //
5 | // Created by Mario Araujo on 11/07/2018.
6 | // Copyright © 2018 Amazon Web Services. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 |
12 | func setAssociatedObject(object: AnyObject, value: T, associativeKey: UnsafeRawPointer, policy: objc_AssociationPolicy) {
13 | objc_setAssociatedObject(object, associativeKey, value, policy)
14 | }
15 |
16 | func getAssociatedObject(object: AnyObject, associativeKey: UnsafeRawPointer) -> T? {
17 | guard let value = objc_getAssociatedObject(object, associativeKey) as? T else {
18 | return nil
19 | }
20 | return value
21 | }
22 |
23 | extension NSObject {
24 | private struct AssociatedKey {
25 | static var swizzledMethodsRestorations = "swizzledMethodsRestorations"
26 | }
27 |
28 | @objc class var swizzledMethodsRestorations: NSMutableArray? {
29 | get {
30 | return getAssociatedObject(object: self, associativeKey: &AssociatedKey.swizzledMethodsRestorations)
31 | }
32 |
33 | set {
34 | if let value = newValue {
35 | setAssociatedObject(object: self, value: value, associativeKey: &AssociatedKey.swizzledMethodsRestorations, policy: objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN_NONATOMIC)
36 | }
37 | }
38 | }
39 |
40 | @objc static func swizzle(selector: Selector, withBlock block: Any) {
41 | guard let originalMethod = class_getInstanceMethod(self, selector) else {
42 | return
43 | }
44 |
45 | let swizzledBlock = imp_implementationWithBlock(block)
46 |
47 | let newMethod = method_setImplementation(originalMethod, swizzledBlock)
48 |
49 | let block: () -> Void = {
50 | method_setImplementation(originalMethod, newMethod)
51 | }
52 |
53 | if let array = self.swizzledMethodsRestorations {
54 | array.add(block)
55 | } else {
56 | self.swizzledMethodsRestorations = NSMutableArray(array: [block])
57 | }
58 | }
59 |
60 | @objc static func restoreSwizzledMethods() {
61 | if let array = self.swizzledMethodsRestorations {
62 | array.forEach { (object) in
63 | if let block = object as? () -> Void {
64 | block()
65 | }
66 | }
67 | array.removeAllObjects()
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/AWSAppSyncTestHostApp/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | import UIKit
8 |
9 | @UIApplicationMain
10 | class AppDelegate: UIResponder, UIApplicationDelegate {
11 |
12 | var window: UIWindow?
13 |
14 |
15 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
16 | // Override point for customization after application launch.
17 | return true
18 | }
19 |
20 | func applicationWillResignActive(_ application: UIApplication) {
21 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
22 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
23 | }
24 |
25 | func applicationDidEnterBackground(_ application: UIApplication) {
26 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
27 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
28 | }
29 |
30 | func applicationWillEnterForeground(_ application: UIApplication) {
31 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
32 | }
33 |
34 | func applicationDidBecomeActive(_ application: UIApplication) {
35 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
36 | }
37 |
38 | func applicationWillTerminate(_ application: UIApplication) {
39 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
40 | }
41 |
42 |
43 | }
44 |
45 |
--------------------------------------------------------------------------------
/AWSAppSyncTestHostApp/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "29x29",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "29x29",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "40x40",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "40x40",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "2x"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "size" : "60x60",
41 | "scale" : "3x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "20x20",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "20x20",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "29x29",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "29x29",
61 | "scale" : "2x"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "size" : "40x40",
66 | "scale" : "1x"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "size" : "40x40",
71 | "scale" : "2x"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "size" : "76x76",
76 | "scale" : "1x"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "size" : "76x76",
81 | "scale" : "2x"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "size" : "83.5x83.5",
86 | "scale" : "2x"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "size" : "1024x1024",
91 | "scale" : "1x"
92 | }
93 | ],
94 | "info" : {
95 | "version" : 1,
96 | "author" : "xcode"
97 | }
98 | }
--------------------------------------------------------------------------------
/AWSAppSyncTestHostApp/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/AWSAppSyncTestHostApp/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/AWSAppSyncTestHostApp/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/AWSAppSyncTestHostApp/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UILaunchStoryboardName
24 | LaunchScreen
25 | UIMainStoryboardFile
26 | Main
27 | UIRequiredDeviceCapabilities
28 |
29 | armv7
30 |
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 |
37 | UISupportedInterfaceOrientations~ipad
38 |
39 | UIInterfaceOrientationPortrait
40 | UIInterfaceOrientationPortraitUpsideDown
41 | UIInterfaceOrientationLandscapeLeft
42 | UIInterfaceOrientationLandscapeRight
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/AWSAppSyncTestHostApp/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | import UIKit
8 |
9 | class ViewController: UIViewController {
10 |
11 | override func viewDidLoad() {
12 | super.viewDidLoad()
13 | // Do any additional setup after loading the view, typically from a nib.
14 | }
15 |
16 |
17 | }
18 |
19 |
--------------------------------------------------------------------------------
/AWSAppSyncTestHostApp/awsconfiguration.json:
--------------------------------------------------------------------------------
1 | {
2 | "UserAgent": "aws-amplify/cli",
3 | "Version": "0.1.0",
4 | "IdentityManager": {
5 | "Default": {}
6 | },
7 | "AppSync": {
8 | "Default": {
9 | "ApiUrl": "https://default.appsync-api.us-east-1.amazonaws.com/graphql",
10 | "Region": "us-east-1",
11 | "AuthMode": "AWS_IAM"
12 | },
13 | "UnitTests_EmptyAPIKeyConfiguration": {
14 | "ApiUrl": "https://empty-api-key.appsync-api.us-east-1.amazonaws.com/graphql",
15 | "Region": "us-east-1",
16 | "AuthMode": "API_KEY",
17 | "ApiKey": ""
18 | },
19 | "UnitTests_MissingAPIKeyConfiguration": {
20 | "ApiUrl": "https://missing-api-key.appsync-api.us-east-1.amazonaws.com/graphql",
21 | "Region": "us-east-1",
22 | "AuthMode": "API_KEY"
23 | },
24 | "UnitTests_GoodAPIKeyConfiguration": {
25 | "ApiUrl": "https://good-api-key.appsync-api.us-east-1.amazonaws.com/graphql",
26 | "Region": "us-east-1",
27 | "AuthMode": "API_KEY",
28 | "ApiKey": "THE_API_KEY"
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/AWSAppSyncTestHostApp/cacheConfigUnitTests-allTables.db:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/awslabs/aws-mobile-appsync-sdk-ios/589d58ba0e2ae7eac0058235f27bf2b41f66fb85/AWSAppSyncTestHostApp/cacheConfigUnitTests-allTables.db
--------------------------------------------------------------------------------
/AWSAppSyncTestHostApp/cacheConfigUnitTests-noMutationRecords.db:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/awslabs/aws-mobile-appsync-sdk-ios/589d58ba0e2ae7eac0058235f27bf2b41f66fb85/AWSAppSyncTestHostApp/cacheConfigUnitTests-noMutationRecords.db
--------------------------------------------------------------------------------
/AWSAppSyncTestHostApp/cacheConfigUnitTests-noRecords.db:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/awslabs/aws-mobile-appsync-sdk-ios/589d58ba0e2ae7eac0058235f27bf2b41f66fb85/AWSAppSyncTestHostApp/cacheConfigUnitTests-noRecords.db
--------------------------------------------------------------------------------
/AWSAppSyncTestHostApp/cacheConfigUnitTests-noSubscriptionMetadata.db:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/awslabs/aws-mobile-appsync-sdk-ios/589d58ba0e2ae7eac0058235f27bf2b41f66fb85/AWSAppSyncTestHostApp/cacheConfigUnitTests-noSubscriptionMetadata.db
--------------------------------------------------------------------------------
/AWSAppSyncTestHostApp/cacheConfigUnitTests-noTables.db:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/awslabs/aws-mobile-appsync-sdk-ios/589d58ba0e2ae7eac0058235f27bf2b41f66fb85/AWSAppSyncTestHostApp/cacheConfigUnitTests-noTables.db
--------------------------------------------------------------------------------
/AWSAppSyncUnitTests/AWSAppSyncClientInfoTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | import XCTest
8 | @testable import AWSAppSync
9 |
10 | @available(*, deprecated, message: "To be removed when we remove AWSAppSyncClientInfo")
11 | class AWSAppSyncClientInfoTests: XCTestCase {
12 |
13 | func testCanLoadFromDefaultConfigSection() {
14 | do {
15 | let config = try AWSAppSyncClientInfo()
16 | XCTAssert(config.apiUrl.starts(with: "https://default.appsync-api"))
17 | } catch {
18 | XCTFail("Can't load from default config section: \(error.localizedDescription)")
19 | }
20 | }
21 |
22 | func testCanLoadFromSpecifiedConfigSection() {
23 | do {
24 | let config = try AWSAppSyncClientInfo(forKey: "UnitTests_GoodAPIKeyConfiguration")
25 | XCTAssert(config.apiUrl.starts(with: "https://good-api-key.appsync-api"))
26 | } catch {
27 | XCTFail("Can't load from good-api-key config section: \(error.localizedDescription)")
28 | }
29 | }
30 |
31 | func testCanLoadValidAPIConfiguration() {
32 | do {
33 | let config = try AWSAppSyncClientInfo(forKey: "UnitTests_GoodAPIKeyConfiguration")
34 | XCTAssertEqual(config.apiKey, "THE_API_KEY")
35 | } catch {
36 | XCTFail("Can't load from good-api-key config section: \(error.localizedDescription)")
37 | }
38 | }
39 |
40 | func testThrowsOnEmptyAPIKey() {
41 | do {
42 | let _ = try AWSAppSyncClientInfo(forKey: "UnitTests_EmptyAPIKeyConfiguration")
43 | XCTFail("Expected validation to fail with empty API Key")
44 | } catch {
45 | guard let clientInfoError = error as? AWSAppSyncClientInfoError else {
46 | XCTFail("Expected validation to throw AWSAppSyncClientInfoError for empty API key, but got \(type(of: error))")
47 | return
48 | }
49 | XCTAssert(clientInfoError.localizedDescription.starts(with: "API_KEY"))
50 | XCTAssert(true, "Threw validation error as expected: \(error.localizedDescription)")
51 | }
52 | }
53 |
54 | func testThrowsOnMissingAPIKey() {
55 | do {
56 | let _ = try AWSAppSyncClientInfo(forKey: "UnitTests_MissingAPIKeyConfiguration")
57 | XCTFail("Expected validation to fail with missing API Key")
58 | } catch {
59 | guard let clientInfoError = error as? AWSAppSyncClientInfoError else {
60 | XCTFail("Expected validation to throw AWSAppSyncClientInfoError for missing API key, but got \(type(of: error))")
61 | return
62 | }
63 | XCTAssert(clientInfoError.localizedDescription.starts(with: "API_KEY"))
64 | XCTAssert(true, "Threw validation error as expected: \(error.localizedDescription)")
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/AWSAppSyncUnitTests/AWSAppSyncServiceConfigTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | import XCTest
8 | @testable import AWSAppSync
9 |
10 | class AWSAppSyncServiceConfigTests: XCTestCase {
11 |
12 | func testCanLoadFromDefaultConfigSection() {
13 | do {
14 | let config = try AWSAppSyncServiceConfig()
15 | XCTAssert(config.endpoint.absoluteString.starts(with: "https://default.appsync-api"))
16 | } catch {
17 | XCTFail("Can't load from default config section: \(error.localizedDescription)")
18 | }
19 | }
20 |
21 | func testCanLoadFromSpecifiedConfigSection() {
22 | do {
23 | let config = try AWSAppSyncServiceConfig(forKey: "UnitTests_GoodAPIKeyConfiguration")
24 | XCTAssert(config.endpoint.absoluteString.starts(with: "https://good-api-key.appsync-api"))
25 | } catch {
26 | XCTFail("Can't load from good-api-key config section: \(error.localizedDescription)")
27 | }
28 | }
29 |
30 | func testCanLoadValidAPIConfiguration() {
31 | do {
32 | let config = try AWSAppSyncServiceConfig(forKey: "UnitTests_GoodAPIKeyConfiguration")
33 | XCTAssertEqual(config.apiKey, "THE_API_KEY")
34 | } catch {
35 | XCTFail("Can't load from good-api-key config section: \(error.localizedDescription)")
36 | }
37 | }
38 |
39 | func testThrowsOnEmptyAPIKey() {
40 | do {
41 | let _ = try AWSAppSyncServiceConfig(forKey: "UnitTests_EmptyAPIKeyConfiguration")
42 | XCTFail("Expected validation to fail with empty API Key")
43 | } catch {
44 | guard case AWSAppSyncServiceConfigError.invalidAPIKey = error else {
45 | XCTFail("Expected validation to throw AWSAppSyncServiceConfigError.invalidAPIKey for empty API key, but got \(type(of: error))")
46 | return
47 | }
48 | }
49 | }
50 |
51 | func testThrowsOnMissingAPIKey() {
52 | do {
53 | let _ = try AWSAppSyncServiceConfig(forKey: "UnitTests_MissingAPIKeyConfiguration")
54 | XCTFail("Expected validation to fail with missing API Key")
55 | } catch {
56 | guard case AWSAppSyncServiceConfigError.invalidAPIKey = error else {
57 | XCTFail("Expected validation to throw AWSAppSyncServiceConfigError.invalidAPIKey for missing API key, but got \(type(of: error))")
58 | return
59 | }
60 | }
61 | }
62 |
63 | }
64 |
--------------------------------------------------------------------------------
/AWSAppSyncUnitTests/AWSSQLiteNormalizedCacheTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | import XCTest
8 | @testable import AWSAppSync
9 | @testable import AWSAppSyncTestCommon
10 | import SQLite
11 |
12 | // This class only tests a few high-level operations like database setup. For more
13 | // detailed behavior tests, see appropriate tests in the ApolloTests suites.
14 | class AWSSQLiteNormalizedCacheTests: XCTestCase {
15 | static let fetchQueue = DispatchQueue(label: "AWSSQLiteNormalizedCacheTests.fetch")
16 | static let mutationQueue = DispatchQueue(label: "AWSSQLiteNormalizedCacheTests.mutations")
17 |
18 | var cacheConfiguration: AWSAppSyncCacheConfiguration!
19 | let mockHTTPTransport = MockAWSNetworkTransport()
20 |
21 | // Set up a new DB for each test
22 | override func setUp() {
23 | let tempDir = FileManager.default.temporaryDirectory
24 | let rootDirectory = tempDir.appendingPathComponent("AWSSQLiteNormalizedCacheTests-\(UUID().uuidString)")
25 | cacheConfiguration = try! AWSAppSyncCacheConfiguration(withRootDirectory: rootDirectory)
26 | }
27 |
28 | /// Xcode 10.2 introduced a behavior change in the #function expression that broke SQLite.swift. That
29 | /// manifested as a break in the caching behavior. This test simply asserts that our creation routine
30 | /// properly populates an initial QUERY_ROOT record as expected. This isn't technically testing the
31 | /// public API, but it's the simplest test that asserts the correct SQLite.swift behavior.
32 | /// See [Issue #211](https://github.com/awslabs/aws-mobile-appsync-sdk-ios/issues/211)
33 | func testInitializedDatabaseHasQueryRoot() throws {
34 | _ = try UnitTestHelpers.makeAppSyncClient(using: mockHTTPTransport,
35 | cacheConfiguration: cacheConfiguration)
36 |
37 | let queriesDB = try Connection(.uri(cacheConfiguration.queries!.absoluteString),
38 | readonly: false)
39 |
40 | let queryRootCount = try queriesDB.scalar("SELECT count(*) FROM records WHERE key='QUERY_ROOT'") as! Int64
41 | XCTAssertEqual(queryRootCount, 1)
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/AWSAppSyncUnitTests/AppSyncApolloCustomizationTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | import Foundation
8 | import XCTest
9 | @testable import AWSAppSync
10 |
11 | class AppSyncApolloCustomizationTests: XCTestCase {
12 |
13 | func testRecordSetConformsToCustomPlaygroundDisplayConvertible() {
14 | // Note: this line will generate a compiler warning, but we're using it as documentation to ensure no regressions in our
15 | // forked Apollo code
16 | let conformedRecordSet = RecordSet(records: [])
17 | XCTAssertNotNil(conformedRecordSet)
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/AWSAppSyncUnitTests/Foundation+UtilsTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | import XCTest
8 | @testable import AWSAppSync
9 |
10 | class Foundation_UtilsTests: XCTestCase {
11 |
12 | func test_isEmpty_extensionPlaysNicelyWithStandardLib_Array() {
13 | let notEmpty: [String] = ["Foo"]
14 | XCTAssertFalse(notEmpty.isEmpty)
15 |
16 | let empty: [String] = []
17 | XCTAssert(empty.isEmpty)
18 | }
19 |
20 | func test_isEmpty_extensionPlaysNicelyWithStandardLib_Dict() {
21 | let notEmpty: [String: Int] = ["Foo": 1]
22 | XCTAssertFalse(notEmpty.isEmpty)
23 |
24 | let empty: [String: Int] = [:]
25 | XCTAssert(empty.isEmpty)
26 | }
27 |
28 | func test_isEmpty_String() {
29 | let notEmpty: String = "Foo"
30 | XCTAssertFalse(notEmpty.isEmpty)
31 |
32 | let empty: String = ""
33 | XCTAssert(empty.isEmpty)
34 |
35 | let notEmptyOptional: String? = "Foo"
36 | XCTAssertFalse(notEmptyOptional.isEmpty)
37 |
38 | let emptyOptional: String? = ""
39 | XCTAssert(emptyOptional.isEmpty)
40 |
41 | let nilOptional: String? = nil
42 | XCTAssert(nilOptional.isEmpty)
43 | }
44 |
45 | func test_isEmpty_Array() {
46 | let notEmpty: [String] = ["Foo"]
47 | XCTAssertFalse(notEmpty.isEmpty)
48 |
49 | let empty: [String] = []
50 | XCTAssert(empty.isEmpty)
51 |
52 | let notEmptyOptional: [String]? = ["Foo"]
53 | XCTAssertFalse(notEmptyOptional.isEmpty)
54 |
55 | let emptyOptional: [String]? = []
56 | XCTAssert(emptyOptional.isEmpty)
57 |
58 | let nilOptional: [String]? = nil
59 | XCTAssert(nilOptional.isEmpty)
60 | }
61 |
62 | func test_isEmpty_Dict() {
63 | let notEmpty: [String: Int] = ["Foo": 1]
64 | XCTAssertFalse(notEmpty.isEmpty)
65 |
66 | let empty: [String: Int] = [:]
67 | XCTAssert(empty.isEmpty)
68 |
69 | let notEmptyOptional: [String: Int]? = ["Foo": 1]
70 | XCTAssertFalse(notEmptyOptional.isEmpty)
71 |
72 | let emptyOptional: [String: Int]? = [:]
73 | XCTAssert(emptyOptional.isEmpty)
74 |
75 | let nilOptional: [String: Int]? = nil
76 | XCTAssert(nilOptional.isEmpty)
77 | }
78 |
79 | }
80 |
--------------------------------------------------------------------------------
/AWSAppSyncUnitTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/AWSAppSyncUnitTests/Logging/AppSyncLogHelperTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | import XCTest
8 | @testable import AWSAppSync
9 | import AWSCore
10 |
11 | class AppSyncLogHelperTests: XCTestCase {
12 |
13 | func testShouldLogTrueResult() {
14 | AWSDDLog.sharedInstance.logLevel = .warning
15 | let result = AppSyncLogHelper.shouldLog(flag: .warning)
16 | XCTAssertTrue(result)
17 | }
18 |
19 | func testShouldLogFalseResult() {
20 | AWSDDLog.sharedInstance.logLevel = .info
21 | let result = AppSyncLogHelper.shouldLog(flag: .warning)
22 | XCTAssertTrue(result)
23 | }
24 |
25 | func testLoggingInfo() {
26 | let mockedLogger = MockLogger()
27 | AWSDDLog.sharedInstance.logLevel = .info
28 | AWSDDLog.sharedInstance.add(mockedLogger)
29 | AppSyncLog.info("Hi there")
30 | // Logging happens in an async queue, so wait a second for
31 | // logging to happen
32 | sleep(1)
33 | XCTAssertEqual(mockedLogger.loggedMessage, "Hi there")
34 | }
35 |
36 | func testLoggingFail() {
37 | let mockedLogger = MockLogger()
38 | AWSDDLog.sharedInstance.logLevel = .info
39 | AWSDDLog.sharedInstance.add(mockedLogger)
40 | AppSyncLog.debug("Hi there")
41 | // Logging happens in an async queue, so wait a second for
42 | // logging to happen
43 | sleep(1)
44 | XCTAssertEqual(mockedLogger.loggedMessage, "")
45 | }
46 | }
47 |
48 | class MockLogger: NSObject, AWSDDLogger {
49 | var loggedMessage = ""
50 |
51 | var logFormatter: AWSDDLogFormatter? = AWSAppSyncClientLogFormatter()
52 |
53 | func log(message logMessage: AWSDDLogMessage) {
54 | loggedMessage = logMessage.message
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/AWSAppSyncUnitTests/MockAWSAppSyncServiceConfig.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | import Foundation
8 | @testable import AWSAppSync
9 | import AWSCore
10 |
11 | public struct MockAWSAppSyncServiceConfig: AWSAppSyncServiceConfigProvider {
12 | public let endpoint: URL
13 | public let region: AWSRegionType
14 | public let authType: AWSAppSyncAuthType
15 | public let apiKey: String?
16 | public let clientDatabasePrefix: String?
17 |
18 | init(endpoint: URL,
19 | region: AWSRegionType,
20 | authType: AWSAppSyncAuthType,
21 | apiKey: String? = nil,
22 | clientDatabasePrefix: String? = nil) {
23 | self.endpoint = endpoint
24 | self.region = region
25 | self.authType = authType
26 | self.apiKey = apiKey
27 | self.clientDatabasePrefix = clientDatabasePrefix
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/AWSAppSyncUnitTests/Subscription/AuthInterceptor/IAMAuthInterceptorTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 | // Licensed under the Amazon Software License
4 | // http://aws.amazon.com/asl/
5 | //
6 |
7 | import XCTest
8 | @testable import AWSAppSync
9 | @testable import AWSAppSyncTestCommon
10 | import AppSyncRealTimeClient
11 |
12 | class IAMAuthInterceptorTests: XCTestCase {
13 |
14 | var authInterceptor: IAMAuthInterceptor!
15 |
16 | override func setUp() {
17 | authInterceptor = IAMAuthInterceptor(MockIAMAuthProvider(), region: .USWest2)
18 | }
19 |
20 | func testInterceptRequest() {
21 | let url = URL(string: "http://xxxc.appsync-api.ap-southeast-2.amazonaws.com/sd")!
22 | let request = AppSyncConnectionRequest(url: url)
23 | let signedRequest = authInterceptor.interceptConnection(request, for: url)
24 |
25 | guard let queries = URLComponents(url: signedRequest.url, resolvingAgainstBaseURL: true)?.queryItems else {
26 | assertionFailure("Query parameters should not be nil")
27 | return
28 | }
29 | XCTAssertTrue(queries.contains{ $0.name == "header"}, "Should contain the header query")
30 | XCTAssertTrue(queries.contains{ $0.name == "payload"}, "Should contain the payload query")
31 | }
32 |
33 | func testInterceptMessage() {
34 | let message = AppSyncMessage(type: .subscribe("start"))
35 | let url = URL(string: "http://xxxc.appsync-api.ap-southeast-2.amazonaws.com/sd")!
36 | let signedMessage = authInterceptor.interceptMessage(message, for: url)
37 | XCTAssertNotNil(signedMessage.payload?.authHeader)
38 |
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/AWSAppSyncUnitTests/SyncStrategyTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SyncStrategyTests.swift
3 | // AWSAppSyncTests
4 | //
5 | // Created by Schmelter, Tim on 12/12/18.
6 | // Copyright © 2018 Amazon Web Services. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import AWSAppSync
11 |
12 | class SyncStrategyTests: XCTestCase {
13 |
14 | func test_ReturnsFullIfNoDeltaQuery() {
15 | let baseRefreshIntervalInSeconds = 1
16 | let syncStrategy = SyncStrategy(hasDeltaQuery: false, baseRefreshIntervalInSeconds: baseRefreshIntervalInSeconds)
17 | let syncMethodToUse = syncStrategy.methodToUseForSync
18 | XCTAssertEqual(syncMethodToUse, SyncMethod.full)
19 | }
20 |
21 | func test_ReturnsFullIfNotPreviouslySynced() {
22 | let baseRefreshIntervalInSeconds = 1
23 | let syncStrategy = SyncStrategy(hasDeltaQuery: false, baseRefreshIntervalInSeconds: baseRefreshIntervalInSeconds)
24 | let syncMethodToUse = syncStrategy.methodToUseForSync
25 | XCTAssertEqual(syncMethodToUse, SyncMethod.full)
26 | }
27 |
28 | func test_ReturnsFullIfLastSyncTimeIsOutsideInterval() {
29 | let now = Date()
30 | let baseRefreshIntervalInSeconds = 1
31 | let lastSyncTime = now.addingTimeInterval(-10)
32 |
33 | var syncStrategy = SyncStrategy(hasDeltaQuery: false, baseRefreshIntervalInSeconds: baseRefreshIntervalInSeconds)
34 | syncStrategy.lastSyncTime = lastSyncTime
35 |
36 | let syncMethodToUse = syncStrategy.methodToUseForSync
37 | XCTAssertEqual(syncMethodToUse, SyncMethod.full)
38 | }
39 |
40 | func test_ReturnsPartialIfLastSyncTimeIsInsideInterval() {
41 | let now = Date()
42 | let baseRefreshIntervalInSeconds = 10
43 | let lastSyncTime = now.addingTimeInterval(-5)
44 |
45 | var syncStrategy = SyncStrategy(hasDeltaQuery: false, baseRefreshIntervalInSeconds: baseRefreshIntervalInSeconds)
46 | syncStrategy.lastSyncTime = lastSyncTime
47 |
48 | let syncMethodToUse = syncStrategy.methodToUseForSync
49 | XCTAssertEqual(syncMethodToUse, SyncMethod.partial)
50 | }
51 |
52 | func test_ReturnsPartialIfLastSyncTimeIsInFuture() {
53 | let now = Date()
54 | let baseRefreshIntervalInSeconds = 1
55 | let lastSyncTime = now.addingTimeInterval(10)
56 |
57 | var syncStrategy = SyncStrategy(hasDeltaQuery: false, baseRefreshIntervalInSeconds: baseRefreshIntervalInSeconds)
58 | syncStrategy.lastSyncTime = lastSyncTime
59 |
60 | let syncMethodToUse = syncStrategy.methodToUseForSync
61 | XCTAssertEqual(syncMethodToUse, SyncMethod.partial)
62 | }
63 |
64 | }
65 |
--------------------------------------------------------------------------------
/ApolloTests/CacheKeyForFieldTests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 | @testable import AWSAppSync
3 | import StarWarsAPI
4 |
5 | extension GraphQLField {
6 | var cacheKey: String {
7 | return try! cacheKey(with: nil)
8 | }
9 | }
10 |
11 | class CacheKeyForFieldTests: XCTestCase {
12 | func testFieldWithResponseNameOnly() {
13 | let field = GraphQLField("hero", type: .scalar(String.self))
14 | XCTAssertEqual(field.cacheKey, "hero")
15 | }
16 |
17 | func testFieldWithAlias() {
18 | let field = GraphQLField("hero", alias: "r2", type: .scalar(String.self))
19 | XCTAssertEqual(field.cacheKey, "hero")
20 | }
21 |
22 | func testFieldWithArgument() {
23 | let field = GraphQLField("hero", arguments: ["episode": Episode.jedi], type: .scalar(String.self))
24 | XCTAssertEqual(field.cacheKey, "hero(episode:JEDI)")
25 | }
26 |
27 | func testFieldWithAliasAndArgument() {
28 | let field = GraphQLField("hero", alias: "r2", arguments: ["episode": Episode.jedi], type: .scalar(String.self))
29 | XCTAssertEqual(field.cacheKey, "hero(episode:JEDI)")
30 | }
31 |
32 | func testFieldWithInputObjectArgument() throws {
33 | let field = GraphQLField("hero", arguments: ["nested": ["foo": 1, "bar": 2]], type: .scalar(String.self))
34 | XCTAssertEqual(field.cacheKey, "hero([nested:bar:2,foo:1])")
35 | }
36 |
37 | func testFieldWithInputObjectArgumentWithVariables() throws {
38 | let field = GraphQLField("hero", arguments: ["nested": ["foo": GraphQLVariable("a"), "bar": GraphQLVariable("b")]], type: .scalar(String.self))
39 | let variables: GraphQLMap = ["a": 1, "b": 2]
40 | XCTAssertEqual(try field.cacheKey(with: variables), "hero([nested:bar:2,foo:1])")
41 | }
42 |
43 | func testFieldWithMultipleArgumentsIsOrderIndependent() {
44 | let field1 = GraphQLField("hero", arguments: ["foo": "a", "bar": "b"], type: .scalar(String.self))
45 | let field2 = GraphQLField("hero", arguments: ["bar": "b", "foo": "a"], type: .scalar(String.self))
46 | XCTAssertEqual(field1.cacheKey, field2.cacheKey)
47 | }
48 |
49 | func testFieldWithInputObjectArgumentIsOrderIndependent() {
50 | let field1 = GraphQLField("hero", arguments: ["episode": Episode.jedi, "nested": ["foo": "a", "bar": "b"]], type: .scalar(String.self))
51 | let field2 = GraphQLField("hero", arguments: ["episode": Episode.jedi, "nested": ["bar": "b", "foo": "a"]], type: .scalar(String.self))
52 | XCTAssertEqual(field1.cacheKey, field2.cacheKey)
53 | }
54 |
55 | func testFieldWithVariableArgument() throws {
56 | let field = GraphQLField("hero", arguments: ["episode": GraphQLVariable("episode")], type: .scalar(String.self))
57 | let variables = ["episode": Episode.jedi]
58 | XCTAssertEqual(try field.cacheKey(with: variables), "hero(episode:JEDI)")
59 | }
60 |
61 | func testFieldWithVariableArgumentWithNil() throws {
62 | let field = GraphQLField("hero", arguments: ["episode": GraphQLVariable("episode")], type: .scalar(String.self))
63 | let variables: GraphQLMap = ["episode": nil as Optional]
64 | XCTAssertEqual(try field.cacheKey(with: variables), "hero")
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/ApolloTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/ApolloTests/MockNetworkTransport.swift:
--------------------------------------------------------------------------------
1 | import AWSAppSync
2 | import Dispatch
3 |
4 | public final class MockNetworkTransport: NetworkTransport {
5 | let body: JSONObject
6 |
7 | public init(body: JSONObject) {
8 | self.body = body
9 | }
10 |
11 | public func send(operation: Operation, completionHandler: @escaping (_ response: GraphQLResponse?, _ error: Error?) -> Void) -> Cancellable {
12 | DispatchQueue.global(qos: .default).async {
13 | completionHandler(GraphQLResponse(operation: operation, body: self.body), nil)
14 | }
15 | return MockTask()
16 | }
17 | }
18 |
19 | private final class MockTask: Cancellable {
20 | func cancel() {
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/ApolloTests/MutatingResultsTests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 | @testable import AWSAppSync
3 | import StarWarsAPI
4 |
5 | class MutatingResultsTests: XCTestCase {
6 | func testSettingNewFragment() throws {
7 | var hero = HeroNameWithFragmentAndIdQuery.Data.Hero.makeDroid(id: "2001", name: "R2-D2")
8 |
9 | let r2d2 = CharacterName.makeDroid(name: "Artoo")
10 |
11 | hero.fragments.characterName = r2d2
12 |
13 | XCTAssertEqual(hero.__typename, "Droid")
14 | XCTAssertEqual(hero.id, "2001")
15 | XCTAssertEqual(hero.name, "Artoo")
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/ApolloTests/TestCacheProvider.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 | @testable import AWSAppSync
3 |
4 | public protocol TestCacheProvider: AnyObject {
5 | static func withCache(initialRecords: RecordSet?, execute test: (NormalizedCache) throws -> ()) rethrows
6 | }
7 |
8 | public class InMemoryTestCacheProvider: TestCacheProvider {
9 | /// Execute a test block rather than return a cache synchronously, since cache setup may be
10 | /// asynchronous at some point.
11 | public static func withCache(initialRecords: RecordSet? = nil, execute test: (NormalizedCache) throws -> ()) rethrows {
12 | let cache = InMemoryNormalizedCache(records: initialRecords ?? [:])
13 | try test(cache)
14 | }
15 | }
16 |
17 | extension XCTestCase {
18 | public static var bundleDirectoryURL: URL {
19 | return Bundle(for: self).bundleURL.deletingLastPathComponent()
20 | }
21 |
22 | public static var cacheProviderClass: TestCacheProvider.Type {
23 | guard let cacheProviderClassName = ProcessInfo.processInfo.environment["APOLLO_TEST_CACHE_PROVIDER"] else {
24 | fatalError("Please define the APOLLO_TEST_CACHE_PROVIDER environment variable")
25 | }
26 |
27 | guard let cacheProviderClass = _typeByName(cacheProviderClassName) as? TestCacheProvider.Type else {
28 | fatalError("Could not load APOLLO_TEST_CACHE_PROVIDER \(cacheProviderClassName)")
29 | }
30 |
31 | return cacheProviderClass
32 | }
33 |
34 | public func withCache(initialRecords: RecordSet? = nil, execute test: (NormalizedCache) throws -> ()) rethrows {
35 | return try type(of: self).cacheProviderClass.withCache(initialRecords: initialRecords, execute: test)
36 | }
37 | }
38 |
39 | public class SQLiteTestCacheProvider: TestCacheProvider {
40 | public static func withCache(initialRecords: RecordSet? = nil, execute test: (NormalizedCache) throws -> ()) rethrows {
41 | return try withCache(initialRecords: initialRecords, fileURL: nil, execute: test)
42 | }
43 |
44 | /// Execute a test block rather than return a cache synchronously, since cache setup may be
45 | /// asynchronous at some point.
46 | public static func withCache(initialRecords: RecordSet? = nil, fileURL: URL? = nil, execute test: (NormalizedCache) throws -> ()) rethrows {
47 | let fileURL = fileURL ?? temporarySQLiteFileURL()
48 | let cache = try! AWSSQLiteNormalizedCache(fileURL: fileURL)
49 | if let initialRecords = initialRecords {
50 | _ = cache.merge(records: initialRecords) // This is synchronous
51 | }
52 | try test(cache)
53 | }
54 |
55 | public static func temporarySQLiteFileURL() -> URL {
56 | let applicationSupportPath = NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true).first!
57 | let applicationSupportURL = URL(fileURLWithPath: applicationSupportPath)
58 | let temporaryDirectoryURL = try! FileManager.default.url(
59 | for: .itemReplacementDirectory,
60 | in: .userDomainMask,
61 | appropriateFor: applicationSupportURL,
62 | create: true)
63 | return temporaryDirectoryURL.appendingPathComponent("db.sqlite3")
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/ApolloTests/XCTAssertHelpers.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 | @testable import AWSAppSync
3 |
4 | public func XCTAssertEqual(_ expression1: @autoclosure () throws -> [T?]?, _ expression2: @autoclosure () throws -> [T?]?, file: StaticString = #file, line: UInt = #line) rethrows {
5 | let optionalValue1 = try expression1()
6 | let optionalValue2 = try expression2()
7 |
8 | let message = {
9 | "(\"\(String(describing: optionalValue1))\") is not equal to (\"\(String(describing: optionalValue2))\")"
10 | }
11 |
12 | switch (optionalValue1, optionalValue2) {
13 | case (.none, .none):
14 | break
15 | case let (value1?, value2?):
16 | // FIXME: This ignores nil values in both lists, which is probably not what you want for true equality checking
17 | XCTAssertEqual(value1.compactMap { $0 }, value2.compactMap { $0 }, message(), file: file, line: line)
18 | default:
19 | XCTFail(message(), file: file, line: line)
20 | }
21 | }
22 |
23 | public func XCTAssertEqual(_ expression1: @autoclosure () throws -> [T : U]?, _ expression2: @autoclosure () throws -> [T : U]?, file: StaticString = #file, line: UInt = #line) rethrows {
24 | let optionalValue1 = try expression1()
25 | let optionalValue2 = try expression2()
26 |
27 | let message = {
28 | "(\"\(String(describing: optionalValue1))\") is not equal to (\"\(String(describing: optionalValue2))\")"
29 | }
30 |
31 | switch (optionalValue1, optionalValue2) {
32 | case (.none, .none):
33 | break
34 | case let (value1 as NSDictionary, value2 as NSDictionary):
35 | XCTAssertEqual(value1, value2, message(), file: file, line: line)
36 | default:
37 | XCTFail(message(), file: file, line: line)
38 | }
39 | }
40 |
41 | public func XCTAssertMatch(_ valueExpression: @autoclosure () throws -> Pattern.Base, _ patternExpression: @autoclosure () throws -> Pattern, file: StaticString = #file, line: UInt = #line) rethrows {
42 | let value = try valueExpression()
43 | let pattern = try patternExpression()
44 |
45 | let message = {
46 | "(\"\(value)\") does not match (\"\(pattern)\")"
47 | }
48 |
49 | if case pattern = value { return }
50 |
51 | XCTFail(message(), file: file, line: line)
52 | }
53 |
--------------------------------------------------------------------------------
/ApolloTests/XCTestCase+Promise.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 | @testable import AWSAppSync
3 |
4 | extension XCTestCase {
5 | public func awaitWith(_ promise: Promise) throws -> T {
6 | let expectation = self.expectation(description: "Expected promise to be resolved")
7 |
8 | promise.finally {
9 | expectation.fulfill()
10 | }
11 |
12 | waitForExpectations(timeout: 5)
13 |
14 | return try promise.result!.valueOrError()
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | ## Code of Conduct
2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).
3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact
4 | opensource-codeofconduct@amazon.com with any additional questions or comments.
5 |
--------------------------------------------------------------------------------
/Cartfile:
--------------------------------------------------------------------------------
1 | github "aws/aws-sdk-ios" ~> 2.36.0
2 | github "stephencelis/SQLite.swift" ~> 0.12.2
3 | github "aws-amplify/aws-appsync-realtime-client-ios" ~> 3.0.0
4 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | # frozen_string_literal: true
2 |
3 | source "https://rubygems.org"
4 | gem 'cocoapods', '1.11.3'
5 | gem 'cocoapods-downloader', '1.6.3'
6 |
--------------------------------------------------------------------------------
/Gemfile.lock:
--------------------------------------------------------------------------------
1 | GEM
2 | remote: https://rubygems.org/
3 | specs:
4 | CFPropertyList (3.0.7)
5 | base64
6 | nkf
7 | rexml
8 | activesupport (6.1.7.6)
9 | concurrent-ruby (~> 1.0, >= 1.0.2)
10 | i18n (>= 1.6, < 2)
11 | minitest (>= 5.1)
12 | tzinfo (~> 2.0)
13 | zeitwerk (~> 2.3)
14 | addressable (2.8.0)
15 | public_suffix (>= 2.0.2, < 5.0)
16 | algoliasearch (1.27.5)
17 | httpclient (~> 2.8, >= 2.8.3)
18 | json (>= 1.5.1)
19 | atomos (0.1.3)
20 | base64 (0.2.0)
21 | claide (1.1.0)
22 | cocoapods (1.11.3)
23 | addressable (~> 2.8)
24 | claide (>= 1.0.2, < 2.0)
25 | cocoapods-core (= 1.11.3)
26 | cocoapods-deintegrate (>= 1.0.3, < 2.0)
27 | cocoapods-downloader (>= 1.4.0, < 2.0)
28 | cocoapods-plugins (>= 1.0.0, < 2.0)
29 | cocoapods-search (>= 1.0.0, < 2.0)
30 | cocoapods-trunk (>= 1.4.0, < 2.0)
31 | cocoapods-try (>= 1.1.0, < 2.0)
32 | colored2 (~> 3.1)
33 | escape (~> 0.0.4)
34 | fourflusher (>= 2.3.0, < 3.0)
35 | gh_inspector (~> 1.0)
36 | molinillo (~> 0.8.0)
37 | nap (~> 1.0)
38 | ruby-macho (>= 1.0, < 3.0)
39 | xcodeproj (>= 1.21.0, < 2.0)
40 | cocoapods-core (1.11.3)
41 | activesupport (>= 5.0, < 7)
42 | addressable (~> 2.8)
43 | algoliasearch (~> 1.0)
44 | concurrent-ruby (~> 1.1)
45 | fuzzy_match (~> 2.0.4)
46 | nap (~> 1.0)
47 | netrc (~> 0.11)
48 | public_suffix (~> 4.0)
49 | typhoeus (~> 1.0)
50 | cocoapods-deintegrate (1.0.5)
51 | cocoapods-downloader (1.6.3)
52 | cocoapods-plugins (1.0.0)
53 | nap
54 | cocoapods-search (1.0.1)
55 | cocoapods-trunk (1.6.0)
56 | nap (>= 0.8, < 2.0)
57 | netrc (~> 0.11)
58 | cocoapods-try (1.2.0)
59 | colored2 (3.1.2)
60 | concurrent-ruby (1.2.2)
61 | escape (0.0.4)
62 | ethon (0.15.0)
63 | ffi (>= 1.15.0)
64 | ffi (1.15.5)
65 | fourflusher (2.3.1)
66 | fuzzy_match (2.0.4)
67 | gh_inspector (1.1.3)
68 | httpclient (2.8.3)
69 | i18n (1.14.1)
70 | concurrent-ruby (~> 1.0)
71 | json (2.6.1)
72 | minitest (5.19.0)
73 | molinillo (0.8.0)
74 | nanaimo (0.3.0)
75 | nap (1.1.0)
76 | netrc (0.11.0)
77 | nkf (0.2.0)
78 | public_suffix (4.0.6)
79 | rexml (3.3.9)
80 | ruby-macho (2.5.1)
81 | typhoeus (1.4.0)
82 | ethon (>= 0.9.0)
83 | tzinfo (2.0.6)
84 | concurrent-ruby (~> 1.0)
85 | xcodeproj (1.25.0)
86 | CFPropertyList (>= 2.3.3, < 4.0)
87 | atomos (~> 0.1.3)
88 | claide (>= 1.0.2, < 2.0)
89 | colored2 (~> 3.1)
90 | nanaimo (~> 0.3.0)
91 | rexml (>= 3.3.2, < 4.0)
92 | zeitwerk (2.6.11)
93 |
94 | PLATFORMS
95 | ruby
96 |
97 | DEPENDENCIES
98 | cocoapods (= 1.11.3)
99 | cocoapods-downloader (= 1.6.3)
100 |
101 | BUNDLED WITH
102 | 2.5.14
103 |
--------------------------------------------------------------------------------
/NOTICE:
--------------------------------------------------------------------------------
1 | AWS Mobile Events SDK for iOS
2 | Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 |
4 | This product includes the Apollo iOS GraphQL client library Copyright (c) 2016-2017 Meteor Development Group, Inc., licensed under the MIT license.
5 |
--------------------------------------------------------------------------------
/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "object": {
3 | "pins": [
4 | {
5 | "package": "AppSyncRealTimeClient",
6 | "repositoryURL": "https://github.com/aws-amplify/aws-appsync-realtime-client-ios.git",
7 | "state": {
8 | "branch": null,
9 | "revision": "9fd77b043e06193848a68740dfde0836648b2b4e",
10 | "version": "3.2.0"
11 | }
12 | },
13 | {
14 | "package": "AWSiOSSDKV2",
15 | "repositoryURL": "https://github.com/aws-amplify/aws-sdk-ios-spm.git",
16 | "state": {
17 | "branch": null,
18 | "revision": "3588f2d300e4a5cbeee09d37d795db94e6158112",
19 | "version": "2.36.2"
20 | }
21 | },
22 | {
23 | "package": "SQLite.swift",
24 | "repositoryURL": "https://github.com/stephencelis/SQLite.swift.git",
25 | "state": {
26 | "branch": null,
27 | "revision": "0a9893ec030501a3956bee572d6b4fdd3ae158a1",
28 | "version": "0.12.2"
29 | }
30 | },
31 | {
32 | "package": "Starscream",
33 | "repositoryURL": "https://github.com/daltoniam/Starscream",
34 | "state": {
35 | "branch": null,
36 | "revision": "c6bfd1af48efcc9a9ad203665db12375ba6b145a",
37 | "version": "4.0.8"
38 | }
39 | }
40 | ]
41 | },
42 | "version": 1
43 | }
44 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.3
2 | // The swift-tools-version declares the minimum version of Swift required to build this package.
3 |
4 | import PackageDescription
5 |
6 | let package = Package(
7 | name: "AWSAppSync",
8 | products: [
9 | .library(
10 | name: "AWSAppSync",
11 | targets: ["AWSAppSync"]),
12 | ],
13 | dependencies: [
14 | .package(
15 | name: "AWSiOSSDKV2",
16 | url: "https://github.com/aws-amplify/aws-sdk-ios-spm.git",
17 | .upToNextMinor(from: "2.36.0")
18 | ),
19 | .package(
20 | name: "AppSyncRealTimeClient",
21 | url: "https://github.com/aws-amplify/aws-appsync-realtime-client-ios.git",
22 | .upToNextMinor(from: "3.2.0")
23 | ),
24 | .package(
25 | url: "https://github.com/stephencelis/SQLite.swift.git",
26 | .upToNextMinor(from: "0.12.0")
27 | )
28 | ],
29 | targets: [
30 | .target(
31 | name: "AWSAppSync",
32 | dependencies: [
33 |
34 | .product(name: "SQLite", package: "SQLite.swift"),
35 | .product(name: "AppSyncRealTimeClient", package: "AppSyncRealTimeClient"),
36 | .product(name: "AWSCore", package: "AWSiOSSDKV2")
37 | ],
38 | path: "AWSAppSyncClient",
39 | exclude: [
40 | "Info.plist",
41 | "Apollo/Sources/Apollo/Info.plist"
42 | ]
43 | )
44 | ]
45 | )
46 |
--------------------------------------------------------------------------------
/Podfile:
--------------------------------------------------------------------------------
1 | platform :ios, "12.0"
2 |
3 | use_frameworks!
4 | inhibit_all_warnings!
5 |
6 | AWS_SDK_VERSION = "2.36.0"
7 |
8 | target "AWSAppSync" do
9 | pod "AWSCore", "~> #{AWS_SDK_VERSION}"
10 | pod "SQLite.swift", "~> 0.12.2"
11 | pod "AppSyncRealTimeClient", "~> 3.2.0"
12 |
13 | pod "SwiftLint"
14 | end
15 |
16 | target "AWSAppSyncTestCommon" do
17 | pod "AWSS3", "~> #{AWS_SDK_VERSION}"
18 | # We directly access a database connection to verify certain initialization
19 | # setups
20 | pod "SQLite.swift", "~> 0.12.2"
21 | pod "AppSyncRealTimeClient", "~> 3.2.0"
22 | end
23 |
24 | target "AWSAppSyncTestApp" do
25 | pod "AWSS3", "~> #{AWS_SDK_VERSION}"
26 | pod "AWSMobileClient", "~> #{AWS_SDK_VERSION}"
27 | end
28 |
29 | target "AWSAppSyncTestHostApp" do
30 | end
31 |
32 | target "AWSAppSyncUnitTests" do
33 | end
34 |
35 | target "AWSAppSyncIntegrationTests" do
36 | end
37 |
38 | target "ApolloTests" do
39 | pod "AWSCore", "~> #{AWS_SDK_VERSION}"
40 | end
41 |
--------------------------------------------------------------------------------
/Podfile.lock:
--------------------------------------------------------------------------------
1 | PODS:
2 | - AppSyncRealTimeClient (3.2.0):
3 | - Starscream (= 4.0.8)
4 | - AWSAuthCore (2.36.2):
5 | - AWSCore (= 2.36.2)
6 | - AWSCognitoIdentityProvider (2.36.2):
7 | - AWSCognitoIdentityProviderASF (= 2.36.2)
8 | - AWSCore (= 2.36.2)
9 | - AWSCognitoIdentityProviderASF (2.36.2):
10 | - AWSCore (= 2.36.2)
11 | - AWSCore (2.36.2)
12 | - AWSMobileClient (2.36.2):
13 | - AWSAuthCore (= 2.36.2)
14 | - AWSCognitoIdentityProvider (= 2.36.2)
15 | - AWSCognitoIdentityProviderASF (= 2.36.2)
16 | - AWSCore (= 2.36.2)
17 | - AWSS3 (2.36.2):
18 | - AWSCore (= 2.36.2)
19 | - SQLite.swift (0.12.2):
20 | - SQLite.swift/standard (= 0.12.2)
21 | - SQLite.swift/standard (0.12.2)
22 | - Starscream (4.0.8)
23 | - SwiftLint (0.55.0)
24 |
25 | DEPENDENCIES:
26 | - AppSyncRealTimeClient (~> 3.2.0)
27 | - AWSCore (~> 2.36.0)
28 | - AWSMobileClient (~> 2.36.0)
29 | - AWSS3 (~> 2.36.0)
30 | - SQLite.swift (~> 0.12.2)
31 | - SwiftLint
32 |
33 | SPEC REPOS:
34 | trunk:
35 | - AppSyncRealTimeClient
36 | - AWSAuthCore
37 | - AWSCognitoIdentityProvider
38 | - AWSCognitoIdentityProviderASF
39 | - AWSCore
40 | - AWSMobileClient
41 | - AWSS3
42 | - SQLite.swift
43 | - Starscream
44 | - SwiftLint
45 |
46 | SPEC CHECKSUMS:
47 | AppSyncRealTimeClient: 35c0d2ae28234a9f5daba5dc31402acf45ad802f
48 | AWSAuthCore: 0cb5d14bba49eaac2c4f470c3363a0dc93cbd5f0
49 | AWSCognitoIdentityProvider: 8c04f76b85b8c6e3463a36f01a2d3980404efc46
50 | AWSCognitoIdentityProviderASF: 39f47443203aefdc90d7758bad98fbecf9afbe8d
51 | AWSCore: 389e60725f6cd23873ffa6a6292c8f38a940fe11
52 | AWSMobileClient: aad2c8dfeea9806e72000d2be187f2680073a400
53 | AWSS3: eb7863c11386653894f722f27f540efb310db04c
54 | SQLite.swift: d2b4642190917051ce6bd1d49aab565fe794eea3
55 | Starscream: 19b5533ddb925208db698f0ac508a100b884a1b9
56 | SwiftLint: c5b49076d727b772434318c236548f0a1110c299
57 |
58 | PODFILE CHECKSUM: 5e218425b76767a14793a18c26000168492e6ce0
59 |
60 | COCOAPODS: 1.12.1
61 |
--------------------------------------------------------------------------------
/StarWarsAPI/CharacterAndSubTypesFragments.graphql:
--------------------------------------------------------------------------------
1 | fragment DroidNameAndPrimaryFunction on Droid {
2 | ...CharacterName
3 | ...DroidPrimaryFunction
4 | }
5 |
6 | fragment CharacterNameAndDroidPrimaryFunction on Character {
7 | ...CharacterName
8 | ...DroidPrimaryFunction
9 | }
10 |
11 | fragment CharacterNameAndDroidAppearsIn on Character {
12 | name
13 | ... on Droid {
14 | appearsIn
15 | }
16 | }
17 |
18 | fragment DroidName on Droid {
19 | name
20 | }
21 |
22 | fragment DroidPrimaryFunction on Droid {
23 | primaryFunction
24 | }
25 |
26 | fragment HumanHeightWithVariable on Human {
27 | height(unit: $heightUnit)
28 | }
29 |
30 | fragment CharacterNameAndAppearsInWithNestedFragments on Character {
31 | ...CharacterNameWithNestedAppearsInFragment
32 | }
33 |
34 | fragment CharacterNameWithNestedAppearsInFragment on Character {
35 | name
36 | ...CharacterAppearsIn
37 | }
38 |
39 | fragment CharacterNameWithInlineFragment on Character {
40 | ... on Human {
41 | friends {
42 | appearsIn
43 | }
44 | }
45 |
46 | ... on Droid {
47 | ...CharacterName
48 | ...FriendsNames
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/StarWarsAPI/CreateReviewForEpisode.graphql:
--------------------------------------------------------------------------------
1 | mutation CreateReviewForEpisode($episode: Episode!, $review: ReviewInput!) {
2 | createReview(episode: $episode, review: $review) {
3 | stars
4 | commentary
5 | }
6 | }
7 |
8 | mutation CreateAwesomeReview {
9 | createReview(episode: JEDI, review: { stars: 10, commentary: "This is awesome!" }) {
10 | stars
11 | commentary
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/StarWarsAPI/HeroAndFriendsNames.graphql:
--------------------------------------------------------------------------------
1 | query HeroAndFriendsNames($episode: Episode) {
2 | hero(episode: $episode) {
3 | name
4 | friends {
5 | name
6 | }
7 | }
8 | }
9 |
10 | query HeroAndFriendsNamesWithIDs($episode: Episode) {
11 | hero(episode: $episode) {
12 | id
13 | name
14 | friends {
15 | id
16 | name
17 | }
18 | }
19 | }
20 |
21 | query HeroAndFriendsNamesWithIDForParentOnly($episode: Episode) {
22 | hero(episode: $episode) {
23 | id
24 | name
25 | friends {
26 | name
27 | }
28 | }
29 | }
30 |
31 | query HeroAndFriendsNamesWithFragment($episode: Episode) {
32 | hero(episode: $episode) {
33 | name
34 | ...FriendsNames
35 | }
36 | }
37 |
38 | query HeroAndFriendsNamesWithFragmentTwice($episode: Episode) {
39 | hero(episode: $episode) {
40 | friends {
41 | ...CharacterName
42 | }
43 | ... on Droid {
44 | friends {
45 | ...CharacterName
46 | }
47 | }
48 | }
49 | }
50 |
51 | fragment FriendsNames on Character {
52 | friends {
53 | name
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/StarWarsAPI/HeroAppearsIn.graphql:
--------------------------------------------------------------------------------
1 | query HeroAppearsIn {
2 | hero {
3 | appearsIn
4 | }
5 | }
6 |
7 | query HeroAppearsInWithFragment($episode: Episode) {
8 | hero(episode: $episode) {
9 | ...CharacterAppearsIn
10 | }
11 | }
12 |
13 | fragment CharacterAppearsIn on Character {
14 | appearsIn
15 | }
16 |
--------------------------------------------------------------------------------
/StarWarsAPI/HeroConditional.graphql:
--------------------------------------------------------------------------------
1 | query HeroNameConditionalExclusion($skipName: Boolean!) {
2 | hero {
3 | name @skip(if: $skipName)
4 | }
5 | }
6 |
7 | query HeroNameConditionalInclusion($includeName: Boolean!) {
8 | hero {
9 | name @include(if: $includeName)
10 | }
11 | }
12 |
13 | query HeroNameConditionalBoth($skipName: Boolean!, $includeName: Boolean!) {
14 | hero {
15 | name @skip(if: $skipName) @include(if: $includeName)
16 | }
17 | }
18 |
19 | query HeroNameConditionalBothSeparate($skipName: Boolean!, $includeName: Boolean!) {
20 | hero {
21 | name @skip(if: $skipName)
22 | name @include(if: $includeName)
23 | }
24 | }
25 |
26 | query HeroDetailsInlineConditionalInclusion($includeDetails: Boolean!) {
27 | hero {
28 | ... @include(if: $includeDetails) {
29 | name
30 | appearsIn
31 | }
32 | }
33 | }
34 |
35 | query HeroDetailsFragmentConditionalInclusion($includeDetails: Boolean!) {
36 | hero {
37 | ...HeroDetails @include(if: $includeDetails)
38 | }
39 | }
40 |
41 | query HeroNameTypeSpecificConditionalInclusion($episode: Episode, $includeName: Boolean!) {
42 | hero(episode: $episode) {
43 | name @include(if: $includeName)
44 | ... on Droid {
45 | name
46 | }
47 | }
48 | }
49 |
50 | query HeroFriendsDetailsConditionalInclusion($includeFriendsDetails: Boolean!) {
51 | hero {
52 | friends @include(if: $includeFriendsDetails) {
53 | name
54 | ... on Droid {
55 | primaryFunction
56 | }
57 | }
58 | }
59 | }
60 |
61 | query HeroFriendsDetailsUnconditionalAndConditionalInclusion($includeFriendsDetails: Boolean!) {
62 | hero {
63 | friends {
64 | name
65 | }
66 | friends @include(if: $includeFriendsDetails) {
67 | name
68 | ... on Droid {
69 | primaryFunction
70 | }
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/StarWarsAPI/HeroDetails.graphql:
--------------------------------------------------------------------------------
1 | query HeroDetails($episode: Episode) {
2 | hero(episode: $episode) {
3 | name
4 | ... on Human {
5 | height
6 | }
7 | ... on Droid {
8 | primaryFunction
9 | }
10 | }
11 | }
12 |
13 | query HeroDetailsWithFragment($episode: Episode) {
14 | hero(episode: $episode) {
15 | ...HeroDetails
16 | }
17 | }
18 |
19 | query DroidDetailsWithFragment($episode: Episode) {
20 | hero(episode: $episode) {
21 | ...DroidDetails
22 | }
23 | }
24 |
25 | fragment HeroDetails on Character {
26 | name
27 | ... on Human {
28 | height
29 | }
30 | ... on Droid {
31 | primaryFunction
32 | }
33 | }
34 |
35 | fragment DroidDetails on Droid {
36 | name
37 | primaryFunction
38 | }
39 |
--------------------------------------------------------------------------------
/StarWarsAPI/HeroFriendsOfFriends.graphql:
--------------------------------------------------------------------------------
1 | query HeroFriendsOfFriendsNames($episode: Episode) {
2 | hero(episode: $episode) {
3 | friends {
4 | id
5 | friends {
6 | name
7 | }
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/StarWarsAPI/HeroName.graphql:
--------------------------------------------------------------------------------
1 | query HeroName($episode: Episode) {
2 | hero(episode: $episode) {
3 | name
4 | }
5 | }
6 |
7 | query HeroNameWithID($episode: Episode) {
8 | hero(episode: $episode) {
9 | id
10 | name
11 | }
12 | }
13 |
14 | query HeroNameWithFragment($episode: Episode) {
15 | hero(episode: $episode) {
16 | ...CharacterName
17 | }
18 | }
19 |
20 | query HeroNameWithFragmentAndID($episode: Episode) {
21 | hero(episode: $episode) {
22 | id
23 | ...CharacterName
24 | }
25 | }
26 |
27 | fragment CharacterName on Character {
28 | name
29 | }
30 |
--------------------------------------------------------------------------------
/StarWarsAPI/HeroNameAndAppearsIn.graphql:
--------------------------------------------------------------------------------
1 | query HeroNameAndAppearsInWithFragment($episode: Episode) {
2 | hero(episode: $episode) {
3 | ...CharacterNameAndAppearsIn
4 | }
5 | }
6 |
7 | fragment CharacterNameAndAppearsIn on Character {
8 | name
9 | appearsIn
10 | }
11 |
--------------------------------------------------------------------------------
/StarWarsAPI/HeroParentTypeDependentField.graphql:
--------------------------------------------------------------------------------
1 | query HeroParentTypeDependentField($episode: Episode) {
2 | hero(episode: $episode) {
3 | name
4 | ... on Human {
5 | friends {
6 | name
7 | ... on Human {
8 | height(unit: FOOT)
9 | }
10 | }
11 | }
12 | ... on Droid {
13 | friends {
14 | name
15 | ... on Human {
16 | height(unit: METER)
17 | }
18 | }
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/StarWarsAPI/HeroTypeDependentAliasedField.graphql:
--------------------------------------------------------------------------------
1 | query HeroTypeDependentAliasedField($episode: Episode) {
2 | hero(episode: $episode) {
3 | ... on Human {
4 | property: homePlanet
5 | }
6 | ... on Droid {
7 | property: primaryFunction
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/StarWarsAPI/Human.graphql:
--------------------------------------------------------------------------------
1 | query Human($id: ID!) {
2 | human(id: $id) {
3 | name
4 | mass
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/StarWarsAPI/HumanFriendsFilteredById.graphql:
--------------------------------------------------------------------------------
1 | query HumanFriendsFilteredById($id: ID!, $friendId: ID = null) {
2 | human(id: $id) {
3 | name
4 | mass
5 | friendsFilteredById(id: $friendId) {
6 | id
7 | name
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/StarWarsAPI/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | $(CURRENT_PROJECT_VERSION)
21 |
22 |
23 |
--------------------------------------------------------------------------------
/StarWarsAPI/SameHeroTwice.graphql:
--------------------------------------------------------------------------------
1 | query SameHeroTwice {
2 | hero {
3 | name
4 | }
5 | r2: hero {
6 | appearsIn
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/StarWarsAPI/StarWarsAPI.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | //! Project version number for StarWarsAPI.
4 | FOUNDATION_EXPORT double StarWarsAPIVersionNumber;
5 |
6 | //! Project version string for StarWarsAPI.
7 | FOUNDATION_EXPORT const unsigned char StarWarsAPIVersionString[];
8 |
9 | // In this header, you should import all the public headers of your framework using statements like #import
10 |
--------------------------------------------------------------------------------
/StarWarsAPI/Starship.graphql:
--------------------------------------------------------------------------------
1 | query Starship {
2 | starship(id: 3000) {
3 | name
4 | coordinates
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/StarWarsAPI/SubscribeReview.graphql:
--------------------------------------------------------------------------------
1 | subscription ReviewAdded($episode: Episode) {
2 | reviewAdded(episode: $episode) {
3 | episode
4 | stars
5 | commentary
6 | }
7 | }
8 |
9 |
--------------------------------------------------------------------------------
/StarWarsAPI/TwoHeroes.graphql:
--------------------------------------------------------------------------------
1 | query TwoHeroes {
2 | r2: hero {
3 | name
4 | }
5 | luke: hero(episode: EMPIRE) {
6 | name
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/build-support/build-xcframeworks.sh:
--------------------------------------------------------------------------------
1 | set -euo pipefail
2 |
3 | framework=$@
4 | pwd=$(pwd)
5 | ios_device_archive_path="$pwd/build/iOS/AWSAppSync"
6 | ios_simulator_archive_path="$pwd/build/Simulator/AWSAppSync"
7 | xcframework_path="$pwd/build/$framework.xcframework"
8 |
9 | if [ -d "$xcframework_path" ]
10 | then
11 | echo "XCFramework exists already, skipping."
12 | exit 0
13 | fi
14 |
15 | # archive for device
16 | xcodebuild archive -workspace AWSAppSyncClient.xcworkspace \
17 | -scheme $framework \
18 | -destination "generic/platform=iOS" \
19 | -archivePath $ios_device_archive_path \
20 | -quiet \
21 | SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES
22 |
23 |
24 | # archive for simulator
25 | xcodebuild archive -workspace AWSAppSyncClient.xcworkspace \
26 | -scheme $framework \
27 | -destination "generic/platform=iOS Simulator" \
28 | -archivePath $ios_simulator_archive_path \
29 | -quiet \
30 | SKIP_INSTALL=NO BUILD_LIBRARY_FOR_DISTRIBUTION=YES
31 |
32 | # create xcframework
33 | xcodebuild -create-xcframework \
34 | -framework "$ios_device_archive_path.xcarchive/Products/Library/Frameworks/$framework.framework" \
35 | -debug-symbols "$ios_device_archive_path.xcarchive/dSYMs/$framework.framework.dSYM" \
36 | -framework "$ios_simulator_archive_path.xcarchive/Products/Library/Frameworks/$framework.framework" \
37 | -debug-symbols "$ios_simulator_archive_path.xcarchive/dSYMs/$framework.framework.dSYM" \
38 | -output "$xcframework_path"
39 |
40 | cd build && zip -q -r AWSAppSync.xcframework.zip AWSAppSync.xcframework
41 |
42 |
--------------------------------------------------------------------------------
/build-support/cocoapods_release.sh:
--------------------------------------------------------------------------------
1 | bundle exec pod trunk push AWSAppSync.podspec --allow-warnings
2 |
--------------------------------------------------------------------------------
/build-support/stage_sdk_release.sh:
--------------------------------------------------------------------------------
1 | if [ -z $1 ] || [ -z $2 ]; then
2 | echo "Usage: ./$0 [current_sdk_version] [new_sdk_version]"
3 | exit 1
4 | fi
5 |
6 | export LC_CTYPE=C LANG=C
7 | echo "Bumping version from $1 to $2"
8 | find . -name 'AWSAppSync.podspec' -print0 | xargs -0 sed -i '' -e "s/$1/$2/g"
9 | find . -path '*AWSAppSyncClient/*.plist' -print0 | xargs -0 sed -i '' -e "s/$1/$2/g"
10 | find . -path '*AWSAppSyncClient/AWSAppSyncHTTPNetworkTransport.swift' -print0 | xargs -0 sed -i '' -e "s/$1/$2/g"
11 | sed -i '' -e "s/$1/$2/g" README.md
12 |
13 | echo "SDK version replaced in podspec, info.plist, network transport.\n\n"
14 |
15 | echo "Adding git commit and tag..."
16 | git add -u && git commit -m "Release SDK version $2"
17 | git tag -a "$2" -m "$2"
18 |
--------------------------------------------------------------------------------
/build-support/update_sdk_deps.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | if [[ -z "$1" ]] || [[ -z "$2" ]]; then
4 | echo "Usage: $0 " >&2
5 | exit 1
6 | fi
7 |
8 | echo "Bumping version from $1 to $2"
9 | find . -name 'AWSAppSync.podspec' -print0 | xargs -0 sed -i '' -e "s/$1/$2/g"
10 | find . -name 'Podfile' -print0 | xargs -0 sed -i '' -e "s/$1/$2/g"
11 | find . -name 'Cartfile' -print0 | xargs -0 sed -i '' -e "s/$1/$2/g"
12 | sed -i '' -e "s/$1/$2/g" Package.swift
13 | sed -i '' -e "s/$1/$2/g" README.md
14 |
15 | echo "SDK dependency updated in podspec, Package.swift, Podfile, and Cartfile."
16 | echo "Review the diff. If it looks correct, run 'swift package resolve && pod update'"
17 | echo "Build and test, then commit, and create a PR."
18 |
19 |
--------------------------------------------------------------------------------