├── .gitignore
├── Podfile
├── CognitoApplication.xcodeproj
├── project.xcworkspace
│ └── contents.xcworkspacedata
└── project.pbxproj
├── CognitoApplication.xcworkspace
└── contents.xcworkspacedata
├── CognitoApplication
├── Assets.xcassets
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── Info.plist
├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
├── MultiFactorAuthenticationController.swift
├── VerificationViewController.swift
├── ResetPasswordViewController.swift
├── CognitoConfig.swift
├── ForgotPasswordViewController.swift
├── LoginViewController.swift
├── AppViewController.swift
├── SignupViewController.swift
└── AppDelegate.swift
├── LICENSE
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | Podfile.lock
2 | Pods
3 | CognitoApplication.xcodeproj/project.xcworkspace/xcuserdata
4 | CognitoApplication.xcodeproj/xcuserdata
5 | CognitoApplication.xcworkspace/xcuserdata
6 | CognitoConfig.plist
7 |
--------------------------------------------------------------------------------
/Podfile:
--------------------------------------------------------------------------------
1 | source 'https://github.com/CocoaPods/Specs.git'
2 |
3 | platform :ios, '10.0'
4 | use_frameworks!
5 |
6 | target 'CognitoApplication' do
7 | pod 'AWSCore', '~> 2.5.5'
8 | pod 'AWSCognitoIdentityProvider', '~> 2.5.5'
9 | end
--------------------------------------------------------------------------------
/CognitoApplication.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/CognitoApplication.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/CognitoApplication/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 | "info" : {
45 | "version" : 1,
46 | "author" : "xcode"
47 | }
48 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2017 David Tucker
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | SOFTWARE.
20 |
--------------------------------------------------------------------------------
/CognitoApplication/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 | 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 |
38 |
39 |
--------------------------------------------------------------------------------
/CognitoApplication/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 |
27 |
28 |
--------------------------------------------------------------------------------
/CognitoApplication/MultiFactorAuthenticationController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MultiFactorAuthenticationController.swift
3 | // CognitoApplication
4 | //
5 | // Created by David Tucker on 8/1/17.
6 | // Copyright © 2017 David Tucker. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import AWSCognitoIdentityProvider
11 |
12 | class MultiFactorAuthenticationController: UIViewController {
13 |
14 | @IBOutlet weak var authenticationCode: UITextField!
15 | @IBOutlet weak var submitCodeButton: UIButton!
16 |
17 | var mfaCompletionSource:AWSTaskCompletionSource?
18 |
19 | @IBAction func submitCodePressed(_ sender: AnyObject) {
20 | self.mfaCompletionSource?.set(result: NSString(string: authenticationCode.text!))
21 | }
22 |
23 | }
24 |
25 | extension MultiFactorAuthenticationController: AWSCognitoIdentityMultiFactorAuthentication {
26 |
27 | func getCode(_ authenticationInput: AWSCognitoIdentityMultifactorAuthenticationInput, mfaCodeCompletionSource: AWSTaskCompletionSource) {
28 | self.mfaCompletionSource = mfaCodeCompletionSource
29 | }
30 |
31 | func didCompleteMultifactorAuthenticationStepWithError(_ error: Error?) {
32 | DispatchQueue.main.async {
33 | self.authenticationCode.text = ""
34 | }
35 | if error != nil {
36 | let alertController = UIAlertController(title: "Cannot Verify Code",
37 | message: (error! as NSError).userInfo["message"] as? String,
38 | preferredStyle: .alert)
39 | let resendAction = UIAlertAction(title: "Try Again", style: .default, handler:nil)
40 | alertController.addAction(resendAction)
41 |
42 | let logoutAction = UIAlertAction(title: "Logout", style: .cancel, handler: { (action) in
43 | AppDelegate.defaultUserPool().currentUser()?.signOut()
44 | self.dismiss(animated: true, completion: {
45 | self.authenticationCode.text = nil
46 | })
47 | })
48 | alertController.addAction(logoutAction)
49 |
50 | self.present(alertController, animated: true, completion: nil)
51 | } else {
52 | self.dismiss(animated: true, completion: nil)
53 | }
54 | }
55 |
56 | }
57 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Cognito User Pools iOS Example Application
2 |
3 | This application is a sample iOS application created to showcase how to integrate iOS applications with [AWS Cognito User Pools](http://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-identity-pools.html) (which are a part of the [AWS Cognito](https://aws.amazon.com/cognito/) service).
4 |
5 | This code sample will be utilized in a series of articles which will expalain the integration. I will include links to the articles as they are published.
6 |
7 | ## Setup
8 |
9 | To run the application, you will have to perform the following steps: installing the AWS dependencies, setting up the user pool configuration, and creating a sample user.
10 |
11 | ### Installing Dependencies
12 |
13 | This application utilizes [CocoaPods](https://cocoapods.org/) for managing the dependencies. At this point, the only dependencies are the specific pieces of the AWS iOS SDK which relate to Cognito User Pools.
14 |
15 | If you haven't used CocoaPods, be sure to read the [Getting Started Guide](https://guides.cocoapods.org/using/getting-started.html). Once you have CocoaPods installed, just navigate to the project directory in your terminal (the one which contains the Podfile file) and enter the following:
16 |
17 | ```bash
18 | pod install
19 | ```
20 |
21 | This will install the needed dependencies for this project.
22 |
23 | ### User Pool Configuration
24 |
25 | To use this example application, you will need to create a Cognito User Pool and add in four specific values to a config file. The config file should be named `CognitoApplication/CognitoConfig.plist`. This file should have the following keys:
26 |
27 | | Key | Type | Value |
28 | |----|----|----|
29 | | **region** | String| This is the region in which you created your user pool. This needs to be the standard region identifier such as 'us-east-1' or 'ap-southeast-1' |
30 | | **poolId** | String | The id of the user pool that you created |
31 | | **clientId** | String | The clientId configured as a part of the app that you attached to the user pool |
32 | | **clientSecret** | String | The clientSecret that is configured as a part of the app that you attached to the user pool |
33 |
34 | ### Creating a Sample User
35 |
36 | This initial version of the application does not include user signup (that will be handled in the second article). This requires that you setup a user in the Cognito console. For this to work as expected, the `given_name`, `family_name`, and `email` should be the only required attributes.
37 |
--------------------------------------------------------------------------------
/CognitoApplication/VerificationViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // VerificationViewController.swift
3 | //
4 | // Copyright (c) 2017 David Tucker
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //
24 | import UIKit
25 | import AWSCognitoIdentityProvider
26 |
27 | class VerificationViewController: UIViewController {
28 |
29 | @IBOutlet weak var verificationField: UITextField!
30 | @IBOutlet weak var verifyButton: UIButton!
31 | @IBOutlet weak var verificationLabel: UILabel!
32 |
33 | var codeDeliveryDetails:AWSCognitoIdentityProviderCodeDeliveryDetailsType?
34 |
35 | var user: AWSCognitoIdentityUser?
36 |
37 | override func viewWillAppear(_ animated: Bool) {
38 | super.viewWillAppear(animated)
39 | self.verifyButton.isEnabled = false
40 | self.verificationField.addTarget(self, action: #selector(inputDidChange(_:)), for: .editingChanged)
41 | populateCodeDeliveryDetails()
42 | }
43 |
44 | func populateCodeDeliveryDetails() {
45 | let isEmail = (codeDeliveryDetails?.deliveryMedium == AWSCognitoIdentityProviderDeliveryMediumType.email)
46 | verifyButton.setTitle(isEmail ? "Verify Email Address" : "Verify Phone Number", for: .normal)
47 | let medium = isEmail ? "your email address" : "your phone number"
48 | let destination = codeDeliveryDetails!.destination!
49 | verificationLabel.text = "Please enter the code that was sent to \(medium) at \(destination)"
50 | }
51 |
52 | func inputDidChange(_ sender:AnyObject) {
53 | if(verificationField.text == nil) {
54 | self.verifyButton.isEnabled = false
55 | return
56 | }
57 | self.verifyButton.isEnabled = true
58 | }
59 |
60 | func resetConfirmation(message:String? = "") {
61 | self.verificationField.text = ""
62 | let alert = UIAlertController(title: "Error", message: message, preferredStyle: .alert)
63 | alert.addAction(UIAlertAction(title: "Retry", style: .default, handler: nil))
64 | self.present(alert, animated: true, completion:nil)
65 | }
66 |
67 | @IBAction func verifyPressed(_ sender: AnyObject) {
68 | self.user?.confirmSignUp(verificationField.text!)
69 | .continueWith(block: { (response) -> Any? in
70 | if response.error != nil {
71 | self.resetConfirmation(message: (response.error! as NSError).userInfo["message"] as? String)
72 | } else {
73 | DispatchQueue.main.async {
74 | // Return to Login View Controller - this should be handled a bit differently, but added in this manner for simplicity
75 | self.presentingViewController?.presentingViewController?.dismiss(animated: true, completion: nil)
76 | }
77 | }
78 | return nil
79 | })
80 | }
81 |
82 | @IBAction func resendConfirmationCodePressed(_ sender: AnyObject) {
83 | self.user?.resendConfirmationCode()
84 | .continueWith(block: { (respone) -> Any? in
85 | let alert = UIAlertController(title: "Resent", message: "The confirmation code has been resent.", preferredStyle: .alert)
86 | alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
87 | self.present(alert, animated: true, completion:nil)
88 | return nil
89 | })
90 | }
91 |
92 | }
93 |
--------------------------------------------------------------------------------
/CognitoApplication/ResetPasswordViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ResetPasswordViewController.swift
3 | // Created by David Tucker (davidtucker.net) on 5/4/17.
4 | //
5 | // Copyright (c) 2017 David Tucker
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy
8 | // of this software and associated documentation files (the "Software"), to deal
9 | // in the Software without restriction, including without limitation the rights
10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | // copies of the Software, and to permit persons to whom the Software is
12 | // furnished to do so, subject to the following conditions:
13 | //
14 | // The above copyright notice and this permission notice shall be included in all
15 | // copies or substantial portions of the Software.
16 | //
17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 | // SOFTWARE.
24 | //
25 |
26 | import Foundation
27 | import UIKit
28 | import AWSCognitoIdentityProvider
29 |
30 | class ResetPasswordViewController: UIViewController {
31 |
32 | @IBOutlet weak var newPasswordButton: UIButton!
33 | @IBOutlet weak var newPasswordInput: UITextField!
34 | @IBOutlet weak var firstNameInput: UITextField!
35 | @IBOutlet weak var lastNameInput: UITextField!
36 |
37 | var currentUserAttributes:[String:String]?
38 |
39 | var resetPasswordCompletion: AWSTaskCompletionSource?
40 |
41 | override func viewWillAppear(_ animated: Bool) {
42 | super.viewWillAppear(animated)
43 | self.newPasswordInput.text = nil
44 | self.newPasswordInput.addTarget(self, action: #selector(inputDidChange(_:)), for: .editingChanged)
45 | self.firstNameInput.addTarget(self, action: #selector(inputDidChange(_:)), for: .editingChanged)
46 | self.lastNameInput.addTarget(self, action: #selector(inputDidChange(_:)), for: .editingChanged)
47 | }
48 |
49 | func inputDidChange(_ sender:AnyObject) {
50 | if (self.newPasswordInput.text != nil && self.firstNameInput != nil && self.lastNameInput != nil) {
51 | self.newPasswordButton.isEnabled = true
52 | } else {
53 | self.newPasswordButton.isEnabled = false
54 | }
55 | }
56 |
57 | @IBAction func submitNewPassword(_ sender:AnyObject) {
58 | var userAttributes:[String:String] = [:]
59 | userAttributes["family_name"] = self.lastNameInput.text
60 | userAttributes["given_name"] = self.firstNameInput.text
61 | let details = AWSCognitoIdentityNewPasswordRequiredDetails(proposedPassword: self.newPasswordInput.text!, userAttributes: userAttributes)
62 | self.resetPasswordCompletion?.set(result: details)
63 | }
64 |
65 | }
66 |
67 | extension ResetPasswordViewController: AWSCognitoIdentityNewPasswordRequired {
68 |
69 | public func getNewPasswordDetails(_ newPasswordRequiredInput: AWSCognitoIdentityNewPasswordRequiredInput, newPasswordRequiredCompletionSource: AWSTaskCompletionSource) {
70 | self.currentUserAttributes = newPasswordRequiredInput.userAttributes
71 | self.resetPasswordCompletion = newPasswordRequiredCompletionSource
72 | }
73 |
74 | public func didCompleteNewPasswordStepWithError(_ error: Error?) {
75 | DispatchQueue.main.async {
76 | if let error = error as NSError? {
77 | let alertController = UIAlertController(title: error.userInfo["__type"] as? String,
78 | message: error.userInfo["message"] as? String,
79 | preferredStyle: .alert)
80 | let retryAction = UIAlertAction(title: "Retry", style: .default, handler: nil)
81 | alertController.addAction(retryAction)
82 | self.present(alertController, animated: true, completion: nil)
83 | } else {
84 | self.newPasswordInput.text = nil
85 | self.dismiss(animated: true, completion: nil)
86 | }
87 | }
88 | }
89 |
90 | }
91 |
--------------------------------------------------------------------------------
/CognitoApplication/CognitoConfig.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CognitoConfig.swift
3 | // Created by David Tucker (davidtucker.net) on 5/4/17.
4 | //
5 | // Copyright (c) 2017 David Tucker
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy
8 | // of this software and associated documentation files (the "Software"), to deal
9 | // in the Software without restriction, including without limitation the rights
10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | // copies of the Software, and to permit persons to whom the Software is
12 | // furnished to do so, subject to the following conditions:
13 | //
14 | // The above copyright notice and this permission notice shall be included in all
15 | // copies or substantial portions of the Software.
16 | //
17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 | // SOFTWARE.
24 | //
25 |
26 | import Foundation
27 | import AWSCore
28 |
29 | class CognitoConfig: NSObject {
30 |
31 | var keys:Dictionary?
32 |
33 | override init() {
34 | super.init()
35 | self.keys = readPropertyList()
36 | guard self.keys != nil else {
37 | fatalError("You must include a CognitoConfig.plist file with the necesary values for your user pool")
38 | }
39 | }
40 |
41 | func readPropertyList() -> Dictionary? {
42 | if let path = Bundle.main.path(forResource: "CognitoConfig", ofType: "plist") {
43 | let keys = NSDictionary(contentsOfFile: path)
44 | return keys as? Dictionary
45 | }
46 | return nil
47 | }
48 |
49 | func getPoolId() -> String {
50 | let poolId = self.keys?["poolId"] as? String
51 | guard poolId != nil else {
52 | fatalError("You must specify a poolId in your CognitoConfig.plist file")
53 | }
54 | return poolId!
55 | }
56 |
57 | func getClientId() -> String {
58 | let clientId = self.keys?["clientId"] as? String
59 | guard clientId != nil else {
60 | fatalError("You must specify a clientlId in your CognitoConfig.plist file")
61 | }
62 | return clientId!
63 | }
64 |
65 | func getClientSecret() -> String {
66 | let clientSecret = self.keys?["clientSecret"] as? String
67 | guard clientSecret != nil else {
68 | fatalError("You must specify a clientSecret in your CognitoConfig.plist file")
69 | }
70 | return clientSecret!
71 | }
72 |
73 | func getRegion() -> AWSRegionType {
74 | let region = self.keys?["region"] as? String
75 | guard region != nil else {
76 | fatalError("You must specify a region value in CognitoConfig.plist")
77 | }
78 | var output:AWSRegionType?
79 |
80 | switch region! {
81 |
82 | case "us-east-1":
83 | output = AWSRegionType.USEast1
84 | case "us-east-2":
85 | output = AWSRegionType.USEast2
86 | case "us-west-1":
87 | output = AWSRegionType.USWest1
88 | case "us-west-2":
89 | output = AWSRegionType.USWest2
90 | case "ap-south-1":
91 | output = AWSRegionType.APSouth1
92 | case "ap-northeast-1":
93 | output = AWSRegionType.APNortheast1
94 | case "ap-northeast-2":
95 | output = AWSRegionType.APNortheast2
96 | case "ap-southeast-1":
97 | output = AWSRegionType.APSoutheast1
98 | case "ap-southeast-2":
99 | output = AWSRegionType.APSoutheast2
100 | case "ca-central-1":
101 | output = AWSRegionType.CACentral1
102 | case "cn-north-1":
103 | output = AWSRegionType.CNNorth1
104 | case "eu-central-1":
105 | output = AWSRegionType.EUCentral1
106 | case "eu-west-1":
107 | output = AWSRegionType.EUWest1
108 | case "eu-west-2":
109 | output = AWSRegionType.EUWest2
110 | case "sa-east-1":
111 | output = AWSRegionType.SAEast1
112 | case "us-gov-west-1":
113 | output = AWSRegionType.USGovWest1
114 | default:
115 | print("Invalid region specified")
116 |
117 | }
118 |
119 | guard output != nil else {
120 | fatalError("You must specify a valid region value in CognitoConfig.plist such as 'us-east-1'")
121 | }
122 |
123 | return output!
124 | }
125 |
126 | }
127 |
--------------------------------------------------------------------------------
/CognitoApplication/ForgotPasswordViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ForgotPasswordViewController.swift
3 | // CognitoApplication
4 | //
5 | // Created by David Tucker on 8/2/17.
6 | // Copyright © 2017 David Tucker. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import AWSCognitoIdentityProvider
11 |
12 | class ForgotPasswordViewController: UIViewController {
13 |
14 | @IBOutlet weak var verificationCode: UITextField!
15 | @IBOutlet weak var newPassword: UITextField!
16 | @IBOutlet weak var confirmPassword: UITextField!
17 | @IBOutlet weak var resetPasswordButton: UIButton!
18 |
19 | var emailAddress:String = ""
20 | var user:AWSCognitoIdentityUser?
21 |
22 | func clearFields() {
23 | self.verificationCode.text = ""
24 | self.newPassword.text = ""
25 | self.confirmPassword.text = ""
26 | self.emailAddress = ""
27 | }
28 |
29 | override func viewDidAppear(_ animated: Bool) {
30 | super.viewDidAppear(animated)
31 | if !emailAddress.isEmpty {
32 | let pool = AppDelegate.defaultUserPool()
33 | // Get a reference to the user using the email address
34 | user = pool.getUser(emailAddress)
35 | // Initiate the forgot password process which will send a verification code to the user
36 | user?.forgotPassword()
37 | .continueWith(block: { (response) -> Any? in
38 | if response.error != nil {
39 | // Cannot request password reset due to error (for example, the attempt limit exceeded)
40 | let alert = UIAlertController(title: "Cannot Reset Password", message: (response.error! as NSError).userInfo["message"] as? String, preferredStyle: .alert)
41 | alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { (action) in
42 | self.clearFields()
43 | self.presentingViewController?.dismiss(animated: true, completion: nil)
44 | }))
45 | self.present(alert, animated: true, completion: nil)
46 | return nil
47 | }
48 | // Password reset was requested and message sent. Let the user know where to look for code.
49 | let result = response.result
50 | let isEmail = (result?.codeDeliveryDetails?.deliveryMedium == AWSCognitoIdentityProviderDeliveryMediumType.email)
51 | let destination:String = result!.codeDeliveryDetails!.destination!
52 | let medium = isEmail ? "an email" : "a text message"
53 | let alert = UIAlertController(title: "Verification Sent", message: "You should receive \(medium) with a verification code at \(destination). Enter that code here along with a new password.", preferredStyle: .alert)
54 | alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
55 | self.present(alert, animated: true, completion: nil)
56 | return nil
57 | })
58 | }
59 | }
60 |
61 | @IBAction func resetPasswordPressed(_ sender: AnyObject) {
62 | user?.confirmForgotPassword(self.verificationCode.text!, password: self.newPassword.text!)
63 | .continueWith { (response) -> Any? in
64 | if response.error != nil {
65 | // The password could not be reset - let the user know
66 | let alert = UIAlertController(title: "Cannot Reset Password", message: (response.error! as NSError).userInfo["message"] as? String, preferredStyle: .alert)
67 | alert.addAction(UIAlertAction(title: "Resend Code", style: .default, handler: { (action) in
68 | self.user?.forgotPassword()
69 | .continueWith(block: { (result) -> Any? in
70 | print("Code Sent")
71 | return nil
72 | })
73 | }))
74 | alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: { (action) in
75 | DispatchQueue.main.async {
76 | self.presentingViewController?.dismiss(animated: true, completion: nil)
77 | }
78 | }))
79 | DispatchQueue.main.async {
80 | self.present(alert, animated: true, completion: nil)
81 | }
82 | } else {
83 | // Password reset. Send the user back to the login and let them know they can login with new password.
84 | DispatchQueue.main.async {
85 | let presentingController = self.presentingViewController
86 | self.presentingViewController?.dismiss(animated: true, completion: {
87 | let alert = UIAlertController(title: "Password Reset", message: "Password reset. Please log into the account with your email and new password.", preferredStyle: .alert)
88 | alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
89 | presentingController?.present(alert, animated: true, completion: nil)
90 | self.clearFields()
91 | }
92 | )
93 | }
94 | }
95 | return nil
96 | }
97 | }
98 |
99 | @IBAction func cancelForgotPasswordPressed(_ sender: AnyObject) {
100 | clearFields()
101 | self.presentingViewController?.dismiss(animated: true, completion: nil)
102 | }
103 |
104 | }
105 |
--------------------------------------------------------------------------------
/CognitoApplication/LoginViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LoginViewController.swift
3 | // Created by David Tucker (davidtucker.net) on 5/4/17.
4 | //
5 | // Copyright (c) 2017 David Tucker
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy
8 | // of this software and associated documentation files (the "Software"), to deal
9 | // in the Software without restriction, including without limitation the rights
10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | // copies of the Software, and to permit persons to whom the Software is
12 | // furnished to do so, subject to the following conditions:
13 | //
14 | // The above copyright notice and this permission notice shall be included in all
15 | // copies or substantial portions of the Software.
16 | //
17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 | // SOFTWARE.
24 | //
25 |
26 | import UIKit
27 | import AWSCognitoIdentityProvider
28 |
29 | class LoginViewController: UIViewController {
30 |
31 | @IBOutlet weak var passwordInput: UITextField?
32 | @IBOutlet weak var usernameInput: UITextField?
33 | @IBOutlet weak var loginButton: UIButton?
34 |
35 | var passwordAuthenticationCompletion: AWSTaskCompletionSource?
36 |
37 | override func viewWillAppear(_ animated: Bool) {
38 | super.viewWillAppear(animated)
39 | self.passwordInput?.addTarget(self, action: #selector(inputDidChange(_:)), for: .editingChanged)
40 | self.usernameInput?.addTarget(self, action: #selector(inputDidChange(_:)), for: .editingChanged)
41 | }
42 |
43 | @IBAction func loginPressed(_ sender: AnyObject) {
44 | if (self.usernameInput?.text == nil || self.passwordInput?.text == nil) {
45 | return
46 | }
47 |
48 | let authDetails = AWSCognitoIdentityPasswordAuthenticationDetails(username: self.usernameInput!.text!, password: self.passwordInput!.text! )
49 | self.passwordAuthenticationCompletion?.set(result: authDetails)
50 | }
51 |
52 | @IBAction func forgotPasswordPressed(_ sender: AnyObject) {
53 | if (self.usernameInput?.text == nil || self.usernameInput!.text!.isEmpty) {
54 | let alertController = UIAlertController(title: "Enter Username",
55 | message: "Please enter your username and then select Forgot Password if you want to reset your password.",
56 | preferredStyle: .alert)
57 | let retryAction = UIAlertAction(title: "OK", style: .default, handler: nil)
58 | alertController.addAction(retryAction)
59 | self.present(alertController, animated: true, completion: nil)
60 | return
61 | }
62 | self.performSegue(withIdentifier: "ForgotPasswordSegue", sender: self)
63 | }
64 |
65 | func inputDidChange(_ sender:AnyObject) {
66 | if (self.usernameInput?.text != nil && self.passwordInput?.text != nil) {
67 | self.loginButton?.isEnabled = true
68 | } else {
69 | self.loginButton?.isEnabled = false
70 | }
71 | }
72 |
73 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
74 | if segue.identifier == "ForgotPasswordSegue" {
75 | let forgotPasswordController = segue.destination as! ForgotPasswordViewController
76 | forgotPasswordController.emailAddress = self.usernameInput!.text!
77 | }
78 | }
79 |
80 | }
81 |
82 | extension LoginViewController: AWSCognitoIdentityPasswordAuthentication {
83 |
84 | public func getDetails(_ authenticationInput: AWSCognitoIdentityPasswordAuthenticationInput, passwordAuthenticationCompletionSource: AWSTaskCompletionSource) {
85 | self.passwordAuthenticationCompletion = passwordAuthenticationCompletionSource
86 | DispatchQueue.main.async {
87 | if (self.usernameInput?.text == nil) {
88 | self.usernameInput?.text = authenticationInput.lastKnownUsername
89 | }
90 | }
91 | }
92 |
93 | public func didCompleteStepWithError(_ error: Error?) {
94 | DispatchQueue.main.async {
95 | if error != nil {
96 | let alertController = UIAlertController(title: "Cannot Login",
97 | message: (error! as NSError).userInfo["message"] as? String,
98 | preferredStyle: .alert)
99 | let retryAction = UIAlertAction(title: "Retry", style: .default, handler: nil)
100 | alertController.addAction(retryAction)
101 |
102 | self.present(alertController, animated: true, completion: nil)
103 | } else {
104 | self.dismiss(animated: true, completion: {
105 | self.usernameInput?.text = nil
106 | self.passwordInput?.text = nil
107 | })
108 | }
109 | }
110 | }
111 |
112 | }
113 |
--------------------------------------------------------------------------------
/CognitoApplication/AppViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppViewController.swift
3 | // Created by David Tucker (davidtucker.net) on 5/4/17.
4 | //
5 | // Copyright (c) 2017 David Tucker
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy
8 | // of this software and associated documentation files (the "Software"), to deal
9 | // in the Software without restriction, including without limitation the rights
10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | // copies of the Software, and to permit persons to whom the Software is
12 | // furnished to do so, subject to the following conditions:
13 | //
14 | // The above copyright notice and this permission notice shall be included in all
15 | // copies or substantial portions of the Software.
16 | //
17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 | // SOFTWARE.
24 | //
25 |
26 | import UIKit
27 | import AWSCognitoIdentityProvider
28 |
29 | class AppViewController: UITableViewController {
30 |
31 | @IBOutlet weak var usernameLabel: UILabel!
32 | @IBOutlet weak var firstNameLabel: UILabel!
33 | @IBOutlet weak var lastNameLabel: UILabel!
34 | @IBOutlet weak var phoneNumberLabel: UILabel!
35 | @IBOutlet weak var mfaSwitch: UISwitch!
36 |
37 | var user:AWSCognitoIdentityUser?
38 | var userAttributes:[AWSCognitoIdentityProviderAttributeType]?
39 | var mfaSettings:[AWSCognitoIdentityProviderMFAOptionType]?
40 |
41 | override func viewDidLoad() {
42 | super.viewDidLoad()
43 | loadUserValues()
44 | }
45 |
46 | func loadUserValues () {
47 | self.resetAttributeValues()
48 | self.fetchUserAttributes()
49 | }
50 |
51 | func fetchUserAttributes() {
52 | self.resetAttributeValues()
53 | user = AppDelegate.defaultUserPool().currentUser()
54 | user?.getDetails().continueOnSuccessWith(block: { (task) -> Any? in
55 | guard task.result != nil else {
56 | return nil
57 | }
58 | self.userAttributes = task.result?.userAttributes
59 | self.mfaSettings = task.result?.mfaOptions
60 | self.userAttributes?.forEach({ (attribute) in
61 | print("Name: " + attribute.name!)
62 | })
63 | DispatchQueue.main.async {
64 | self.setAttributeValues()
65 | }
66 | return nil
67 | })
68 | }
69 |
70 | func resetAttributeValues() {
71 | DispatchQueue.main.async {
72 | self.lastNameLabel.text = ""
73 | self.firstNameLabel.text = ""
74 | self.usernameLabel.text = ""
75 | self.phoneNumberLabel.text = ""
76 | self.mfaSwitch.setOn(false, animated: false)
77 | }
78 | }
79 |
80 | @IBAction func handleSwitch(_ sender: AnyObject) {
81 | let settings = AWSCognitoIdentityUserSettings()
82 | if mfaSwitch.isOn {
83 | // Enable MFA
84 | let mfaOptions = AWSCognitoIdentityUserMFAOption()
85 | mfaOptions.attributeName = "phone_number"
86 | mfaOptions.deliveryMedium = .sms
87 | settings.mfaOptions = [mfaOptions]
88 | } else {
89 | // Disable MFA
90 | settings.mfaOptions = []
91 | }
92 | user?.setUserSettings(settings)
93 | .continueOnSuccessWith(block: { (response) -> Any? in
94 | if response.error != nil {
95 | let alert = UIAlertController(title: "Error", message: (response.error! as NSError).userInfo["message"] as? String, preferredStyle: .alert)
96 | alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
97 | self.present(alert, animated: true, completion:nil)
98 | self.resetAttributeValues()
99 | } else {
100 | self.fetchUserAttributes()
101 | }
102 | return nil
103 | })
104 |
105 | }
106 |
107 | func isEmailMFAEnabled() -> Bool {
108 | let values = self.mfaSettings?.filter { $0.deliveryMedium == AWSCognitoIdentityProviderDeliveryMediumType.sms }
109 | if values?.first != nil {
110 | return true
111 | }
112 | return false
113 | }
114 |
115 | func setAttributeValues() {
116 | DispatchQueue.main.async {
117 | self.lastNameLabel.text = self.valueForAttribute(name: "family_name")
118 | self.firstNameLabel.text = self.valueForAttribute(name: "given_name")
119 | self.usernameLabel.text = self.valueForAttribute(name: "email")
120 | self.phoneNumberLabel.text = self.valueForAttribute(name: "phone_number")
121 | if self.mfaSettings == nil {
122 | self.mfaSwitch.setOn(false, animated: false)
123 | } else {
124 | self.mfaSwitch.setOn(self.isEmailMFAEnabled(), animated: false)
125 | }
126 | }
127 | }
128 |
129 | func valueForAttribute(name:String) -> String? {
130 | let values = self.userAttributes?.filter { $0.name == name }
131 | return values?.first?.value
132 | }
133 |
134 | @IBAction func logout(_ sender:AnyObject) {
135 | user?.signOut()
136 | self.fetchUserAttributes()
137 | }
138 |
139 | }
140 |
--------------------------------------------------------------------------------
/CognitoApplication/SignupViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SignupViewController.swift
3 | //
4 | // Copyright (c) 2017 David Tucker
5 | //
6 | // Permission is hereby granted, free of charge, to any person obtaining a copy
7 | // of this software and associated documentation files (the "Software"), to deal
8 | // in the Software without restriction, including without limitation the rights
9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | // copies of the Software, and to permit persons to whom the Software is
11 | // furnished to do so, subject to the following conditions:
12 | //
13 | // The above copyright notice and this permission notice shall be included in all
14 | // copies or substantial portions of the Software.
15 | //
16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | // SOFTWARE.
23 | //
24 |
25 | import UIKit
26 | import AWSCognitoIdentityProvider
27 |
28 | class SignupViewController : UIViewController {
29 |
30 | @IBOutlet weak var firstName: UITextField!
31 | @IBOutlet weak var lastName: UITextField!
32 | @IBOutlet weak var email: UITextField!
33 | @IBOutlet weak var password: UITextField!
34 | @IBOutlet weak var confirmPassword: UITextField!
35 | @IBOutlet weak var submitButton: UIButton!
36 | @IBOutlet weak var phoneNumber: UITextField!
37 |
38 | var user: AWSCognitoIdentityUser?
39 | var codeDeliveryDetails:AWSCognitoIdentityProviderCodeDeliveryDetailsType?
40 |
41 | override func viewWillAppear(_ animated: Bool) {
42 | super.viewWillAppear(animated)
43 | self.submitButton.isEnabled = false
44 | self.firstName.addTarget(self, action: #selector(inputDidChange(_:)), for: .editingChanged)
45 | self.lastName.addTarget(self, action: #selector(inputDidChange(_:)), for: .editingChanged)
46 | self.email.addTarget(self, action: #selector(inputDidChange(_:)), for: .editingChanged)
47 | self.password.addTarget(self, action: #selector(inputDidChange(_:)), for: .editingChanged)
48 | self.confirmPassword.addTarget(self, action: #selector(inputDidChange(_:)), for: .editingChanged)
49 | self.phoneNumber.addTarget(self, action: #selector(inputDidChange(_:)), for: .editingChanged)
50 | }
51 |
52 | func inputDidChange(_ sender:AnyObject) {
53 | if(firstName.text == nil || lastName.text == nil) {
54 | self.submitButton.isEnabled = false
55 | return
56 | }
57 | if(email.text == nil) {
58 | self.submitButton.isEnabled = false
59 | return
60 | }
61 | if(password.text == nil || confirmPassword.text == nil) {
62 | self.submitButton.isEnabled = false
63 | return
64 | }
65 | if phoneNumber.text == nil || phoneNumber.text!.isEmpty == true {
66 | self.submitButton.isEnabled = false
67 | return
68 | }
69 | self.submitButton.isEnabled = (password.text == confirmPassword.text)
70 | }
71 |
72 | @IBAction func signupPressed(_ sender: AnyObject) {
73 | let userPool = AppDelegate.defaultUserPool()
74 | let emailAttribute = AWSCognitoIdentityUserAttributeType(name: "email", value: email.text!)
75 | let firstNameAttribute = AWSCognitoIdentityUserAttributeType(name: "given_name", value: firstName.text!)
76 | let lastNameAttribute = AWSCognitoIdentityUserAttributeType(name: "family_name", value: lastName.text!)
77 | let phoneNumberAttribute = AWSCognitoIdentityUserAttributeType(name: "phone_number", value: phoneNumber.text!)
78 | let attributes:[AWSCognitoIdentityUserAttributeType] = [emailAttribute, firstNameAttribute, lastNameAttribute, phoneNumberAttribute]
79 | userPool.signUp(email.text!, password: password.text!, userAttributes: attributes, validationData: nil)
80 | .continueWith { (response) -> Any? in
81 | if response.error != nil {
82 | // Error in the Signup Process
83 | let alert = UIAlertController(title: "Error", message: (response.error! as NSError).userInfo["message"] as? String, preferredStyle: .alert)
84 | alert.addAction(UIAlertAction(title: "OK", style: .default, handler:nil))
85 | self.present(alert, animated: true, completion: nil)
86 | } else {
87 | self.user = response.result!.user
88 | // Does user need confirmation?
89 | if (response.result?.userConfirmed?.intValue != AWSCognitoIdentityUserStatus.confirmed.rawValue) {
90 | // User needs confirmation, so we need to proceed to the verify view controller
91 | DispatchQueue.main.async {
92 | self.codeDeliveryDetails = response.result?.codeDeliveryDetails
93 | self.performSegue(withIdentifier: "VerifySegue", sender: self)
94 | }
95 | } else {
96 | // User signed up but does not need confirmation. This should rarely happen (if ever).
97 | DispatchQueue.main.async {
98 | self.presentingViewController?.dismiss(animated: true, completion: nil)
99 | }
100 | }
101 | }
102 | return nil
103 | }
104 | }
105 |
106 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
107 | let verificationController = segue.destination as! VerificationViewController
108 | verificationController.codeDeliveryDetails = self.codeDeliveryDetails
109 | verificationController.user = self.user!
110 | }
111 |
112 | }
113 |
--------------------------------------------------------------------------------
/CognitoApplication/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // Created by David Tucker (davidtucker.net) on 5/4/17.
4 | //
5 | // Copyright (c) 2017 David Tucker
6 | //
7 | // Permission is hereby granted, free of charge, to any person obtaining a copy
8 | // of this software and associated documentation files (the "Software"), to deal
9 | // in the Software without restriction, including without limitation the rights
10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 | // copies of the Software, and to permit persons to whom the Software is
12 | // furnished to do so, subject to the following conditions:
13 | //
14 | // The above copyright notice and this permission notice shall be included in all
15 | // copies or substantial portions of the Software.
16 | //
17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 | // SOFTWARE.
24 | //
25 |
26 | import UIKit
27 | import AWSCognitoIdentityProvider
28 |
29 | let userPoolID = "SampleUserPool"
30 |
31 | @UIApplicationMain
32 | class AppDelegate: UIResponder, UIApplicationDelegate {
33 |
34 | class func defaultUserPool() -> AWSCognitoIdentityUserPool {
35 | return AWSCognitoIdentityUserPool(forKey: userPoolID)
36 | }
37 |
38 | var window: UIWindow?
39 |
40 | var loginViewController: LoginViewController?
41 |
42 | var resetPasswordViewController: ResetPasswordViewController?
43 |
44 | var multiFactorAuthenticationController: MultiFactorAuthenticationController?
45 |
46 | var navigationController: UINavigationController?
47 |
48 | var cognitoConfig:CognitoConfig?
49 |
50 | var storyboard: UIStoryboard? {
51 | return UIStoryboard(name: "Main", bundle: nil)
52 | }
53 |
54 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
55 | // setup logging
56 | AWSDDLog.sharedInstance.logLevel = .verbose
57 | AWSDDLog.add(AWSDDTTYLogger.sharedInstance)
58 |
59 | // setup cognito config
60 | self.cognitoConfig = CognitoConfig()
61 |
62 | // setup cognito
63 | setupCognitoUserPool()
64 |
65 | // Override point for customization after application launch.
66 | return true
67 | }
68 |
69 | func setupCognitoUserPool() {
70 | let clientId:String = self.cognitoConfig!.getClientId()
71 | let poolId:String = self.cognitoConfig!.getPoolId()
72 | let clientSecret:String = self.cognitoConfig!.getClientSecret()
73 | let region:AWSRegionType = self.cognitoConfig!.getRegion()
74 |
75 | let serviceConfiguration:AWSServiceConfiguration = AWSServiceConfiguration(region: region, credentialsProvider: nil)
76 | let cognitoConfiguration:AWSCognitoIdentityUserPoolConfiguration = AWSCognitoIdentityUserPoolConfiguration(clientId: clientId, clientSecret: clientSecret, poolId: poolId)
77 | AWSCognitoIdentityUserPool.register(with: serviceConfiguration, userPoolConfiguration: cognitoConfiguration, forKey: userPoolID)
78 | let pool:AWSCognitoIdentityUserPool = AppDelegate.defaultUserPool()
79 | pool.delegate = self
80 | }
81 |
82 | }
83 |
84 | extension AppDelegate: AWSCognitoIdentityInteractiveAuthenticationDelegate {
85 |
86 | func startPasswordAuthentication() -> AWSCognitoIdentityPasswordAuthentication {
87 | if(self.navigationController == nil) {
88 | self.navigationController = self.window?.rootViewController as? UINavigationController
89 | }
90 |
91 | if(self.loginViewController == nil) {
92 | self.loginViewController = self.storyboard?.instantiateViewController(withIdentifier: "LoginViewController") as? LoginViewController
93 | }
94 |
95 | DispatchQueue.main.async {
96 | if(self.loginViewController!.isViewLoaded || self.loginViewController!.view.window == nil) {
97 | self.navigationController?.present(self.loginViewController!, animated: true, completion: nil)
98 | }
99 | }
100 |
101 | return self.loginViewController!
102 | }
103 |
104 | func startNewPasswordRequired() -> AWSCognitoIdentityNewPasswordRequired {
105 | if (self.resetPasswordViewController == nil) {
106 | self.resetPasswordViewController = self.storyboard?.instantiateViewController(withIdentifier: "ResetPasswordController") as? ResetPasswordViewController
107 | }
108 |
109 | DispatchQueue.main.async {
110 | if(self.resetPasswordViewController!.isViewLoaded || self.resetPasswordViewController!.view.window == nil) {
111 | self.navigationController?.present(self.resetPasswordViewController!, animated: true, completion: nil)
112 | }
113 | }
114 |
115 | return self.resetPasswordViewController!
116 | }
117 |
118 | func startMultiFactorAuthentication() -> AWSCognitoIdentityMultiFactorAuthentication {
119 | if (self.multiFactorAuthenticationController == nil) {
120 | self.multiFactorAuthenticationController = self.storyboard?.instantiateViewController(withIdentifier: "MultiFactorAuthenticationController") as? MultiFactorAuthenticationController
121 | }
122 |
123 | DispatchQueue.main.async {
124 | if(self.multiFactorAuthenticationController!.isViewLoaded || self.multiFactorAuthenticationController!.view.window == nil) {
125 | self.navigationController?.present(self.multiFactorAuthenticationController!, animated: true, completion: nil)
126 | }
127 | }
128 |
129 | return self.multiFactorAuthenticationController!
130 | }
131 |
132 | }
133 |
134 |
135 |
136 |
--------------------------------------------------------------------------------
/CognitoApplication.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 632D25211EB81732005A4E31 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 632D25201EB81732005A4E31 /* AppDelegate.swift */; };
11 | 632D25261EB81732005A4E31 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 632D25241EB81732005A4E31 /* Main.storyboard */; };
12 | 632D25281EB81732005A4E31 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 632D25271EB81732005A4E31 /* Assets.xcassets */; };
13 | 632D252B1EB81732005A4E31 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 632D25291EB81732005A4E31 /* LaunchScreen.storyboard */; };
14 | 632D25351EB95CBD005A4E31 /* LoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 632D25341EB95CBD005A4E31 /* LoginViewController.swift */; };
15 | 632D25371EB95CCE005A4E31 /* ResetPasswordViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 632D25361EB95CCE005A4E31 /* ResetPasswordViewController.swift */; };
16 | 632D25391EB95CDE005A4E31 /* AppViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 632D25381EB95CDE005A4E31 /* AppViewController.swift */; };
17 | 6373365E1F3252B700EA5BCE /* ForgotPasswordViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6373365D1F3252B700EA5BCE /* ForgotPasswordViewController.swift */; };
18 | 6398A6701EC15413009C2D77 /* CognitoConfig.plist in Resources */ = {isa = PBXBuildFile; fileRef = 6398A66F1EC15413009C2D77 /* CognitoConfig.plist */; };
19 | 6398A6721EC1548D009C2D77 /* CognitoConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6398A6711EC1548D009C2D77 /* CognitoConfig.swift */; };
20 | 63A8F7D51F30E79B00AD21E0 /* SignupViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63A8F7D41F30E79B00AD21E0 /* SignupViewController.swift */; };
21 | 63A8F7D71F30FAC800AD21E0 /* VerificationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63A8F7D61F30FAC800AD21E0 /* VerificationViewController.swift */; };
22 | 63B5E4F31F33EBC300B9D1A3 /* MultiFactorAuthenticationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63B5E4F21F33EBC300B9D1A3 /* MultiFactorAuthenticationController.swift */; };
23 | 6E3BB744AD21E1DD722F496E /* Pods_CognitoApplication.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 50439609D2FD5FDF1A0EA3E5 /* Pods_CognitoApplication.framework */; };
24 | /* End PBXBuildFile section */
25 |
26 | /* Begin PBXFileReference section */
27 | 50439609D2FD5FDF1A0EA3E5 /* Pods_CognitoApplication.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_CognitoApplication.framework; sourceTree = BUILT_PRODUCTS_DIR; };
28 | 5D6FA8A57C007EC439FA41F7 /* Pods-CognitoApplication.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CognitoApplication.debug.xcconfig"; path = "Pods/Target Support Files/Pods-CognitoApplication/Pods-CognitoApplication.debug.xcconfig"; sourceTree = ""; };
29 | 632D251D1EB81732005A4E31 /* CognitoApplication.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CognitoApplication.app; sourceTree = BUILT_PRODUCTS_DIR; };
30 | 632D25201EB81732005A4E31 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
31 | 632D25251EB81732005A4E31 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
32 | 632D25271EB81732005A4E31 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
33 | 632D252A1EB81732005A4E31 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
34 | 632D252C1EB81732005A4E31 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
35 | 632D25341EB95CBD005A4E31 /* LoginViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginViewController.swift; sourceTree = ""; };
36 | 632D25361EB95CCE005A4E31 /* ResetPasswordViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ResetPasswordViewController.swift; sourceTree = ""; };
37 | 632D25381EB95CDE005A4E31 /* AppViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppViewController.swift; sourceTree = ""; };
38 | 6373365D1F3252B700EA5BCE /* ForgotPasswordViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ForgotPasswordViewController.swift; sourceTree = ""; };
39 | 6398A66F1EC15413009C2D77 /* CognitoConfig.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = CognitoConfig.plist; sourceTree = ""; };
40 | 6398A6711EC1548D009C2D77 /* CognitoConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CognitoConfig.swift; sourceTree = ""; };
41 | 63A8F7D41F30E79B00AD21E0 /* SignupViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignupViewController.swift; sourceTree = ""; };
42 | 63A8F7D61F30FAC800AD21E0 /* VerificationViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VerificationViewController.swift; sourceTree = ""; };
43 | 63B5E4F21F33EBC300B9D1A3 /* MultiFactorAuthenticationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MultiFactorAuthenticationController.swift; sourceTree = ""; };
44 | 88B9FE351F2439551342AEC4 /* Pods-CognitoApplication.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CognitoApplication.release.xcconfig"; path = "Pods/Target Support Files/Pods-CognitoApplication/Pods-CognitoApplication.release.xcconfig"; sourceTree = ""; };
45 | /* End PBXFileReference section */
46 |
47 | /* Begin PBXFrameworksBuildPhase section */
48 | 632D251A1EB81732005A4E31 /* Frameworks */ = {
49 | isa = PBXFrameworksBuildPhase;
50 | buildActionMask = 2147483647;
51 | files = (
52 | 6E3BB744AD21E1DD722F496E /* Pods_CognitoApplication.framework in Frameworks */,
53 | );
54 | runOnlyForDeploymentPostprocessing = 0;
55 | };
56 | /* End PBXFrameworksBuildPhase section */
57 |
58 | /* Begin PBXGroup section */
59 | 02715F221045F7A1A6B346C4 /* Pods */ = {
60 | isa = PBXGroup;
61 | children = (
62 | 5D6FA8A57C007EC439FA41F7 /* Pods-CognitoApplication.debug.xcconfig */,
63 | 88B9FE351F2439551342AEC4 /* Pods-CognitoApplication.release.xcconfig */,
64 | );
65 | name = Pods;
66 | sourceTree = "";
67 | };
68 | 632D25141EB81731005A4E31 = {
69 | isa = PBXGroup;
70 | children = (
71 | 632D251F1EB81732005A4E31 /* CognitoApplication */,
72 | 632D251E1EB81732005A4E31 /* Products */,
73 | 02715F221045F7A1A6B346C4 /* Pods */,
74 | F5247A24A42C70CE5B467572 /* Frameworks */,
75 | );
76 | sourceTree = "";
77 | };
78 | 632D251E1EB81732005A4E31 /* Products */ = {
79 | isa = PBXGroup;
80 | children = (
81 | 632D251D1EB81732005A4E31 /* CognitoApplication.app */,
82 | );
83 | name = Products;
84 | sourceTree = "";
85 | };
86 | 632D251F1EB81732005A4E31 /* CognitoApplication */ = {
87 | isa = PBXGroup;
88 | children = (
89 | 63A8F7D81F311C0400AD21E0 /* Extension */,
90 | 6398A66E1EC153F0009C2D77 /* Config */,
91 | 632D25331EB95CAB005A4E31 /* Views */,
92 | 632D25201EB81732005A4E31 /* AppDelegate.swift */,
93 | 632D25241EB81732005A4E31 /* Main.storyboard */,
94 | 632D25271EB81732005A4E31 /* Assets.xcassets */,
95 | 632D25291EB81732005A4E31 /* LaunchScreen.storyboard */,
96 | 632D252C1EB81732005A4E31 /* Info.plist */,
97 | );
98 | path = CognitoApplication;
99 | sourceTree = "";
100 | };
101 | 632D25331EB95CAB005A4E31 /* Views */ = {
102 | isa = PBXGroup;
103 | children = (
104 | 63B5E4F21F33EBC300B9D1A3 /* MultiFactorAuthenticationController.swift */,
105 | 632D25341EB95CBD005A4E31 /* LoginViewController.swift */,
106 | 632D25361EB95CCE005A4E31 /* ResetPasswordViewController.swift */,
107 | 632D25381EB95CDE005A4E31 /* AppViewController.swift */,
108 | 63A8F7D41F30E79B00AD21E0 /* SignupViewController.swift */,
109 | 63A8F7D61F30FAC800AD21E0 /* VerificationViewController.swift */,
110 | 6373365D1F3252B700EA5BCE /* ForgotPasswordViewController.swift */,
111 | );
112 | name = Views;
113 | sourceTree = "";
114 | };
115 | 6398A66E1EC153F0009C2D77 /* Config */ = {
116 | isa = PBXGroup;
117 | children = (
118 | 6398A66F1EC15413009C2D77 /* CognitoConfig.plist */,
119 | 6398A6711EC1548D009C2D77 /* CognitoConfig.swift */,
120 | );
121 | name = Config;
122 | sourceTree = "";
123 | };
124 | 63A8F7D81F311C0400AD21E0 /* Extension */ = {
125 | isa = PBXGroup;
126 | children = (
127 | );
128 | name = Extension;
129 | sourceTree = "";
130 | };
131 | F5247A24A42C70CE5B467572 /* Frameworks */ = {
132 | isa = PBXGroup;
133 | children = (
134 | 50439609D2FD5FDF1A0EA3E5 /* Pods_CognitoApplication.framework */,
135 | );
136 | name = Frameworks;
137 | sourceTree = "";
138 | };
139 | /* End PBXGroup section */
140 |
141 | /* Begin PBXNativeTarget section */
142 | 632D251C1EB81732005A4E31 /* CognitoApplication */ = {
143 | isa = PBXNativeTarget;
144 | buildConfigurationList = 632D252F1EB81732005A4E31 /* Build configuration list for PBXNativeTarget "CognitoApplication" */;
145 | buildPhases = (
146 | 873D1490F79AA8373D085D9F /* [CP] Check Pods Manifest.lock */,
147 | 632D25191EB81732005A4E31 /* Sources */,
148 | 632D251A1EB81732005A4E31 /* Frameworks */,
149 | 632D251B1EB81732005A4E31 /* Resources */,
150 | 82D36ECA54A64E276AEE3212 /* [CP] Embed Pods Frameworks */,
151 | 46911EFD3642422C02B746D8 /* [CP] Copy Pods Resources */,
152 | );
153 | buildRules = (
154 | );
155 | dependencies = (
156 | );
157 | name = CognitoApplication;
158 | productName = CognitoApplication;
159 | productReference = 632D251D1EB81732005A4E31 /* CognitoApplication.app */;
160 | productType = "com.apple.product-type.application";
161 | };
162 | /* End PBXNativeTarget section */
163 |
164 | /* Begin PBXProject section */
165 | 632D25151EB81731005A4E31 /* Project object */ = {
166 | isa = PBXProject;
167 | attributes = {
168 | LastSwiftUpdateCheck = 0830;
169 | LastUpgradeCheck = 0830;
170 | ORGANIZATIONNAME = "David Tucker";
171 | TargetAttributes = {
172 | 632D251C1EB81732005A4E31 = {
173 | CreatedOnToolsVersion = 8.3.2;
174 | DevelopmentTeam = 45HB25QHYT;
175 | ProvisioningStyle = Automatic;
176 | };
177 | };
178 | };
179 | buildConfigurationList = 632D25181EB81732005A4E31 /* Build configuration list for PBXProject "CognitoApplication" */;
180 | compatibilityVersion = "Xcode 3.2";
181 | developmentRegion = English;
182 | hasScannedForEncodings = 0;
183 | knownRegions = (
184 | en,
185 | Base,
186 | );
187 | mainGroup = 632D25141EB81731005A4E31;
188 | productRefGroup = 632D251E1EB81732005A4E31 /* Products */;
189 | projectDirPath = "";
190 | projectRoot = "";
191 | targets = (
192 | 632D251C1EB81732005A4E31 /* CognitoApplication */,
193 | );
194 | };
195 | /* End PBXProject section */
196 |
197 | /* Begin PBXResourcesBuildPhase section */
198 | 632D251B1EB81732005A4E31 /* Resources */ = {
199 | isa = PBXResourcesBuildPhase;
200 | buildActionMask = 2147483647;
201 | files = (
202 | 632D252B1EB81732005A4E31 /* LaunchScreen.storyboard in Resources */,
203 | 632D25281EB81732005A4E31 /* Assets.xcassets in Resources */,
204 | 6398A6701EC15413009C2D77 /* CognitoConfig.plist in Resources */,
205 | 632D25261EB81732005A4E31 /* Main.storyboard in Resources */,
206 | );
207 | runOnlyForDeploymentPostprocessing = 0;
208 | };
209 | /* End PBXResourcesBuildPhase section */
210 |
211 | /* Begin PBXShellScriptBuildPhase section */
212 | 46911EFD3642422C02B746D8 /* [CP] Copy Pods Resources */ = {
213 | isa = PBXShellScriptBuildPhase;
214 | buildActionMask = 2147483647;
215 | files = (
216 | );
217 | inputPaths = (
218 | );
219 | name = "[CP] Copy Pods Resources";
220 | outputPaths = (
221 | );
222 | runOnlyForDeploymentPostprocessing = 0;
223 | shellPath = /bin/sh;
224 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-CognitoApplication/Pods-CognitoApplication-resources.sh\"\n";
225 | showEnvVarsInLog = 0;
226 | };
227 | 82D36ECA54A64E276AEE3212 /* [CP] Embed Pods Frameworks */ = {
228 | isa = PBXShellScriptBuildPhase;
229 | buildActionMask = 2147483647;
230 | files = (
231 | );
232 | inputPaths = (
233 | );
234 | name = "[CP] Embed Pods Frameworks";
235 | outputPaths = (
236 | );
237 | runOnlyForDeploymentPostprocessing = 0;
238 | shellPath = /bin/sh;
239 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-CognitoApplication/Pods-CognitoApplication-frameworks.sh\"\n";
240 | showEnvVarsInLog = 0;
241 | };
242 | 873D1490F79AA8373D085D9F /* [CP] Check Pods Manifest.lock */ = {
243 | isa = PBXShellScriptBuildPhase;
244 | buildActionMask = 2147483647;
245 | files = (
246 | );
247 | inputPaths = (
248 | );
249 | name = "[CP] Check Pods Manifest.lock";
250 | outputPaths = (
251 | );
252 | runOnlyForDeploymentPostprocessing = 0;
253 | shellPath = /bin/sh;
254 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n";
255 | showEnvVarsInLog = 0;
256 | };
257 | /* End PBXShellScriptBuildPhase section */
258 |
259 | /* Begin PBXSourcesBuildPhase section */
260 | 632D25191EB81732005A4E31 /* Sources */ = {
261 | isa = PBXSourcesBuildPhase;
262 | buildActionMask = 2147483647;
263 | files = (
264 | 63B5E4F31F33EBC300B9D1A3 /* MultiFactorAuthenticationController.swift in Sources */,
265 | 6373365E1F3252B700EA5BCE /* ForgotPasswordViewController.swift in Sources */,
266 | 63A8F7D51F30E79B00AD21E0 /* SignupViewController.swift in Sources */,
267 | 632D25371EB95CCE005A4E31 /* ResetPasswordViewController.swift in Sources */,
268 | 6398A6721EC1548D009C2D77 /* CognitoConfig.swift in Sources */,
269 | 632D25351EB95CBD005A4E31 /* LoginViewController.swift in Sources */,
270 | 632D25391EB95CDE005A4E31 /* AppViewController.swift in Sources */,
271 | 63A8F7D71F30FAC800AD21E0 /* VerificationViewController.swift in Sources */,
272 | 632D25211EB81732005A4E31 /* AppDelegate.swift in Sources */,
273 | );
274 | runOnlyForDeploymentPostprocessing = 0;
275 | };
276 | /* End PBXSourcesBuildPhase section */
277 |
278 | /* Begin PBXVariantGroup section */
279 | 632D25241EB81732005A4E31 /* Main.storyboard */ = {
280 | isa = PBXVariantGroup;
281 | children = (
282 | 632D25251EB81732005A4E31 /* Base */,
283 | );
284 | name = Main.storyboard;
285 | sourceTree = "";
286 | };
287 | 632D25291EB81732005A4E31 /* LaunchScreen.storyboard */ = {
288 | isa = PBXVariantGroup;
289 | children = (
290 | 632D252A1EB81732005A4E31 /* Base */,
291 | );
292 | name = LaunchScreen.storyboard;
293 | sourceTree = "";
294 | };
295 | /* End PBXVariantGroup section */
296 |
297 | /* Begin XCBuildConfiguration section */
298 | 632D252D1EB81732005A4E31 /* Debug */ = {
299 | isa = XCBuildConfiguration;
300 | buildSettings = {
301 | ALWAYS_SEARCH_USER_PATHS = NO;
302 | CLANG_ANALYZER_NONNULL = YES;
303 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
304 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
305 | CLANG_CXX_LIBRARY = "libc++";
306 | CLANG_ENABLE_MODULES = YES;
307 | CLANG_ENABLE_OBJC_ARC = YES;
308 | CLANG_WARN_BOOL_CONVERSION = YES;
309 | CLANG_WARN_CONSTANT_CONVERSION = YES;
310 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
311 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
312 | CLANG_WARN_EMPTY_BODY = YES;
313 | CLANG_WARN_ENUM_CONVERSION = YES;
314 | CLANG_WARN_INFINITE_RECURSION = YES;
315 | CLANG_WARN_INT_CONVERSION = YES;
316 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
317 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
318 | CLANG_WARN_UNREACHABLE_CODE = YES;
319 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
320 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
321 | COPY_PHASE_STRIP = NO;
322 | DEBUG_INFORMATION_FORMAT = dwarf;
323 | ENABLE_STRICT_OBJC_MSGSEND = YES;
324 | ENABLE_TESTABILITY = YES;
325 | GCC_C_LANGUAGE_STANDARD = gnu99;
326 | GCC_DYNAMIC_NO_PIC = NO;
327 | GCC_NO_COMMON_BLOCKS = YES;
328 | GCC_OPTIMIZATION_LEVEL = 0;
329 | GCC_PREPROCESSOR_DEFINITIONS = (
330 | "DEBUG=1",
331 | "$(inherited)",
332 | );
333 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
334 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
335 | GCC_WARN_UNDECLARED_SELECTOR = YES;
336 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
337 | GCC_WARN_UNUSED_FUNCTION = YES;
338 | GCC_WARN_UNUSED_VARIABLE = YES;
339 | IPHONEOS_DEPLOYMENT_TARGET = 10.3;
340 | MTL_ENABLE_DEBUG_INFO = YES;
341 | ONLY_ACTIVE_ARCH = YES;
342 | SDKROOT = iphoneos;
343 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
344 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
345 | };
346 | name = Debug;
347 | };
348 | 632D252E1EB81732005A4E31 /* Release */ = {
349 | isa = XCBuildConfiguration;
350 | buildSettings = {
351 | ALWAYS_SEARCH_USER_PATHS = NO;
352 | CLANG_ANALYZER_NONNULL = YES;
353 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
354 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
355 | CLANG_CXX_LIBRARY = "libc++";
356 | CLANG_ENABLE_MODULES = YES;
357 | CLANG_ENABLE_OBJC_ARC = YES;
358 | CLANG_WARN_BOOL_CONVERSION = YES;
359 | CLANG_WARN_CONSTANT_CONVERSION = YES;
360 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
361 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
362 | CLANG_WARN_EMPTY_BODY = YES;
363 | CLANG_WARN_ENUM_CONVERSION = YES;
364 | CLANG_WARN_INFINITE_RECURSION = YES;
365 | CLANG_WARN_INT_CONVERSION = YES;
366 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
367 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
368 | CLANG_WARN_UNREACHABLE_CODE = YES;
369 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
370 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
371 | COPY_PHASE_STRIP = NO;
372 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
373 | ENABLE_NS_ASSERTIONS = NO;
374 | ENABLE_STRICT_OBJC_MSGSEND = YES;
375 | GCC_C_LANGUAGE_STANDARD = gnu99;
376 | GCC_NO_COMMON_BLOCKS = YES;
377 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
378 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
379 | GCC_WARN_UNDECLARED_SELECTOR = YES;
380 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
381 | GCC_WARN_UNUSED_FUNCTION = YES;
382 | GCC_WARN_UNUSED_VARIABLE = YES;
383 | IPHONEOS_DEPLOYMENT_TARGET = 10.3;
384 | MTL_ENABLE_DEBUG_INFO = NO;
385 | SDKROOT = iphoneos;
386 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
387 | VALIDATE_PRODUCT = YES;
388 | };
389 | name = Release;
390 | };
391 | 632D25301EB81732005A4E31 /* Debug */ = {
392 | isa = XCBuildConfiguration;
393 | baseConfigurationReference = 5D6FA8A57C007EC439FA41F7 /* Pods-CognitoApplication.debug.xcconfig */;
394 | buildSettings = {
395 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
396 | DEVELOPMENT_TEAM = 45HB25QHYT;
397 | INFOPLIST_FILE = CognitoApplication/Info.plist;
398 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
399 | PRODUCT_BUNDLE_IDENTIFIER = net.davidtucker.CognitoApplication;
400 | PRODUCT_NAME = "$(TARGET_NAME)";
401 | SWIFT_VERSION = 3.0;
402 | };
403 | name = Debug;
404 | };
405 | 632D25311EB81732005A4E31 /* Release */ = {
406 | isa = XCBuildConfiguration;
407 | baseConfigurationReference = 88B9FE351F2439551342AEC4 /* Pods-CognitoApplication.release.xcconfig */;
408 | buildSettings = {
409 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
410 | DEVELOPMENT_TEAM = 45HB25QHYT;
411 | INFOPLIST_FILE = CognitoApplication/Info.plist;
412 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
413 | PRODUCT_BUNDLE_IDENTIFIER = net.davidtucker.CognitoApplication;
414 | PRODUCT_NAME = "$(TARGET_NAME)";
415 | SWIFT_VERSION = 3.0;
416 | };
417 | name = Release;
418 | };
419 | /* End XCBuildConfiguration section */
420 |
421 | /* Begin XCConfigurationList section */
422 | 632D25181EB81732005A4E31 /* Build configuration list for PBXProject "CognitoApplication" */ = {
423 | isa = XCConfigurationList;
424 | buildConfigurations = (
425 | 632D252D1EB81732005A4E31 /* Debug */,
426 | 632D252E1EB81732005A4E31 /* Release */,
427 | );
428 | defaultConfigurationIsVisible = 0;
429 | defaultConfigurationName = Release;
430 | };
431 | 632D252F1EB81732005A4E31 /* Build configuration list for PBXNativeTarget "CognitoApplication" */ = {
432 | isa = XCConfigurationList;
433 | buildConfigurations = (
434 | 632D25301EB81732005A4E31 /* Debug */,
435 | 632D25311EB81732005A4E31 /* Release */,
436 | );
437 | defaultConfigurationIsVisible = 0;
438 | defaultConfigurationName = Release;
439 | };
440 | /* End XCConfigurationList section */
441 | };
442 | rootObject = 632D25151EB81731005A4E31 /* Project object */;
443 | }
444 |
--------------------------------------------------------------------------------
/CognitoApplication/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 |
26 |
27 |
28 |
37 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
184 |
197 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
281 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 |
379 |
388 |
401 |
402 |
403 |
404 |
405 |
406 |
407 |
408 |
409 |
410 |
411 |
412 |
413 |
414 |
415 |
416 |
417 |
418 |
419 |
420 |
421 |
422 |
423 |
424 |
425 |
426 |
427 |
428 |
429 |
430 |
431 |
432 |
433 |
434 |
435 |
436 |
437 |
438 |
439 |
440 |
441 |
442 |
443 |
444 |
445 |
446 |
447 |
448 |
449 |
450 |
451 |
452 |
453 |
454 |
455 |
456 |
457 |
458 |
459 |
460 |
461 |
462 |
463 |
464 |
465 |
466 |
467 |
468 |
469 |
470 |
471 |
472 |
473 |
474 |
475 |
476 |
477 |
478 |
479 |
480 |
481 |
482 |
483 |
484 |
485 |
486 |
487 |
488 |
489 |
490 |
491 |
492 |
493 |
494 |
495 |
496 |
497 |
498 |
499 |
500 |
501 |
502 |
503 |
504 |
505 |
506 |
507 |
508 |
509 |
510 |
511 |
512 |
513 |
514 |
515 |
516 |
517 |
518 |
519 |
520 |
521 |
522 |
523 |
524 |
525 |
526 |
527 |
528 |
537 |
550 |
551 |
552 |
553 |
554 |
555 |
556 |
557 |
558 |
559 |
572 |
573 |
574 |
575 |
576 |
577 |
578 |
579 |
580 |
581 |
582 |
583 |
584 |
585 |
586 |
587 |
588 |
589 |
590 |
591 |
592 |
593 |
594 |
595 |
596 |
597 |
598 |
599 |
600 |
601 |
602 |
603 |
604 |
605 |
606 |
607 |
608 |
609 |
610 |
611 |
612 |
613 |
614 |
615 |
616 |
617 |
618 |
619 |
620 |
621 |
622 |
623 |
624 |
625 |
634 |
635 |
636 |
637 |
638 |
639 |
640 |
641 |
642 |
643 |
656 |
657 |
658 |
659 |
660 |
661 |
662 |
663 |
664 |
665 |
666 |
667 |
668 |
669 |
670 |
671 |
672 |
673 |
674 |
675 |
676 |
677 |
678 |
679 |
680 |
681 |
682 |
683 |
684 |
685 |
686 |
687 |
688 |
689 |
690 |
691 |
692 |
693 |
694 |
695 |
696 |
697 |
698 |
699 |
700 |
701 |
702 |
703 |
704 |
705 |
706 |
707 |
708 |
709 |
710 |
711 |
712 |
713 |
714 |
725 |
726 |
727 |
728 |
729 |
730 |
731 |
732 |
733 |
734 |
735 |
745 |
755 |
756 |
757 |
758 |
759 |
760 |
761 |
762 |
763 |
764 |
765 |
766 |
767 |
768 |
769 |
770 |
771 |
781 |
791 |
792 |
793 |
794 |
795 |
796 |
797 |
798 |
799 |
800 |
801 |
802 |
803 |
804 |
805 |
806 |
807 |
817 |
827 |
828 |
829 |
830 |
831 |
832 |
833 |
834 |
835 |
836 |
837 |
838 |
839 |
840 |
841 |
842 |
843 |
853 |
863 |
864 |
865 |
866 |
867 |
868 |
869 |
870 |
871 |
872 |
873 |
874 |
875 |
876 |
877 |
878 |
879 |
889 |
890 |
891 |
892 |
893 |
894 |
895 |
896 |
897 |
898 |
899 |
900 |
901 |
902 |
903 |
904 |
905 |
906 |
907 |
908 |
909 |
910 |
911 |
912 |
913 |
914 |
915 |
916 |
917 |
918 |
919 |
920 |
921 |
922 |
923 |
924 |
925 |
926 |
927 |
928 |
929 |
930 |
931 |
--------------------------------------------------------------------------------