aws-mobile-appsync-sdk-ios icon indicating copy to clipboard operation
aws-mobile-appsync-sdk-ios copied to clipboard

Improve documentation on using AWSMobileClient with AWSAppSyncClient

Open helloniklas opened this issue 6 years ago • 24 comments

State your question @palpatim After initialising the AWSMobileClient and being signed in. I then want to set up the AWSAppSyncClient. Using the code from Amplify docs says it's deprecated. But using new method call still says Error initializing appsync client. invalidAuthConfiguration("AWSCognitoUserPoolsAuthProvider cannot be nil.")

    AWSMobileClient.sharedInstance().initialize { (userState, error) in
         self.resolveUserState(userState, error)
     }
       
    func resolveUserState(_ userState: UserState?,  _ error: Error?) {
        guard let userState = userState else {
            return
        }
        
        switch userState {
        case .signedIn:
            print("singned in")
            initializeAppSyncClient()
        case .signedOut:
            print("signed out")
            destroyAppSyncClient()
            AWSMobileClient.sharedInstance().showSignIn(navigationController: getNavigationController()) { (result, error) in
                self.resolveUserState(userState: result, error: error)
            }
        default:
            AWSMobileClient.sharedInstance().signOut()
        }
        
    }

    func initializeAppSyncClient() {
        do {
            let appSyncConfig = try AWSAppSyncClientConfiguration(appSyncServiceConfig: AWSAppSyncServiceConfig())
            appSyncClient = try AWSAppSyncClient(appSyncConfig: appSyncConfig)
            appSyncClient?.apolloClient?.cacheKeyForObject = { $0["id"] }
        } catch {
            print("Error initializing appsync client. \(error)")
        }

    }

Environment(please complete the following information):

  • AppSync SDK Version: [e.g. 2.6.21] 2.9.1
  • Dependency Manager: [e.g. Cocoapods, Carthage] Cocoapods
  • Swift Version : [e.g. 4.0] 4.0

Device Information (please complete the following information):

  • Device: [e.g. iPhone6, Simulator] Simulator
  • iOS Version: [e.g. iOS 11.4] iOS12
  • Specific to simulators:

helloniklas avatar Jan 17 '19 17:01 helloniklas

For it to work I have to do:

            let appSyncConfig = try AWSAppSyncClientConfiguration(appSyncServiceConfig: AWSAppSyncServiceConfig(), userPoolsAuthProvider: MyCognitoUserPoolsAuthProvider())

But my understanding is that AWSMobileClient should deal with all the refresh token flow etc.

helloniklas avatar Jan 17 '19 17:01 helloniklas

That depends on the implementation of MyCognitoUserPoolsAuthProvider. For example, if it calls AWSMobileClient.sharedInstance().getTokens() then the mobile client will take care of handling refresh, etc. From the getTokens method:

Returns cached UserPools auth JWT tokens if valid. If the idToken is not valid, and a refresh token is available, refresh token is used to get a new idToken. If there is no refresh token and the user is signed in, a notification is dispatched to indicate requirement of user to re-signin. The call to wait will be synchronized so that if multiple threads call this method, they will block till the first thread gets the token.

So you might expect an implementation to look something like:

class MyCognitoUserPoolsAuthProvider : AWSCognitoUserPoolsAuthProviderAsync {
    func getLatestAuthToken(_ callback: @escaping (String?, Error?) -> Void) {
        AWSMobileClient.sharedInstance().getTokens { (tokens, error) in
            if error != nil {
                callback(nil, error)
            } else {
                callback(tokens?.idToken?.tokenString, nil)
            }
        }
    }
}

Or even, if you just want to use MobileClient directly:

// Instead of a separate class, declare a direct conformance
extension AWSMobileClient: AWSCognitoUserPoolsAuthProviderAsync {
    public func getLatestAuthToken(_ callback: @escaping (String?, Error?) -> Void) {
        getTokens { (tokens, error) in
            if error != nil {
                callback(nil, error)
            } else {
                callback(tokens?.idToken?.tokenString, nil)
            }
        }
    }
}

// And then when you're ready to initialize the AppSync client

func initializeAppSyncClient() {
    let appSyncConfig = try AWSAppSyncClientConfiguration(
        appSyncServiceConfig: AWSAppSyncServiceConfig(),
        userPoolsAuthProvider: AWSMobileClient.sharedInstance(),
        databaseURL: databaseURL
    )
    appSyncClient = try AWSAppSyncClient(appSyncConfig: appSyncConfig)
    appSyncClient?.apolloClient?.cacheKeyForObject = { $0["id"] }
}

