Improve documentation on using AWSMobileClient with AWSAppSyncClient
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:
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.
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
idTokenis not valid, and a refresh token is available, refresh token is used to get a newidToken. 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
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.
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.
@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.
I've updated the documentation to use UIApplication.LaunchOptionsKey. Please let me know if you've got any other questions.
@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
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.
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.
Thanks for the feedback @zigfreid5.
Tagging @nikhil-dabhade for visibility.
@helloniklas thanks for opening this issue and @palpatim thank you for your help
Adding "feature request" label for @nikhil-dabhade visibility
Any update on this ? Following the step by step documentation fails because of this issue. It has been opened 6+ months ago
@sebsto : Can you point to the doc link which you are using to follow the steps?
@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 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.
Great. I would love to be wrong on this one and to have an opportunity to simplify my code.
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 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 thanks for fast answer, that seems to have done it...
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?
Thanks for this thread! Saved me. The above code suggestion worked for me with AWSAppSync 3.0.0 also.
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.
I am afraid the workaround above didn't work for me and the documentation doesn't seem to be updated yet. Can anyone help?
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 ?
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.