I'm trying to implement the new AWS Cognito User Pools in my iOS (Swift) app, but I'm struggling to get the sign in process to work. I am essentially trying to follow the example available here.
This is what I have so far:
AppDelegate:
class AppDelegate: UIResponder, UIApplicationDelegate, AWSCognitoIdentityInteractiveAuthenticationDelegate {
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let serviceConfiguration = AWSServiceConfiguration(region: AWSRegionType.USEast1, credentialsProvider: nil)
AWSServiceManager.defaultServiceManager().defaultServiceConfiguration = serviceConfiguration
let configurationUserPool = AWSCognitoIdentityUserPoolConfiguration(
clientId: "###",
clientSecret: "#########",
poolId: "###")
AWSCognitoIdentityUserPool.registerCognitoIdentityUserPoolWithConfiguration(serviceConfiguration, userPoolConfiguration: configurationUserPool, forKey: "UserPool")
self.userPool = AWSCognitoIdentityUserPool(forKey: "UserPool")
self.userPool!.delegate = self
return true
}
func startPasswordAuthentication() -> AWSCognitoIdentityPasswordAuthentication {
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let logInNavigationController = mainStoryboard.instantiateViewControllerWithIdentifier("LogInNavigationController") as! UINavigationController
dispatch_async(dispatch_get_main_queue(), {
self.window?.rootViewController = logInNavigationController
})
let logInViewController = mainStoryboard.instantiateViewControllerWithIdentifier("LogInViewController") as! LogInViewController
return logInViewController
}
}
LogInViewController:
class LogInViewController: UIViewController, AWSCognitoIdentityPasswordAuthentication {
var usernameText : String?
var passwordAuthenticationCompletion = AWSTaskCompletionSource()
func getPasswordAuthenticationDetails(authenticationInput: AWSCognitoIdentityPasswordAuthenticationInput, passwordAuthenticationCompletionSource: AWSTaskCompletionSource) {
self.passwordAuthenticationCompletion = passwordAuthenticationCompletionSource
dispatch_async(dispatch_get_main_queue(), {
if self.usernameText == nil {
self.usernameText = authenticationInput.lastKnownUsername
}
})
}
func didCompletePasswordAuthenticationStepWithError(error: NSError) {
dispatch_async(dispatch_get_main_queue(), {
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let mainNavigationController = mainStoryboard.instantiateViewControllerWithIdentifier("MainNavigationController") as! UINavigationController
(UIApplication.sharedApplication().delegate as! AppDelegate).window?.rootViewController = mainNavigationController
})
}
func logInButtonPressed() {
self.passwordAuthenticationCompletion.setResult(AWSCognitoIdentityPasswordAuthenticationDetails(username: emailTextField.text, password: passwordTextField.text))
}
}
Nothing seems to happen when I hit the log in button, although if I hit it again I get an NSInternalInconsistencyException (which I believe is because the AWSTask result has already been set).
Any help with this would be appreciated. I am using the AWS SDK for iOS version 2.4.1.
UPDATE:
Not a solution to my original problem, but I've been able to get User Pools working by using the explicit sign in method rather than the delegate method (see this page for details). Here is the code from my SignInViewController:
class SignInViewController: UIViewController {
@IBAction func signInButtonTouched(sender: UIButton) {
if (emailTextField.text != nil) && (passwordTextField.text != nil) {
let user = (UIApplication.sharedApplication().delegate as! AppDelegate).userPool!.getUser(emailTextField.text!)
user.getSession(emailTextField.text!, password: passwordTextField.text!, validationData: nil, scopes: nil).continueWithExecutor(AWSExecutor.mainThreadExecutor(), withBlock: {
(task:AWSTask!) -> AnyObject! in
if task.error == nil {
// user is logged in - show logged in UI
} else {
// error
}
return nil
})
} else {
// email or password not set
}
}
}
Then, to consume an AWS service (which in my case is located in a different region to Cognito) I have created a new Credentials Provider using the User Pool:
let credentialsProvider = AWSCognitoCredentialsProvider(regionType: .USEast1, identityPoolId: "###", identityProviderManager: (UIApplication.sharedApplication().delegate as! AppDelegate).userPool!)
let serviceConfiguration = AWSServiceConfiguration(region: .APNortheast1, credentialsProvider: credentialsProvider)
AWSLambdaInvoker.registerLambdaInvokerWithConfiguration(serviceConfiguration, forKey: "Lambda")
let lambdaInvoker = AWSLambdaInvoker(forKey: "Lambda")
One additional issue is that I was seeing this error each time I launched the app: "Could not find valid 'AWSDefaultRegionType', 'AWSCognitoRegionType', and 'AWSCognitoIdentityPoolId' values in info.plist.". This seems to be related to Fabric, which I am using to track crashes. I've solved this by changing this line in the AppDelegate:
Fabric.with([AWSCognito.self, Crashlytics.self])
to this:
Fabric.with([Crashlytics.self])
I hope this helps someone else.