Hope this helps

palpatim avatar Jan 17 '19 20:01 palpatim

Ok great that helps a lot... These steps are missing from the AWS Amplify step by step tutorial when following the Add auth stuff. I thought maybe there was a way to just send the AWSMobileClient.sharedInstance() into the AWSAppSyncClientConfiguration then this would just be taken care of.

helloniklas avatar Jan 17 '19 21:01 helloniklas

Thanks for the feedback--I'm going to recharacterize this issue as a feature request to improve our documentation, since it is a bit difficult to follow the auth flow setting up an AppSync client using Mobile Client.

palpatim avatar Jan 18 '19 00:01 palpatim

@helloniklas I've edited my answers above to use async rather than sync methods--sorry for the confusion. I'll leave the feature request the same though.

palpatim avatar Jan 18 '19 16:01 palpatim

I've updated the documentation to use UIApplication.LaunchOptionsKey. Please let me know if you've got any other questions.

palpatim avatar Jan 18 '19 16:01 palpatim

@palpatim Thanks... but it says AWSAppSyncClientInfo is depreciated.

'AWSAppSyncClientInfo' is deprecated: Use a AWSAppSyncServiceConfigProviding instance like AWSAppSyncServiceConfig

The only method I could find that takes the userPoolsAuthProvider is:

    private func initializeAppSyncClient() {
        do {
            let appSyncConfig = try AWSAppSyncClientConfiguration(appSyncServiceConfig: AWSAppSyncServiceConfig(), userPoolsAuthProvider: AWSMobileClient.sharedInstance())
            appSyncClient = try AWSAppSyncClient(appSyncConfig: appSyncConfig)
            appSyncClient?.apolloClient?.cacheKeyForObject = { $0["id"] }
        } catch {
            print("Error initializing appsync client. \(error)")
        }
    }

Is this the way to do it?

helloniklas avatar Jan 18 '19 17:01 helloniklas

@helloniklas

I'm so sorry for the continued trouble here--my test project was using an older version of the library and didn't pick up the deprecation warning. After updating and confirming the deprecation, I can confirm that you have the right pattern:

let appSyncConfig = try AWSAppSyncClientConfiguration(appSyncServiceConfig: AWSAppSyncServiceConfig(), userPoolsAuthProvider: AWSMobileClient.sharedInstance())

I've updated the docs (again) with the proper config providing type, and I've also edited my response above so future folks can copy/paste correct code.

palpatim avatar Jan 19 '19 00:01 palpatim

These code examples for different situations are exactly what should be in the documentation. There are so many things with AWS that are so poorly documented that the best resources are random blogs and articles. Adding more code samples and example projects with varied setups would greatly improve the developer onboarding experience.

zigfreid5 avatar Jan 22 '19 18:01 zigfreid5

Thanks for the feedback @zigfreid5.

Tagging @nikhil-dabhade for visibility.

palpatim avatar Jan 22 '19 21:01 palpatim

@helloniklas thanks for opening this issue and @palpatim thank you for your help

srstomp avatar Feb 06 '19 11:02 srstomp

Adding "feature request" label for @nikhil-dabhade visibility

palpatim avatar Feb 06 '19 17:02 palpatim

Any update on this ? Following the step by step documentation fails because of this issue. It has been opened 6+ months ago

sebsto avatar Oct 22 '19 08:10 sebsto

@sebsto : Can you point to the doc link which you are using to follow the steps?

nikhil-dabhade avatar Oct 22 '19 15:10 nikhil-dabhade

@nikhil-dabhade

The link is here https://aws-amplify.github.io/docs/ios/start#step-4-integrate-into-your-app

To make it work, I folowed the instructions above on this thread and created an extension to ASMobileClient https://github.com/sebsto/amplify-ios-workshop/blob/master/Landmarks/AppDelegate.swift#L233

and add a reference to AWSMobileClient when creating the AppSyncClient https://github.com/sebsto/amplify-ios-workshop/blob/master/Landmarks/AppDelegate.swift#L156

sebsto avatar Oct 22 '19 15:10 sebsto

@sebsto Thanks for sharing. I will try to reproduce this on a clean set up and revert here. I worked with the same link last week and did not run in this issue so want to do it on a clean setup.

nikhil-dabhade avatar Oct 22 '19 16:10 nikhil-dabhade

Great. I would love to be wrong on this one and to have an opportunity to simplify my code.

sebsto avatar Oct 22 '19 17:10 sebsto

This has now stopped working after updating Amplify to 2.12.1.

The line let appSyncConfig = try AWSAppSyncClientConfiguration(appSyncServiceConfig: AWSAppSyncServiceConfig(), userPoolsAuthProvider: AWSMobileClient.default())

Does not work anymore.

helloniklas avatar Nov 19 '19 15:11 helloniklas

@helloniklas Can you try the below ? This is the workaround proposed above. Tested with AWSAppSync 2.15

    func appSyncInit() {
        do {
            // You can choose the directory in which AppSync stores its persistent cache databases
            let cacheConfiguration = try AWSAppSyncCacheConfiguration()

            // AppSync configuration & client initialization
            let appSyncServiceConfig = try AWSAppSyncServiceConfig()
            let appSyncConfig = try AWSAppSyncClientConfiguration(appSyncServiceConfig: appSyncServiceConfig,
                                                                  userPoolsAuthProvider: AWSMobileClient.default() as AWSCognitoUserPoolsAuthProvider,
                                                                  cacheConfiguration: cacheConfiguration)
            self.appSyncClient = try AWSAppSyncClient(appSyncConfig: appSyncConfig)
            print("Initialized appsync client.")
        } catch {
            print("Error initializing appsync client. \(error)")
        }
    }

and

// Make sure AWSMobileClient is a Cognito User Pool credentails providers
// this makes it easy to AWSMobileClient shared instance with AppSync Client
// read https://github.com/awslabs/aws-mobile-appsync-sdk-ios/issues/157 for details
extension AWSMobileClient: AWSCognitoUserPoolsAuthProviderAsync {
    public func getLatestAuthToken(_ callback: @escaping (String?, Error?) -> Void) {
        getTokens { (tokens, error) in
            if error != nil {
                callback(nil, error)
            } else {
                callback(tokens?.idToken?.tokenString, nil)
            }
        }
    }
}

sebsto avatar Nov 19 '19 15:11 sebsto

@sebsto thanks for fast answer, that seems to have done it...

helloniklas avatar Nov 19 '19 16:11 helloniklas

Error initializing appsync client. invalidAuthConfiguration("API_KEY is selected in configuration but other providers are passed.")

What to do with this error? Can someone please help me?

zgao67 avatar Dec 06 '19 21:12 zgao67

Thanks for this thread! Saved me. The above code suggestion worked for me with AWSAppSync 3.0.0 also.

cabeaulac avatar Jan 18 '20 20:01 cabeaulac

Hi,

I think it's very apparent that the documentation lacks clear instructions for initialization and using AWSMobileClient along with the AWSAppSync in tandem.

You guys are amazing and have massively helped us developers to create apps but please look into updating the documentation and providing samples where the most commonly used configurations are implemented together.

I am thankful I found this thread and have completed my setup of the AWSMobileClient and AWSAppSync but without it, I'd probably would've been scratching my head for days and probably be demotivated into trying out these amazing products.

n1schal avatar Feb 18 '20 12:02 n1schal

I am afraid the workaround above didn't work for me and the documentation doesn't seem to be updated yet. Can anyone help?

czaku avatar Apr 15 '20 11:04 czaku

Hi @palpatim I am trying to implement this

class MyCognitoUserPoolsAuthProvider : AWSCognitoUserPoolsAuthProviderAsync {
        func getLatestAuthToken(_ callback: @escaping (String?, Error?) -> Void) {
            AWSMobileClient.default().getTokens { (tokens, error) in
                if error != nil {
                    callback(nil, error)
                } else {
                    callback(tokens?.idToken?.tokenString, nil)
            }
        }
    }
}

but XCode give me this error

Type 'AWSMobileClient' does not conform to protocol 'AWSCognitoUserPoolsAuthProviderAsync'

and the implementation is exactly like the documentation here https://docs.amplify.aws/sdk/api/graphql/q/platform/ios/#authorization-modes

Can you help ?

GonzalezEvelio avatar Sep 27 '23 21:09 GonzalezEvelio

Thank you for opening this issue. AWS AppSync SDK for iOS entered maintenance mode in September 2023 and will receive no further updates as of September 2024.

Please use Amplify Swift going forward. For information on upgrading to Amplify Swift, refer to the Upgrade from AppSync SDK documentation.

atierian avatar Dec 07 '23 20:12 atierian