├── AWSBased_SwiftAppDemo ├── ViewModels │ ├── MainViewModel.swift │ ├── SignupViewModel.swift │ └── LoginViewModel.swift ├── SessionProviders │ ├── AWSEnvironment.swift │ ├── AmazonSessionProvider.swift │ ├── CustomIdentityProvider.swift │ ├── FBSessionProvider.swift │ ├── LoginUserPoolSessionProvider.swift │ ├── SignupUserPoolSessionProvider.swift │ ├── UserPoolSessionProvider.swift │ └── AmazonClientManager.swift ├── Views │ ├── MainViewController.swift │ ├── UIViewController_ex.swift │ ├── CreateAccountViewController.swift │ ├── LoginViewController.swift │ └── Login.storyboard ├── Utils │ └── Facebook.swift ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── FlowCoordinators │ ├── MainFlowCoordinator.swift │ ├── LoginFlowCoordinator.swift │ └── RootFlowCoordinator.swift ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── DataModels │ └── CognitoUser.swift ├── Info.plist └── AppDelegate.swift ├── README.md ├── Podfile ├── LICENSE ├── Podfile.lock ├── .gitignore └── AWSBased_SwiftAppDemo.xcodeproj └── project.pbxproj /AWSBased_SwiftAppDemo/ViewModels/MainViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MainViewModel.swift 3 | // AWSBased_SwiftAppDemo 4 | // 5 | // Created by Dmytro Kabyshev on 9/22/16. 6 | // Copyright © 2016 HillTrix sp. z o.o. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | final class MainViewModel { 12 | var firstName: String? 13 | var lastName: String? 14 | 15 | init() { 16 | let user = CognitoUser.currentUser() 17 | firstName = user.firstName 18 | lastName = user.lastName 19 | } 20 | 21 | func logout() { 22 | AmazonClientManager.shared.logOut() 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AWS_APILess_SwiftApp 2 | This repository contains a complete example of iOS application (Swift) with AWS as mobile backend. 3 | 4 | ------------------------ 5 | 6 | ### [API-less backend for iOS with AWS in Swift (part 1)](https://medium.com/@dkabyshev/api-less-backend-for-ios-with-aws-in-swift-part-1-cf230f080af5#.ahbzwl3ps) 7 | #### Complete project in this [branch] (https://github.com/dkabyshev/AWS_APILess_SwiftApp/tree/part1) 8 | 9 | ------------------------ 10 | 11 | `AWS_APILess_SwiftApp` is released under an [MIT License][mitLink]. See `LICENSE` for details. 12 | 13 | >**Copyright © 2016-present Dmytro Kabyshev.** 14 | 15 | *Please provide attribution, it is greatly appreciated.* 16 | 17 | [mitLink]:http://opensource.org/licenses/MIT 18 | -------------------------------------------------------------------------------- /AWSBased_SwiftAppDemo/SessionProviders/AWSEnvironment.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AWSEnvironment.swift 3 | // AWSBased_SwiftAppDemo 4 | // 5 | // Created by Dmytro Kabyshev on 9/22/16. 6 | // Copyright © 2016 HillTrix sp. z o.o. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import AWSCore 11 | 12 | let COGNITO_REGIONTYPE = AWSRegionType.unknown // e.g. AWSRegionType.USEast1 13 | let COGNITO_REGIONTYPE_STR = "" // Your Cognito region, e.g us-east-1 14 | let COGNITO_IDENTITYPOOL_ID = "" // e.g. "us-east-1:111e111-8efa-dead-b8d7-11c7f4e00de1" 15 | let COGNITO_USERPOOL_ID = "" 16 | let COGNITO_USERPOOL_KEY = "" 17 | let COGNITO_USERPOOL_SECRET_KEY = "" 18 | let COGNITO_USERPOOL_PROVIDER = "cognito-idp.\(COGNITO_REGIONTYPE_STR).amazonaws.com/\(COGNITO_USERPOOL_ID)" 19 | -------------------------------------------------------------------------------- /AWSBased_SwiftAppDemo/Views/MainViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // AWSBased_SwiftAppDemo 4 | // 5 | // Created by Dmytro Kabyshev on 9/21/16. 6 | // Copyright © 2016 HillTrix sp. z o.o. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import AWSCognito 11 | class MainViewController: UIViewController { 12 | @IBOutlet weak var firstNameLabel: UILabel! 13 | @IBOutlet weak var lastNameLabel: UILabel! 14 | 15 | var viewModel: MainViewModel! 16 | 17 | override func viewDidLoad() { 18 | super.viewDidLoad() 19 | firstNameLabel.text = viewModel.firstName 20 | lastNameLabel.text = viewModel.lastName 21 | } 22 | 23 | @IBAction func logoutAction(_ sender: AnyObject) { 24 | viewModel.logout() 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /AWSBased_SwiftAppDemo/Views/UIViewController_ex.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewController_ex.swift 3 | // AWSBased_SwiftAppDemo 4 | // 5 | // Created by Dmytro Kabyshev on 9/22/16. 6 | // Copyright © 2016 HillTrix sp. z o.o. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIViewController { 12 | func showAlertError(message: String) { 13 | let alertController = UIAlertController(title: "Error", 14 | message: message, 15 | preferredStyle: .alert) 16 | let defaultAction = UIAlertAction(title: "OK", style: .default, handler: nil) 17 | alertController.addAction(defaultAction) 18 | present(alertController, animated: true, completion: nil) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /AWSBased_SwiftAppDemo/Utils/Facebook.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Facebook.swift 3 | // AWSBased_SwiftAppDemo 4 | // 5 | // Created by Dmytro Kabyshev on 9/22/16. 6 | // Copyright © 2016 HillTrix sp. z o.o. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import FBSDKCoreKit 11 | 12 | /** 13 | Executes one Facebook Graph request 14 | - path: a graph path for request 15 | - params: optional params to send with request 16 | - returns: Observable with DataProviderResult, in case of success the data is a dictionary 17 | */ 18 | func executeFBRequest(path: String, params: [String : String]?, completition: (([String:AnyObject]?, Error?) -> ())?){ 19 | let graphRequest: FBSDKGraphRequest = FBSDKGraphRequest(graphPath: path, parameters: params) 20 | _ = graphRequest.start {(connection, result, error) -> Void in 21 | completition?(result as? [String:AnyObject], error) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | use_frameworks! 2 | platform :ios, '9.0' 3 | source 'https://github.com/CocoaPods/Specs.git' 4 | 5 | # ignore all warnings from all pods 6 | inhibit_all_warnings! 7 | 8 | project 'AWSBased_SwiftAppDemo.xcodeproj' 9 | target 'AWSBased_SwiftAppDemo' do 10 | # AWS 11 | pod 'AWSCore', '~> 2.4.9' 12 | pod 'AWSCognitoIdentityProvider', '~> 2.4.9' 13 | pod 'AWSCognito', '~> 2.4.9' 14 | 15 | # Social 16 | pod 'FBSDKCoreKit', '~> 4.15.0' 17 | pod 'FBSDKLoginKit', '~> 4.15.0' 18 | 19 | # Few nice additions 20 | pod 'SVProgressHUD', '~> 2.0.3' 21 | pod 'R.swift', '~> 3.0.0' 22 | pod 'UICKeyChainStore', '~> 2.0.6' 23 | 24 | # To ensure we select swift version for all pod targets 25 | post_install do |installer| 26 | installer.pods_project.targets.each do |target| 27 | target.build_configurations.each do |configuration| 28 | configuration.build_settings['SWIFT_VERSION'] = "3.0" 29 | end 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /AWSBased_SwiftAppDemo/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 | } -------------------------------------------------------------------------------- /AWSBased_SwiftAppDemo/SessionProviders/AmazonSessionProvider.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AmazonSessionProvider.swift 3 | // AWSBased_SwiftAppDemo 4 | // 5 | // Created by Dmytro Kabyshev on 9/22/16. 6 | // Copyright © 2016 HillTrix sp. z o.o. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | enum AmazonSessionProviderErrors: Error { 12 | case RestoreSessionFailed 13 | case LoginFailed 14 | } 15 | 16 | extension Error { 17 | var infoMessage: String? { 18 | return (self as NSError).userInfo["message"] as? String 19 | } 20 | } 21 | 22 | protocol AmazonSessionProvider { 23 | /** 24 | Each entry in logins represents a single login with an identity provider. 25 | The key is the domain of the login provider (e.g. 'graph.facebook.com') 26 | and the value is the OAuth/OpenId Connect token that results from an authentication with that login provider. 27 | */ 28 | func getSessionTokens(completition: @escaping (([String : String]?, Error?) -> ())) 29 | func isLoggedIn() -> Bool 30 | func logout() 31 | } 32 | -------------------------------------------------------------------------------- /AWSBased_SwiftAppDemo/FlowCoordinators/MainFlowCoordinator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MainFlowCoordinator.swift 3 | // AWSBased_SwiftAppDemo 4 | // 5 | // Created by Dmytro Kabyshev on 9/21/16. 6 | // Copyright © 2016 HillTrix sp. z o.o. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Rswift 11 | import SVProgressHUD 12 | 13 | final class MainFlowCoordinator: NSObject, FlowCoordinator { 14 | private let parent: UIViewController 15 | private var main: UIViewController! 16 | 17 | var onDone: DoneHandler? 18 | 19 | init(parent: UIViewController) { 20 | self.parent = parent 21 | } 22 | 23 | internal func dismiss(completion: @escaping () -> ()) { 24 | main.dismiss(animated: false, completion: completion) 25 | } 26 | 27 | func present() { 28 | // Show app's main UI 29 | let mainView = R.storyboard.main.mainView()! 30 | mainView.viewModel = MainViewModel() 31 | main = mainView 32 | parent.present(main, animated: true, completion: nil) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /AWSBased_SwiftAppDemo/SessionProviders/CustomIdentityProvider.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CustomIdentityProvider.swift 3 | // AWSBased_SwiftAppDemo 4 | // 5 | // Created by Dmytro Kabyshev on 9/22/16. 6 | // Copyright © 2016 HillTrix sp. z o.o. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import AWSCore 11 | import AWSCognitoIdentityProvider 12 | 13 | final class CustomIdentityProvider: NSObject, AWSIdentityProviderManager { 14 | var tokens: [String : String]? 15 | 16 | init(tokens: [String : String]?) { 17 | self.tokens = tokens 18 | } 19 | 20 | /** 21 | Each entry in logins represents a single login with an identity provider. 22 | The key is the domain of the login provider (e.g. 'graph.facebook.com') 23 | and the value is the OAuth/OpenId Connect token that results from an authentication with that login provider. 24 | */ 25 | func logins() -> AWSTask { 26 | let logins: NSDictionary = NSDictionary(dictionary: tokens ?? [:]) 27 | return AWSTask(result: logins) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Dmytro Kabyshev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /AWSBased_SwiftAppDemo/SessionProviders/FBSessionProvider.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FBSessionProvider.swift 3 | // AWSBased_SwiftAppDemo 4 | // 5 | // Created by Dmytro Kabyshev on 9/22/16. 6 | // Copyright © 2016 HillTrix sp. z o.o. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import AWSCore 11 | import AWSCognito 12 | import AWSCognitoIdentityProvider 13 | import FBSDKCoreKit 14 | import FBSDKLoginKit 15 | import UICKeyChainStore 16 | 17 | let COGNITO_FB_PROVIDER = "graph.facebook.com" 18 | 19 | final class FBSessionProvider: AmazonSessionProvider { 20 | private let keyChain: UICKeyChainStore 21 | 22 | init() { 23 | keyChain = UICKeyChainStore(service: Bundle.main.bundleIdentifier!) 24 | } 25 | 26 | func getSessionTokens(completition: @escaping (([String : String]?, Error?) -> ())) { 27 | guard FBSDKAccessToken.current() != nil else { 28 | completition(nil, AmazonSessionProviderErrors.LoginFailed) 29 | return 30 | } 31 | keyChain[KEYCHAIN_PROVIDER_KEY] = AmazonSessionProviderType.FB.rawValue 32 | completition([COGNITO_FB_PROVIDER : FBSDKAccessToken.current().tokenString], nil) 33 | } 34 | 35 | func isLoggedIn() -> Bool { 36 | return FBSDKAccessToken.current() != nil 37 | } 38 | 39 | func logout() { 40 | FBSDKLoginManager().logOut() 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /AWSBased_SwiftAppDemo/Views/CreateAccountViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CreateAccountViewController.swift 3 | // AWSBased_SwiftAppDemo 4 | // 5 | // Created by Dmytro Kabyshev on 9/21/16. 6 | // Copyright © 2016 HillTrix sp. z o.o. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SVProgressHUD 11 | 12 | class CreateAccountViewController: UIViewController { 13 | @IBOutlet weak var emailTextField: UITextField! 14 | @IBOutlet weak var passwordTextField: UITextField! 15 | @IBOutlet weak var firstNameTextField: UITextField! 16 | @IBOutlet weak var lastNameTextField: UITextField! 17 | 18 | var viewModel: SignupViewModel! 19 | 20 | override func viewDidLoad() { 21 | super.viewDidLoad() 22 | viewModel.onError = { [weak self] in 23 | self?.showAlertError(message: $0) 24 | } 25 | viewModel.isLoading = { 26 | $0 == true ? SVProgressHUD.show() : SVProgressHUD.dismiss() 27 | } 28 | } 29 | 30 | override func viewWillAppear(_ animated: Bool) { 31 | super.viewWillAppear(animated) 32 | navigationController?.isNavigationBarHidden = false 33 | } 34 | 35 | @IBAction func signupAction(sender: AnyObject) { 36 | viewModel.singup(email: emailTextField.text ?? "", 37 | password: passwordTextField.text ?? "", 38 | firstName: firstNameTextField.text ?? "", 39 | lastName: lastNameTextField.text ?? "") 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /AWSBased_SwiftAppDemo/SessionProviders/LoginUserPoolSessionProvider.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LoginUserPoolSessionProvider.swift 3 | // AWSBased_SwiftAppDemo 4 | // 5 | // Created by Dmytro Kabyshev on 9/23/16. 6 | // Copyright © 2016 HillTrix sp. z o.o. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import AWSCore 11 | import AWSCognito 12 | import AWSCognitoIdentityProvider 13 | 14 | final class LoginUserPoolSessionProvider: UserPoolSessionProvider { 15 | private var username: String? 16 | private var password: String? 17 | 18 | convenience init(username: String, password: String) { 19 | self.init() 20 | self.username = username 21 | self.password = password 22 | } 23 | 24 | override func getSessionTokens(completition: @escaping (([String : String]?, Error?) -> ())) { 25 | if let username = username, let password = password { 26 | let emailAttr = AWSCognitoIdentityUserAttributeType() 27 | emailAttr?.name = "email" 28 | emailAttr?.value = username 29 | // Login existing user 30 | let user = awsUserPool.getUser(username) 31 | completeWith(task: user.getSession(username, password: password, validationData: [emailAttr!]), 32 | completition: completition) 33 | self.username = nil 34 | self.password = nil 35 | } else { 36 | super.getSessionTokens(completition: completition) 37 | } 38 | } 39 | 40 | override func logout() { 41 | super.logout() 42 | username = nil 43 | password = nil 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - AWSCognito (2.4.9): 3 | - AWSCore (= 2.4.9) 4 | - AWSCognitoIdentityProvider (2.4.9): 5 | - AWSCore (= 2.4.9) 6 | - AWSCore (2.4.9) 7 | - Bolts (1.8.4): 8 | - Bolts/AppLinks (= 1.8.4) 9 | - Bolts/Tasks (= 1.8.4) 10 | - Bolts/AppLinks (1.8.4): 11 | - Bolts/Tasks 12 | - Bolts/Tasks (1.8.4) 13 | - FBSDKCoreKit (4.15.1): 14 | - Bolts (~> 1.7) 15 | - FBSDKLoginKit (4.15.1): 16 | - FBSDKCoreKit 17 | - R.swift (3.0.0): 18 | - R.swift.Library (~> 3.0.0) 19 | - R.swift.Library (3.0.1) 20 | - SVProgressHUD (2.0.3) 21 | - UICKeyChainStore (2.0.7) 22 | 23 | DEPENDENCIES: 24 | - AWSCognito (~> 2.4.9) 25 | - AWSCognitoIdentityProvider (~> 2.4.9) 26 | - AWSCore (~> 2.4.9) 27 | - FBSDKCoreKit (~> 4.15.0) 28 | - FBSDKLoginKit (~> 4.15.0) 29 | - R.swift (~> 3.0.0) 30 | - SVProgressHUD (~> 2.0.3) 31 | - UICKeyChainStore (~> 2.0.6) 32 | 33 | SPEC CHECKSUMS: 34 | AWSCognito: 4fa9fefcadae1d8425b01cbde27d564c21e3fd35 35 | AWSCognitoIdentityProvider: 34707960c3bd3f4956cb3b9a129dfda12376bd40 36 | AWSCore: 175208358b95492e0ebe0337df2ab415300bddac 37 | Bolts: 8a7995239dbe724f9cba2248b766d48b7ebdd322 38 | FBSDKCoreKit: fc7fa53d16b095ee3fd53f0cd59f1df8bcbb634d 39 | FBSDKLoginKit: 073f79b3b2e762fc6f54a283fe15f45372d59f5f 40 | R.swift: 5a8b4a4ea565b0f20c39d8ce1445f3d3a9064768 41 | R.swift.Library: 0869d8d546517aa79db078fd8262724391a38db8 42 | SVProgressHUD: b0830714205bea1317ea1a2ebc71e5633af334d4 43 | UICKeyChainStore: 696cd35083f7c8fb5e201d909b7e6aea6b478575 44 | 45 | PODFILE CHECKSUM: c017c600b66071959253f15a3ea2776a0e0ae6bf 46 | 47 | COCOAPODS: 1.0.1 48 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | 29 | ## Playgrounds 30 | timeline.xctimeline 31 | playground.xcworkspace 32 | 33 | # Swift Package Manager 34 | # 35 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 36 | # Packages/ 37 | .build/ 38 | 39 | # CocoaPods 40 | # 41 | # We recommend against adding the Pods directory to your .gitignore. However 42 | # you should judge for yourself, the pros and cons are mentioned at: 43 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 44 | # 45 | Pods/ 46 | 47 | # Carthage 48 | # 49 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 50 | # Carthage/Checkouts 51 | 52 | Carthage/Build 53 | 54 | # fastlane 55 | # 56 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 57 | # screenshots whenever they are needed. 58 | # For more information about the recommended setup visit: 59 | # https://github.com/fastlane/fastlane/blob/master/docs/Gitignore.md 60 | 61 | fastlane/report.xml 62 | fastlane/screenshots 63 | 64 | # R swift 65 | *.generated.swift -------------------------------------------------------------------------------- /AWSBased_SwiftAppDemo/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 | -------------------------------------------------------------------------------- /AWSBased_SwiftAppDemo/FlowCoordinators/LoginFlowCoordinator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LoginFlowCoordinator.swift 3 | // AWSBased_SwiftAppDemo 4 | // 5 | // Created by Dmytro Kabyshev on 9/21/16. 6 | // Copyright © 2016 HillTrix sp. z o.o. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Rswift 11 | import AWSCore 12 | import AWSCognitoIdentityProvider 13 | import SVProgressHUD 14 | 15 | final class LoginFlowCoordinator: NSObject, FlowCoordinator { 16 | private let parent: UIViewController 17 | private var navigation: UINavigationController! 18 | 19 | var onDone: DoneHandler? 20 | 21 | init(parent: UIViewController) { 22 | self.parent = parent 23 | } 24 | 25 | internal func dismiss(completion: @escaping () -> ()) { 26 | navigation.popToRootViewController(animated: false) 27 | navigation.dismiss(animated: false, completion: completion) 28 | } 29 | 30 | func present() { 31 | let login = R.storyboard.login.loginView()! 32 | navigation = UINavigationController(rootViewController: login) 33 | let handleLoginDone = { [unowned self] in 34 | let _ = self.navigation.popToRootViewController(animated: false) 35 | self.navigation.dismiss(animated: false) { 36 | self.onDone?() 37 | } 38 | } 39 | login.viewModel = LoginViewModel() 40 | login.viewModel.onDone = handleLoginDone 41 | login.onCreareAccount = { [unowned self] in 42 | // Show create account screen 43 | let createAccount = R.storyboard.login.createAccount()! 44 | createAccount.viewModel = SignupViewModel() 45 | createAccount.viewModel.onDone = handleLoginDone 46 | self.navigation.pushViewController(createAccount, animated: true) 47 | } 48 | parent.present(navigation, animated: true, completion: nil) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /AWSBased_SwiftAppDemo/ViewModels/SignupViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SignupViewModel.swift 3 | // AWSBased_SwiftAppDemo 4 | // 5 | // Created by Dmytro Kabyshev on 9/22/16. 6 | // Copyright © 2016 HillTrix sp. z o.o. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | final class SignupViewModel { 12 | var isLoading: ((Bool) -> ())? 13 | var onError: ((String) -> ())? 14 | var onDone: (() -> ())? 15 | 16 | init() { 17 | } 18 | 19 | func singup(email: String, password: String, firstName: String, lastName: String) { 20 | guard !email.isEmpty && !password.isEmpty && !firstName.isEmpty && !lastName.isEmpty else { 21 | onError?("All fields are required.") 22 | return 23 | } 24 | guard password.characters.count > 5 else { 25 | onError?("Password is too short (at least 6 symbols).") 26 | return 27 | } 28 | 29 | isLoading?(true) 30 | let sessionProvider = SignupUserPoolSessionProvider(username: email, password: password) 31 | AmazonClientManager.shared.login(sessionProvider: sessionProvider) { [weak self] (error) in 32 | guard error == nil else { 33 | self?.isLoading?(false) 34 | AmazonClientManager.shared.clearAll() 35 | self?.onError?(error?.infoMessage ?? "Failed to create new user") 36 | return 37 | } 38 | // Save new user 39 | let user = CognitoUser() 40 | user.firstName = firstName 41 | user.lastName = lastName 42 | user.email = email 43 | // Save info and proceed 44 | user.save() { (done) in 45 | self?.isLoading?(false) 46 | guard done else { 47 | AmazonClientManager.shared.clearAll() 48 | self?.onError?(error?.infoMessage ?? "Failed to create new user") 49 | return 50 | } 51 | self?.onDone?() 52 | } 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /AWSBased_SwiftAppDemo/SessionProviders/SignupUserPoolSessionProvider.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SignupUserPoolSessionProvider.swift 3 | // AWSBased_SwiftAppDemo 4 | // 5 | // Created by Dmytro Kabyshev on 9/23/16. 6 | // Copyright © 2016 HillTrix sp. z o.o. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import AWSCore 11 | import AWSCognito 12 | import AWSCognitoIdentityProvider 13 | 14 | final class SignupUserPoolSessionProvider: UserPoolSessionProvider { 15 | private var username: String? 16 | private var password: String? 17 | 18 | convenience init(username: String, password: String) { 19 | self.init() 20 | self.username = username 21 | self.password = password 22 | } 23 | 24 | override func getSessionTokens(completition: @escaping (([String : String]?, Error?) -> ())) { 25 | if let username = username, let password = password { 26 | let emailAttr = AWSCognitoIdentityUserAttributeType() 27 | emailAttr?.name = "email" 28 | emailAttr?.value = username 29 | // Signup new user 30 | awsUserPool.signUp(username, password: password, userAttributes: [emailAttr!], validationData: nil) 31 | .continue({ [weak self] (task) in 32 | guard let user = self?.awsUserPool.getUser(username), task.error == nil else { 33 | completition(nil, task.error) 34 | return nil 35 | } 36 | 37 | self?.completeWith(task: user.getSession(username, password: password, validationData: [emailAttr!]), 38 | completition: completition) 39 | self?.username = nil 40 | self?.password = nil 41 | return nil 42 | }) 43 | } else { 44 | super.getSessionTokens(completition: completition) 45 | } 46 | } 47 | 48 | override func logout() { 49 | super.logout() 50 | username = nil 51 | password = nil 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /AWSBased_SwiftAppDemo/DataModels/CognitoUser.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CognitoUser.swift 3 | // AWSBased_SwiftAppDemo 4 | // 5 | // Created by Dmytro Kabyshev on 9/21/16. 6 | // Copyright © 2016 HillTrix sp. z o.o. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import AWSCore 11 | import AWSCognito 12 | 13 | internal let cognitoSyncDataset = "userProfile" 14 | 15 | final class CognitoUser { 16 | var userId: String? 17 | var email: String? 18 | var firstName: String? 19 | var lastName: String? 20 | 21 | init() { 22 | userId = NSUUID().uuidString 23 | } 24 | } 25 | 26 | extension CognitoUser { 27 | // Save all user's data to CognitoSync 28 | func save(completition: @escaping ((Bool) -> ())) { 29 | // openOrCreateDataset - creates a dataset if it doesn't exists or open existing 30 | let dataset = AWSCognito(forKey: cognitoSyncKey).openOrCreateDataset(cognitoSyncDataset) 31 | dataset?.setString(firstName, forKey: "firstName") 32 | dataset?.setString(lastName, forKey: "lastName") 33 | dataset?.setString(email, forKey: "email") 34 | CognitoUser.sync(completition: completition) 35 | } 36 | 37 | static func sync(completition: @escaping ((Bool) -> ())) { 38 | // openOrCreateDataset - creates a dataset if it doesn't exists or open existing 39 | let dataset = AWSCognito(forKey: cognitoSyncKey).openOrCreateDataset(cognitoSyncDataset) 40 | // synchronize is going to: 41 | // 1) Fetch from AWS, if there were any changes 42 | // 2) Upload local changes to AWS 43 | dataset?.synchronize().continue({ (task) in 44 | DispatchQueue.main.async { completition(task.error == nil) } 45 | return nil 46 | }) 47 | } 48 | 49 | static func currentUser(firstName: String? = nil, lastName: String? = nil) -> CognitoUser { 50 | let dataset = AWSCognito(forKey: cognitoSyncKey).openOrCreateDataset(cognitoSyncDataset) 51 | let user = CognitoUser() 52 | user.userId = dataset?.string(forKey: "userId") ?? NSUUID().uuidString 53 | user.firstName = dataset?.string(forKey:"firstName") 54 | user.lastName = dataset?.string(forKey:"lastName") 55 | user.email = dataset?.string(forKey:"email") 56 | return user 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /AWSBased_SwiftAppDemo/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 | FacebookAppID 24 | xxxxxxxxxxxxxx 25 | CFBundleURLTypes 26 | 27 | 28 | CFBundleURLSchemes 29 | 30 | fbxxxxxxxxxxxxxxxx 31 | 32 | 33 | 34 | UILaunchStoryboardName 35 | LaunchScreen 36 | UIMainStoryboardFile 37 | Main 38 | UIRequiredDeviceCapabilities 39 | 40 | armv7 41 | 42 | UISupportedInterfaceOrientations 43 | 44 | UIInterfaceOrientationPortrait 45 | UIInterfaceOrientationLandscapeLeft 46 | UIInterfaceOrientationLandscapeRight 47 | 48 | LSApplicationQueriesSchemes 49 | 50 | fbapi 51 | fb-messenger-api 52 | fbauth2 53 | fbshareextension 54 | 55 | NSAppTransportSecurity 56 | 57 | NSExceptionDomains 58 | 59 | akamaihd.net 60 | 61 | NSIncludesSubdomains 62 | 63 | NSThirdPartyExceptionRequiresForwardSecrecy 64 | 65 | 66 | facebook.com 67 | 68 | NSIncludesSubdomains 69 | 70 | NSThirdPartyExceptionRequiresForwardSecrecy 71 | 72 | 73 | fbcdn.net 74 | 75 | NSIncludesSubdomains 76 | 77 | NSThirdPartyExceptionRequiresForwardSecrecy 78 | 79 | 80 | 81 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /AWSBased_SwiftAppDemo/SessionProviders/UserPoolSessionProvider.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UserPoolSessionProvider.swift 3 | // AWSBased_SwiftAppDemo 4 | // 5 | // Created by Dmytro Kabyshev on 9/22/16. 6 | // Copyright © 2016 HillTrix sp. z o.o. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import AWSCore 11 | import AWSCognito 12 | import AWSCognitoIdentityProvider 13 | import UICKeyChainStore 14 | 15 | let KEYCHAIN_SESSION_KEY = "CognitoPoolKey" 16 | 17 | class UserPoolSessionProvider: AmazonSessionProvider { 18 | internal let awsUserPool: AWSCognitoIdentityUserPool 19 | private let keyChain: UICKeyChainStore 20 | 21 | init() { 22 | // Initialize AWS User Pool with separate configuration and empty credentials 23 | keyChain = UICKeyChainStore(service: Bundle.main.bundleIdentifier!) 24 | let configuration = AWSServiceConfiguration(region: COGNITO_REGIONTYPE, credentialsProvider: nil) 25 | let config = AWSCognitoIdentityUserPoolConfiguration(clientId: COGNITO_USERPOOL_KEY, 26 | clientSecret: COGNITO_USERPOOL_SECRET_KEY, 27 | poolId: COGNITO_USERPOOL_ID) 28 | AWSCognitoIdentityUserPool.register(with: configuration,userPoolConfiguration: config, forKey: "userpool") 29 | awsUserPool = AWSCognitoIdentityUserPool(forKey: "userpool") 30 | } 31 | 32 | func getSessionTokens(completition: @escaping (([String : String]?, Error?) -> ())) { 33 | if let user = awsUserPool.currentUser() { 34 | // Try to restore prev. session 35 | completeWith(task: user.getSession(), completition: completition) 36 | } else { 37 | completition(nil, AmazonSessionProviderErrors.RestoreSessionFailed) 38 | } 39 | } 40 | 41 | internal func completeWith(task: AWSTask, 42 | completition: @escaping (([String : String]?, Error?) -> ())) { 43 | // Execute AWSTask to get AWSCognitoIdentityUserSession, usualy getSession(...) 44 | task.continue({ [weak self] (task) in 45 | guard let session = task.result, task.error == nil else { 46 | completition(nil, task.error) 47 | return nil 48 | } 49 | self?.keyChain[KEYCHAIN_PROVIDER_KEY] = AmazonSessionProviderType.USERPOOL.rawValue 50 | self?.keyChain[KEYCHAIN_SESSION_KEY] = session.idToken?.tokenString ?? "" 51 | completition([COGNITO_USERPOOL_PROVIDER : session.idToken?.tokenString ?? ""], nil) 52 | return nil 53 | }) 54 | } 55 | 56 | func isLoggedIn() -> Bool { 57 | return awsUserPool.currentUser()?.isSignedIn ?? false 58 | } 59 | 60 | func logout() { 61 | keyChain[KEYCHAIN_SESSION_KEY] = nil 62 | awsUserPool.clearAll() 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /AWSBased_SwiftAppDemo/Views/LoginViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LoginViewController.swift 3 | // AWSBased_SwiftAppDemo 4 | // 5 | // Created by Dmytro Kabyshev on 9/21/16. 6 | // Copyright © 2016 HillTrix sp. z o.o. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import FBSDKCoreKit 11 | import FBSDKLoginKit 12 | import SVProgressHUD 13 | 14 | class LoginViewController: UIViewController { 15 | @IBOutlet weak var facebookLoginButton: UIButton! 16 | @IBOutlet weak var createAccountButton: UIButton! 17 | @IBOutlet weak var loginWithEmailButton: UIButton! 18 | @IBOutlet weak var emailTextField: UITextField! 19 | @IBOutlet weak var passwordTextField: UITextField! 20 | 21 | var onCreareAccount: (() -> ())? 22 | 23 | var viewModel: LoginViewModel! 24 | 25 | override func viewDidLoad() { 26 | super.viewDidLoad() 27 | viewModel.onError = { [weak self] in 28 | self?.showAlertError(message: $0) 29 | } 30 | viewModel.isLoading = { 31 | $0 == true ? SVProgressHUD.show() : SVProgressHUD.dismiss() 32 | } 33 | } 34 | 35 | override func viewWillAppear(_ animated: Bool) { 36 | navigationController?.isNavigationBarHidden = true 37 | } 38 | } 39 | 40 | extension LoginViewController { 41 | @IBAction func createAccountAction(sender: AnyObject) { 42 | onCreareAccount?() 43 | } 44 | 45 | @IBAction func loginWithEmailAction(sender: AnyObject) { 46 | viewModel.login(email: emailTextField.text ?? "", password: passwordTextField.text ?? "") 47 | } 48 | 49 | @IBAction func loginWithFacebookAction(sender: AnyObject) { 50 | loginToFacebookWithSuccess(successBlock: { [weak self] (token) -> () in 51 | self?.viewModel.login(fbToken: token) 52 | }, andFailure: { _ in 53 | // let murmur = Murmur(title: "Failed to login via Facebook", 54 | // backgroundColor: UIColor.snwSalmon15Color(), 55 | // titleColor: UIColor.snwOrangeyRedColor(), 56 | // font: UIFont(name: "AvalonT", size: 12)!) 57 | // delay(1) { 58 | // MainQueue.run() { 59 | // show(whistle: murmur, action: .Show(0.5)) 60 | // } 61 | // } 62 | }) 63 | } 64 | } 65 | 66 | extension LoginViewController { 67 | func loginToFacebookWithSuccess(successBlock: @escaping (String) -> (), 68 | andFailure failureBlock: @escaping (NSError?) -> ()) { 69 | let facebookReadPermissions = ["public_profile", "email"] 70 | if FBSDKAccessToken.current() != nil { 71 | //For debugging, when we want to ensure that facebook login always happens 72 | FBSDKLoginManager().logOut() 73 | } 74 | 75 | FBSDKLoginManager().logIn(withReadPermissions: facebookReadPermissions, from: self) { (result, error) in 76 | guard let token = result?.token.tokenString, error == nil else { 77 | // Handle cancellations 78 | FBSDKLoginManager().logOut() 79 | failureBlock(nil) 80 | return 81 | } 82 | successBlock(token) 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /AWSBased_SwiftAppDemo/FlowCoordinators/RootFlowCoordinator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RootFlowCoordinator.swift 3 | // AWSBased_SwiftAppDemo 4 | // 5 | // Created by Dmytro Kabyshev on 9/21/16. 6 | // Copyright © 2016 HillTrix sp. z o.o. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import AWSCore 11 | import AWSCognito 12 | import AWSCognitoIdentityProvider 13 | import SVProgressHUD 14 | 15 | typealias DoneHandler = (() -> ()) 16 | 17 | protocol FlowCoordinator { 18 | func present() 19 | func dismiss(completion: @escaping () -> ()) 20 | } 21 | 22 | // --- The purpose of this class is to instantiate the first flow, it could be either login or main 23 | final class RootFlowCoordinator: NSObject { 24 | private let parent: UIViewController 25 | private var topFlowCoordinator: FlowCoordinator? 26 | private var loginFlowCoordinator: FlowCoordinator? 27 | 28 | init(parent: UIViewController) { 29 | self.parent = parent 30 | super.init() 31 | 32 | NotificationCenter.default.addObserver(self, selector: #selector(didLogoutHandler), 33 | name: AmazonClientManagerDidLogoutNotification, object: nil) 34 | } 35 | 36 | deinit { 37 | NotificationCenter.default.removeObserver(self) 38 | } 39 | 40 | func didLogoutHandler() { 41 | topFlowCoordinator?.dismiss() { [weak self] in 42 | self?.present() 43 | } 44 | } 45 | 46 | func present() { 47 | let loginFlowBlock = { [unowned self] in 48 | self.topFlowCoordinator = self.loginFlow(parent: self.parent) 49 | self.topFlowCoordinator?.present() 50 | } 51 | // Check if we logged-in and can resume session 52 | if AmazonClientManager.shared.isLoggedIn() { 53 | SVProgressHUD.show() 54 | AmazonClientManager.shared.resumeSession() { [unowned self] (error) in 55 | guard error == nil else { 56 | SVProgressHUD.dismiss() 57 | loginFlowBlock() 58 | return 59 | } 60 | CognitoUser.sync() { _ in 61 | SVProgressHUD.dismiss() 62 | self.topFlowCoordinator = MainFlowCoordinator(parent: self.parent) 63 | self.topFlowCoordinator?.present() 64 | } 65 | } 66 | } else { 67 | loginFlowBlock() 68 | } 69 | } 70 | 71 | private func loginFlow(parent: UIViewController) -> LoginFlowCoordinator { 72 | let login = LoginFlowCoordinator(parent: parent) 73 | login.onDone = { 74 | SVProgressHUD.show() 75 | CognitoUser.sync() { _ in 76 | SVProgressHUD.dismiss() 77 | self.topFlowCoordinator = MainFlowCoordinator(parent: self.parent) 78 | self.topFlowCoordinator?.present() 79 | } 80 | } 81 | return login 82 | } 83 | 84 | private func presentLogin() -> LoginFlowCoordinator { 85 | if let auth = topFlowCoordinator as? LoginFlowCoordinator { 86 | return auth 87 | } 88 | let login = loginFlow(parent: UIApplication.topViewController() ?? parent) 89 | loginFlowCoordinator = login 90 | DispatchQueue.main.async { 91 | self.loginFlowCoordinator?.present() 92 | } 93 | return login 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /AWSBased_SwiftAppDemo/ViewModels/LoginViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LoginViewModel.swift 3 | // AWSBased_SwiftAppDemo 4 | // 5 | // Created by Dmytro Kabyshev on 9/22/16. 6 | // Copyright © 2016 HillTrix sp. z o.o. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | final class LoginViewModel { 12 | var isLoading: ((Bool) -> ())? 13 | var onError: ((String) -> ())? 14 | var onDone: (() -> ())? 15 | 16 | init() { 17 | } 18 | 19 | func login(email: String, password: String) { 20 | guard !email.isEmpty && !password.isEmpty else { 21 | onError?("All fields are required.") 22 | return 23 | } 24 | let sessionProvider = LoginUserPoolSessionProvider(username: email, password: password) 25 | AmazonClientManager.shared.login(sessionProvider: sessionProvider) { [unowned self] (error) in 26 | guard error == nil else { 27 | AmazonClientManager.shared.clearAll() 28 | DispatchQueue.main.async { [weak self] in 29 | self?.onError?(error?.infoMessage ?? "Failed to login") 30 | } 31 | return 32 | } 33 | self.onDone?() 34 | } 35 | } 36 | 37 | func login(fbToken: String) { 38 | isLoading?(true) 39 | let params = ["fields": "id, email, first_name, last_name"] 40 | let sessionProvider = FBSessionProvider() 41 | AmazonClientManager.shared.login(sessionProvider: sessionProvider) { [unowned self] (error) in 42 | guard error == nil else { 43 | AmazonClientManager.shared.clearAll() 44 | DispatchQueue.main.async { [weak self] in 45 | self?.onError?(error?.infoMessage ?? "Failed to login with Facebook") 46 | } 47 | return 48 | } 49 | // Sync user profile from CognitoSync storage 50 | CognitoUser.sync() { [unowned self] _ in 51 | let user = CognitoUser.currentUser() 52 | if user.firstName == nil || user.lastName == nil { 53 | // Fetch basic info from Facebook, e.g. first and last name 54 | executeFBRequest(path: "me", params: params) { (data, error) in 55 | if error != nil { 56 | DispatchQueue.main.async { [weak self] in 57 | self?.onError?(error?.infoMessage ?? "Failed to login with Facebook") 58 | } 59 | } 60 | user.firstName = data?["first_name"] as? String 61 | user.lastName = data?["last_name"] as? String 62 | user.email = data?["email"] as? String 63 | // Save info and proceed 64 | user.save() { (done) in 65 | guard done == true else { 66 | DispatchQueue.main.async { [weak self] in 67 | self?.onError?("Failed to login with Facebook") 68 | } 69 | return 70 | } 71 | self.onDone?() 72 | } 73 | } 74 | } else { 75 | self.onDone?() 76 | } 77 | } 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /AWSBased_SwiftAppDemo/SessionProviders/AmazonClientManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AmazonClientManager.swift 3 | // AWSBased_SwiftAppDemo 4 | // 5 | // Created by Dmytro Kabyshev on 9/22/16. 6 | // Copyright © 2016 HillTrix sp. z o.o. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import AWSCore 11 | import AWSCognito 12 | import AWSCognitoIdentityProvider 13 | import UICKeyChainStore 14 | 15 | let cognitoSyncKey = "cognitoSync" 16 | let AmazonClientManagerDidLogoutNotification = Notification.Name("AmazonClientManagerDidLogoutNotification") 17 | let KEYCHAIN_PROVIDER_KEY = "KEYCHAIN_PROVIDER_KEY" 18 | typealias AmazonClientCompletition = ((Error?) -> ()) 19 | 20 | enum AmazonSessionProviderType: String { 21 | case FB, USERPOOL 22 | } 23 | 24 | final class AmazonClientManager { 25 | static let shared = AmazonClientManager() 26 | 27 | private let keyChain: UICKeyChainStore 28 | private var credentialsProvider: AWSCognitoCredentialsProvider? 29 | private var sessionProvider: AmazonSessionProvider? 30 | private let customIdentityProvider: CustomIdentityProvider 31 | 32 | var currentIdentity: String? { 33 | return credentialsProvider?.identityId 34 | } 35 | 36 | init() { 37 | keyChain = UICKeyChainStore(service: Bundle.main.bundleIdentifier!) 38 | self.customIdentityProvider = CustomIdentityProvider(tokens: nil) 39 | #if DEBUG 40 | AWSLogger.default().logLevel = .verbose 41 | #endif 42 | // Check if we have session indicator stored 43 | if keyChain[KEYCHAIN_PROVIDER_KEY] == AmazonSessionProviderType.FB.rawValue { 44 | sessionProvider = FBSessionProvider() 45 | } else if keyChain[KEYCHAIN_PROVIDER_KEY] == AmazonSessionProviderType.USERPOOL.rawValue { 46 | sessionProvider = UserPoolSessionProvider() 47 | } 48 | } 49 | 50 | // Tries to initiate a session with given session provider returns an error otherwise 51 | func login(sessionProvider: AmazonSessionProvider, completion: AmazonClientCompletition?) { 52 | self.sessionProvider = sessionProvider 53 | self.resumeSession(completion: completion) 54 | } 55 | 56 | // Tries to restore session or returns an error 57 | func resumeSession(completion: AmazonClientCompletition?) { 58 | if let sessionProvider = sessionProvider { 59 | sessionProvider.getSessionTokens() { [unowned self] (tokens, error) in 60 | guard let tokens = tokens, error == nil else { 61 | completion?(error) 62 | return 63 | } 64 | self.completeLogin(logins: tokens, completition: completion) 65 | } 66 | } else { 67 | completion?(AmazonSessionProviderErrors.RestoreSessionFailed) 68 | } 69 | } 70 | 71 | // Removes all associated session and cleans keychain 72 | func clearAll() { 73 | if let cognito = AWSCognito(forKey: cognitoSyncKey) { 74 | cognito.wipe() 75 | } 76 | sessionProvider?.logout() 77 | keyChain.removeAllItems() 78 | credentialsProvider?.clearKeychain() 79 | credentialsProvider?.clearCredentials() 80 | sessionProvider = nil 81 | customIdentityProvider.tokens = nil 82 | } 83 | 84 | func logOut() { 85 | clearAll() 86 | DispatchQueue.main.async { 87 | NotificationCenter.default.post(name: AmazonClientManagerDidLogoutNotification, object: nil) 88 | } 89 | } 90 | 91 | // Checks if we have logged in before 92 | func isLoggedIn() -> Bool { 93 | return sessionProvider?.isLoggedIn() ?? false 94 | } 95 | 96 | private func completeLogin(logins: [String : String]?, completition: AmazonClientCompletition?) { 97 | // Here we setup our default configuration with Credentials Provider, which uses our custom Identity Provider 98 | customIdentityProvider.tokens = logins 99 | self.credentialsProvider = AWSCognitoCredentialsProvider(regionType: COGNITO_REGIONTYPE, 100 | identityPoolId: COGNITO_IDENTITYPOOL_ID, 101 | identityProviderManager: customIdentityProvider) 102 | 103 | self.credentialsProvider?.getIdentityId().continue({ (task) in 104 | if (task.error != nil) { 105 | completition?(task.error) 106 | } else { 107 | let configuration = AWSServiceConfiguration(region: COGNITO_REGIONTYPE, 108 | credentialsProvider: self.credentialsProvider) 109 | 110 | // Save confuguration as default for all AWS services 111 | // It can be set only once, any subsequent setters are ignored. 112 | AWSServiceManager.default().defaultServiceConfiguration = configuration 113 | AWSCognito.register(with: configuration, forKey: cognitoSyncKey) 114 | 115 | completition?(nil) 116 | } 117 | return task 118 | }) 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /AWSBased_SwiftAppDemo/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // AWSBased_SwiftAppDemo 4 | // 5 | // Created by Dmytro Kabyshev on 9/21/16. 6 | // Copyright © 2016 HillTrix sp. z o.o. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import FBSDKCoreKit 11 | import FBSDKLoginKit 12 | import SVProgressHUD 13 | 14 | typealias ViewController = UIViewController 15 | @UIApplicationMain 16 | class AppDelegate: UIResponder, UIApplicationDelegate { 17 | 18 | var window: UIWindow? 19 | var rootFlowCoordinator: RootFlowCoordinator! 20 | 21 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 22 | FBSDKApplicationDelegate.sharedInstance() 23 | .application(application, didFinishLaunchingWithOptions: launchOptions) 24 | // Optionally add to ensure your credentials are valid: 25 | FBSDKLoginManager.renewSystemCredentials { 26 | (result, error) in 27 | } 28 | 29 | SVProgressHUD.setDefaultMaskType(.gradient) 30 | 31 | self.window = UIWindow(frame: UIScreen.main.bounds) 32 | // We have a dedicated dump viewcontroller to use as bottom cover for switching screens 33 | let root = R.storyboard.main.rootView()! 34 | rootFlowCoordinator = RootFlowCoordinator(parent: root) 35 | self.window?.rootViewController = root 36 | self.window?.makeKeyAndVisible() 37 | 38 | DispatchQueue.main.async { 39 | self.rootFlowCoordinator.present() // start the root flow 40 | } 41 | return FBSDKApplicationDelegate.sharedInstance().application(application, didFinishLaunchingWithOptions: launchOptions) 42 | } 43 | 44 | func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool { 45 | // Even though the Facebook SDK can make this determinitaion on its own, 46 | // let's make sure that the facebook SDK only sees urls intended for it, 47 | // facebook has enough info already! 48 | 49 | let isFacebookURL = (url.scheme?.hasPrefix("fb\(FBSDKSettings.appID() ?? "")") ?? false) && url.host == "authorize" 50 | if isFacebookURL { 51 | return FBSDKApplicationDelegate.sharedInstance().application(application, 52 | open: url, 53 | sourceApplication: sourceApplication, 54 | annotation: annotation) 55 | } 56 | 57 | return false 58 | } 59 | 60 | func applicationWillResignActive(_ application: UIApplication) { 61 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 62 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 63 | } 64 | 65 | func applicationDidEnterBackground(_ application: UIApplication) { 66 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 67 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 68 | } 69 | 70 | func applicationWillEnterForeground(_ application: UIApplication) { 71 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. 72 | } 73 | 74 | func applicationDidBecomeActive(_ application: UIApplication) { 75 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 76 | } 77 | 78 | func applicationWillTerminate(_ application: UIApplication) { 79 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 80 | } 81 | 82 | 83 | } 84 | 85 | extension UIApplication { 86 | static var activity: Bool { 87 | get { 88 | return UIApplication.shared.isNetworkActivityIndicatorVisible 89 | } 90 | set(newValue) { 91 | UIApplication.shared.isNetworkActivityIndicatorVisible = newValue 92 | } 93 | } 94 | 95 | class func topViewController(base: UIViewController? = nil) -> UIViewController? { 96 | let currentBase = base == nil ? (UIApplication.shared.delegate as? AppDelegate)?.window?.rootViewController : base 97 | if let nav = currentBase as? UINavigationController { 98 | return topViewController(base: nav.visibleViewController) 99 | } 100 | if let tab = currentBase as? UITabBarController { 101 | if let selected = tab.selectedViewController { 102 | return topViewController(base: selected) 103 | } 104 | } 105 | if let presented = currentBase?.presentedViewController { 106 | return topViewController(base: presented) 107 | } 108 | return currentBase 109 | } 110 | } 111 | 112 | -------------------------------------------------------------------------------- /AWSBased_SwiftAppDemo/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 | 32 | 40 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 115 | 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 | -------------------------------------------------------------------------------- /AWSBased_SwiftAppDemo.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 80C02349F622402AB559CCFB /* Pods_AWSBased_SwiftAppDemo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DA43FC843ECF3D7179DCF5B3 /* Pods_AWSBased_SwiftAppDemo.framework */; }; 11 | 932A9BC31D92DC0B00A9687B /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 932A9BC21D92DC0B00A9687B /* AppDelegate.swift */; }; 12 | 932A9BC81D92DC0B00A9687B /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 932A9BC61D92DC0B00A9687B /* Main.storyboard */; }; 13 | 932A9BCA1D92DC0B00A9687B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 932A9BC91D92DC0B00A9687B /* Assets.xcassets */; }; 14 | 932A9BCD1D92DC0B00A9687B /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 932A9BCB1D92DC0B00A9687B /* LaunchScreen.storyboard */; }; 15 | 932A9BDD1D92DC6B00A9687B /* LoginFlowCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 932A9BD51D92DC6B00A9687B /* LoginFlowCoordinator.swift */; }; 16 | 932A9BDE1D92DC6B00A9687B /* MainFlowCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 932A9BD61D92DC6B00A9687B /* MainFlowCoordinator.swift */; }; 17 | 932A9BDF1D92DC6B00A9687B /* RootFlowCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 932A9BD71D92DC6B00A9687B /* RootFlowCoordinator.swift */; }; 18 | 932A9BE01D92DC6B00A9687B /* CreateAccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 932A9BD91D92DC6B00A9687B /* CreateAccountViewController.swift */; }; 19 | 932A9BE11D92DC6B00A9687B /* Login.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 932A9BDA1D92DC6B00A9687B /* Login.storyboard */; }; 20 | 932A9BE21D92DC6B00A9687B /* LoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 932A9BDB1D92DC6B00A9687B /* LoginViewController.swift */; }; 21 | 932A9BE31D92DC6B00A9687B /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 932A9BDC1D92DC6B00A9687B /* MainViewController.swift */; }; 22 | 932A9BE91D92DCF300A9687B /* CognitoUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 932A9BE81D92DCF300A9687B /* CognitoUser.swift */; }; 23 | 939B64A21D92F16700353756 /* R.generated.swift in Sources */ = {isa = PBXBuildFile; fileRef = 939B64A11D92F16700353756 /* R.generated.swift */; }; 24 | 939B64BF1D94301700353756 /* Facebook.swift in Sources */ = {isa = PBXBuildFile; fileRef = 939B64BE1D94301700353756 /* Facebook.swift */; }; 25 | 939B64C21D94326E00353756 /* LoginViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 939B64C11D94326E00353756 /* LoginViewModel.swift */; }; 26 | 939B64C61D943C8400353756 /* SignupViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 939B64C51D943C8400353756 /* SignupViewModel.swift */; }; 27 | 939B64C91D94415100353756 /* UIViewController_ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = 939B64C81D94415100353756 /* UIViewController_ex.swift */; }; 28 | 939B64CB1D94538C00353756 /* MainViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 939B64CA1D94538C00353756 /* MainViewModel.swift */; }; 29 | 939B64CE1D94658F00353756 /* FBSessionProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 939B64CD1D94658F00353756 /* FBSessionProvider.swift */; }; 30 | 939B64D01D94659C00353756 /* UserPoolSessionProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 939B64CF1D94659C00353756 /* UserPoolSessionProvider.swift */; }; 31 | 939B64D21D9465ED00353756 /* CustomIdentityProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 939B64D11D9465ED00353756 /* CustomIdentityProvider.swift */; }; 32 | 939B64D41D94670900353756 /* AWSEnvironment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 939B64D31D94670900353756 /* AWSEnvironment.swift */; }; 33 | 939B64D61D946D5B00353756 /* AmazonSessionProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 939B64D51D946D5B00353756 /* AmazonSessionProvider.swift */; }; 34 | 939B64D81D946FEA00353756 /* AmazonClientManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 939B64D71D946FEA00353756 /* AmazonClientManager.swift */; }; 35 | 939B64DC1D953D4800353756 /* SignupUserPoolSessionProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 939B64DB1D953D4800353756 /* SignupUserPoolSessionProvider.swift */; }; 36 | 939B64DE1D953E8A00353756 /* LoginUserPoolSessionProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 939B64DD1D953E8A00353756 /* LoginUserPoolSessionProvider.swift */; }; 37 | /* End PBXBuildFile section */ 38 | 39 | /* Begin PBXFileReference section */ 40 | 12E72487A0875A8311B192A9 /* Pods-AWSBased_SwiftAppDemo.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AWSBased_SwiftAppDemo.release.xcconfig"; path = "Pods/Target Support Files/Pods-AWSBased_SwiftAppDemo/Pods-AWSBased_SwiftAppDemo.release.xcconfig"; sourceTree = ""; }; 41 | 27A4355CF7DBBF1D3B63EE8E /* Pods-AWSBased_SwiftAppDemo.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AWSBased_SwiftAppDemo.debug.xcconfig"; path = "Pods/Target Support Files/Pods-AWSBased_SwiftAppDemo/Pods-AWSBased_SwiftAppDemo.debug.xcconfig"; sourceTree = ""; }; 42 | 932A9BBF1D92DC0B00A9687B /* AWSBased_SwiftAppDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AWSBased_SwiftAppDemo.app; sourceTree = BUILT_PRODUCTS_DIR; }; 43 | 932A9BC21D92DC0B00A9687B /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 44 | 932A9BC71D92DC0B00A9687B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 45 | 932A9BC91D92DC0B00A9687B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 46 | 932A9BCC1D92DC0B00A9687B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 47 | 932A9BCE1D92DC0B00A9687B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 48 | 932A9BD51D92DC6B00A9687B /* LoginFlowCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginFlowCoordinator.swift; sourceTree = ""; }; 49 | 932A9BD61D92DC6B00A9687B /* MainFlowCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainFlowCoordinator.swift; sourceTree = ""; }; 50 | 932A9BD71D92DC6B00A9687B /* RootFlowCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RootFlowCoordinator.swift; sourceTree = ""; }; 51 | 932A9BD91D92DC6B00A9687B /* CreateAccountViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CreateAccountViewController.swift; sourceTree = ""; }; 52 | 932A9BDA1D92DC6B00A9687B /* Login.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Login.storyboard; sourceTree = ""; }; 53 | 932A9BDB1D92DC6B00A9687B /* LoginViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginViewController.swift; sourceTree = ""; }; 54 | 932A9BDC1D92DC6B00A9687B /* MainViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = ""; }; 55 | 932A9BE81D92DCF300A9687B /* CognitoUser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CognitoUser.swift; sourceTree = ""; }; 56 | 939B64A11D92F16700353756 /* R.generated.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = R.generated.swift; sourceTree = SOURCE_ROOT; }; 57 | 939B64BE1D94301700353756 /* Facebook.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Facebook.swift; sourceTree = ""; }; 58 | 939B64C11D94326E00353756 /* LoginViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginViewModel.swift; sourceTree = ""; }; 59 | 939B64C51D943C8400353756 /* SignupViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignupViewModel.swift; sourceTree = ""; }; 60 | 939B64C81D94415100353756 /* UIViewController_ex.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewController_ex.swift; sourceTree = ""; }; 61 | 939B64CA1D94538C00353756 /* MainViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainViewModel.swift; sourceTree = ""; }; 62 | 939B64CD1D94658F00353756 /* FBSessionProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FBSessionProvider.swift; sourceTree = ""; }; 63 | 939B64CF1D94659C00353756 /* UserPoolSessionProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserPoolSessionProvider.swift; sourceTree = ""; }; 64 | 939B64D11D9465ED00353756 /* CustomIdentityProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomIdentityProvider.swift; sourceTree = ""; }; 65 | 939B64D31D94670900353756 /* AWSEnvironment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AWSEnvironment.swift; sourceTree = ""; }; 66 | 939B64D51D946D5B00353756 /* AmazonSessionProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AmazonSessionProvider.swift; sourceTree = ""; }; 67 | 939B64D71D946FEA00353756 /* AmazonClientManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AmazonClientManager.swift; sourceTree = ""; }; 68 | 939B64DB1D953D4800353756 /* SignupUserPoolSessionProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SignupUserPoolSessionProvider.swift; sourceTree = ""; }; 69 | 939B64DD1D953E8A00353756 /* LoginUserPoolSessionProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginUserPoolSessionProvider.swift; sourceTree = ""; }; 70 | DA43FC843ECF3D7179DCF5B3 /* Pods_AWSBased_SwiftAppDemo.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AWSBased_SwiftAppDemo.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 71 | /* End PBXFileReference section */ 72 | 73 | /* Begin PBXFrameworksBuildPhase section */ 74 | 932A9BBC1D92DC0B00A9687B /* Frameworks */ = { 75 | isa = PBXFrameworksBuildPhase; 76 | buildActionMask = 2147483647; 77 | files = ( 78 | 80C02349F622402AB559CCFB /* Pods_AWSBased_SwiftAppDemo.framework in Frameworks */, 79 | ); 80 | runOnlyForDeploymentPostprocessing = 0; 81 | }; 82 | /* End PBXFrameworksBuildPhase section */ 83 | 84 | /* Begin PBXGroup section */ 85 | 932A9BB61D92DC0B00A9687B = { 86 | isa = PBXGroup; 87 | children = ( 88 | 939B64CC1D94657E00353756 /* SessionProviders */, 89 | 932A9BEF1D92E29900A9687B /* Utils */, 90 | 932A9BE71D92DCE400A9687B /* DataModels */, 91 | 932A9BD41D92DC6B00A9687B /* FlowCoordinators */, 92 | 939B64C01D94326100353756 /* ViewModels */, 93 | 932A9BD81D92DC6B00A9687B /* Views */, 94 | 932A9BC11D92DC0B00A9687B /* AWSBased_SwiftAppDemo */, 95 | 932A9BC01D92DC0B00A9687B /* Products */, 96 | CFA7DD5D10AE19D042DA494C /* Pods */, 97 | D498BCA2C8D7F223B62909CD /* Frameworks */, 98 | ); 99 | sourceTree = ""; 100 | }; 101 | 932A9BC01D92DC0B00A9687B /* Products */ = { 102 | isa = PBXGroup; 103 | children = ( 104 | 932A9BBF1D92DC0B00A9687B /* AWSBased_SwiftAppDemo.app */, 105 | ); 106 | name = Products; 107 | sourceTree = ""; 108 | }; 109 | 932A9BC11D92DC0B00A9687B /* AWSBased_SwiftAppDemo */ = { 110 | isa = PBXGroup; 111 | children = ( 112 | 939B64A11D92F16700353756 /* R.generated.swift */, 113 | 932A9BC21D92DC0B00A9687B /* AppDelegate.swift */, 114 | 932A9BC91D92DC0B00A9687B /* Assets.xcassets */, 115 | 932A9BCE1D92DC0B00A9687B /* Info.plist */, 116 | ); 117 | path = AWSBased_SwiftAppDemo; 118 | sourceTree = ""; 119 | }; 120 | 932A9BD41D92DC6B00A9687B /* FlowCoordinators */ = { 121 | isa = PBXGroup; 122 | children = ( 123 | 932A9BD51D92DC6B00A9687B /* LoginFlowCoordinator.swift */, 124 | 932A9BD61D92DC6B00A9687B /* MainFlowCoordinator.swift */, 125 | 932A9BD71D92DC6B00A9687B /* RootFlowCoordinator.swift */, 126 | ); 127 | name = FlowCoordinators; 128 | path = AWSBased_SwiftAppDemo/FlowCoordinators; 129 | sourceTree = ""; 130 | }; 131 | 932A9BD81D92DC6B00A9687B /* Views */ = { 132 | isa = PBXGroup; 133 | children = ( 134 | 939B64C71D94410B00353756 /* Common */, 135 | 932A9BE61D92DC8900A9687B /* Login */, 136 | 932A9BE51D92DC8500A9687B /* Main */, 137 | 932A9BE41D92DC7000A9687B /* Storyboards */, 138 | ); 139 | name = Views; 140 | path = AWSBased_SwiftAppDemo/Views; 141 | sourceTree = ""; 142 | }; 143 | 932A9BE41D92DC7000A9687B /* Storyboards */ = { 144 | isa = PBXGroup; 145 | children = ( 146 | 932A9BDA1D92DC6B00A9687B /* Login.storyboard */, 147 | 932A9BCB1D92DC0B00A9687B /* LaunchScreen.storyboard */, 148 | 932A9BC61D92DC0B00A9687B /* Main.storyboard */, 149 | ); 150 | name = Storyboards; 151 | sourceTree = ""; 152 | }; 153 | 932A9BE51D92DC8500A9687B /* Main */ = { 154 | isa = PBXGroup; 155 | children = ( 156 | 932A9BDC1D92DC6B00A9687B /* MainViewController.swift */, 157 | ); 158 | name = Main; 159 | sourceTree = ""; 160 | }; 161 | 932A9BE61D92DC8900A9687B /* Login */ = { 162 | isa = PBXGroup; 163 | children = ( 164 | 932A9BD91D92DC6B00A9687B /* CreateAccountViewController.swift */, 165 | 932A9BDB1D92DC6B00A9687B /* LoginViewController.swift */, 166 | ); 167 | name = Login; 168 | sourceTree = ""; 169 | }; 170 | 932A9BE71D92DCE400A9687B /* DataModels */ = { 171 | isa = PBXGroup; 172 | children = ( 173 | 932A9BE81D92DCF300A9687B /* CognitoUser.swift */, 174 | ); 175 | name = DataModels; 176 | path = AWSBased_SwiftAppDemo/DataModels; 177 | sourceTree = ""; 178 | }; 179 | 932A9BEF1D92E29900A9687B /* Utils */ = { 180 | isa = PBXGroup; 181 | children = ( 182 | 939B64BE1D94301700353756 /* Facebook.swift */, 183 | ); 184 | name = Utils; 185 | path = AWSBased_SwiftAppDemo/Utils; 186 | sourceTree = ""; 187 | }; 188 | 939B64C01D94326100353756 /* ViewModels */ = { 189 | isa = PBXGroup; 190 | children = ( 191 | 939B64C11D94326E00353756 /* LoginViewModel.swift */, 192 | 939B64C51D943C8400353756 /* SignupViewModel.swift */, 193 | 939B64CA1D94538C00353756 /* MainViewModel.swift */, 194 | ); 195 | name = ViewModels; 196 | path = AWSBased_SwiftAppDemo/ViewModels; 197 | sourceTree = ""; 198 | }; 199 | 939B64C71D94410B00353756 /* Common */ = { 200 | isa = PBXGroup; 201 | children = ( 202 | 939B64C81D94415100353756 /* UIViewController_ex.swift */, 203 | ); 204 | name = Common; 205 | sourceTree = ""; 206 | }; 207 | 939B64CC1D94657E00353756 /* SessionProviders */ = { 208 | isa = PBXGroup; 209 | children = ( 210 | 939B64D51D946D5B00353756 /* AmazonSessionProvider.swift */, 211 | 939B64D31D94670900353756 /* AWSEnvironment.swift */, 212 | 939B64DA1D95368A00353756 /* User Pool */, 213 | 939B64D91D95368100353756 /* Facebook */, 214 | 939B64D71D946FEA00353756 /* AmazonClientManager.swift */, 215 | 939B64D11D9465ED00353756 /* CustomIdentityProvider.swift */, 216 | ); 217 | name = SessionProviders; 218 | path = AWSBased_SwiftAppDemo/SessionProviders; 219 | sourceTree = ""; 220 | }; 221 | 939B64D91D95368100353756 /* Facebook */ = { 222 | isa = PBXGroup; 223 | children = ( 224 | 939B64CD1D94658F00353756 /* FBSessionProvider.swift */, 225 | ); 226 | name = Facebook; 227 | sourceTree = ""; 228 | }; 229 | 939B64DA1D95368A00353756 /* User Pool */ = { 230 | isa = PBXGroup; 231 | children = ( 232 | 939B64CF1D94659C00353756 /* UserPoolSessionProvider.swift */, 233 | 939B64DB1D953D4800353756 /* SignupUserPoolSessionProvider.swift */, 234 | 939B64DD1D953E8A00353756 /* LoginUserPoolSessionProvider.swift */, 235 | ); 236 | name = "User Pool"; 237 | sourceTree = ""; 238 | }; 239 | CFA7DD5D10AE19D042DA494C /* Pods */ = { 240 | isa = PBXGroup; 241 | children = ( 242 | 27A4355CF7DBBF1D3B63EE8E /* Pods-AWSBased_SwiftAppDemo.debug.xcconfig */, 243 | 12E72487A0875A8311B192A9 /* Pods-AWSBased_SwiftAppDemo.release.xcconfig */, 244 | ); 245 | name = Pods; 246 | sourceTree = ""; 247 | }; 248 | D498BCA2C8D7F223B62909CD /* Frameworks */ = { 249 | isa = PBXGroup; 250 | children = ( 251 | DA43FC843ECF3D7179DCF5B3 /* Pods_AWSBased_SwiftAppDemo.framework */, 252 | ); 253 | name = Frameworks; 254 | sourceTree = ""; 255 | }; 256 | /* End PBXGroup section */ 257 | 258 | /* Begin PBXNativeTarget section */ 259 | 932A9BBE1D92DC0B00A9687B /* AWSBased_SwiftAppDemo */ = { 260 | isa = PBXNativeTarget; 261 | buildConfigurationList = 932A9BD11D92DC0B00A9687B /* Build configuration list for PBXNativeTarget "AWSBased_SwiftAppDemo" */; 262 | buildPhases = ( 263 | D89D0ECE91AF67170F6A39BF /* [CP] Check Pods Manifest.lock */, 264 | 932A9BEA1D92E14800A9687B /* ShellScript */, 265 | 932A9BBB1D92DC0B00A9687B /* Sources */, 266 | 932A9BBC1D92DC0B00A9687B /* Frameworks */, 267 | 932A9BBD1D92DC0B00A9687B /* Resources */, 268 | 5A2C64BF7F87028F0CEBCC44 /* [CP] Embed Pods Frameworks */, 269 | A47BC82274A88C3EE612CBD1 /* [CP] Copy Pods Resources */, 270 | ); 271 | buildRules = ( 272 | ); 273 | dependencies = ( 274 | ); 275 | name = AWSBased_SwiftAppDemo; 276 | productName = AWSBased_SwiftAppDemo; 277 | productReference = 932A9BBF1D92DC0B00A9687B /* AWSBased_SwiftAppDemo.app */; 278 | productType = "com.apple.product-type.application"; 279 | }; 280 | /* End PBXNativeTarget section */ 281 | 282 | /* Begin PBXProject section */ 283 | 932A9BB71D92DC0B00A9687B /* Project object */ = { 284 | isa = PBXProject; 285 | attributes = { 286 | LastSwiftUpdateCheck = 0800; 287 | LastUpgradeCheck = 0800; 288 | ORGANIZATIONNAME = "HillTrix sp. z o.o."; 289 | TargetAttributes = { 290 | 932A9BBE1D92DC0B00A9687B = { 291 | CreatedOnToolsVersion = 8.0; 292 | ProvisioningStyle = Automatic; 293 | }; 294 | }; 295 | }; 296 | buildConfigurationList = 932A9BBA1D92DC0B00A9687B /* Build configuration list for PBXProject "AWSBased_SwiftAppDemo" */; 297 | compatibilityVersion = "Xcode 3.2"; 298 | developmentRegion = English; 299 | hasScannedForEncodings = 0; 300 | knownRegions = ( 301 | en, 302 | Base, 303 | ); 304 | mainGroup = 932A9BB61D92DC0B00A9687B; 305 | productRefGroup = 932A9BC01D92DC0B00A9687B /* Products */; 306 | projectDirPath = ""; 307 | projectRoot = ""; 308 | targets = ( 309 | 932A9BBE1D92DC0B00A9687B /* AWSBased_SwiftAppDemo */, 310 | ); 311 | }; 312 | /* End PBXProject section */ 313 | 314 | /* Begin PBXResourcesBuildPhase section */ 315 | 932A9BBD1D92DC0B00A9687B /* Resources */ = { 316 | isa = PBXResourcesBuildPhase; 317 | buildActionMask = 2147483647; 318 | files = ( 319 | 932A9BE11D92DC6B00A9687B /* Login.storyboard in Resources */, 320 | 932A9BCD1D92DC0B00A9687B /* LaunchScreen.storyboard in Resources */, 321 | 932A9BCA1D92DC0B00A9687B /* Assets.xcassets in Resources */, 322 | 932A9BC81D92DC0B00A9687B /* Main.storyboard in Resources */, 323 | ); 324 | runOnlyForDeploymentPostprocessing = 0; 325 | }; 326 | /* End PBXResourcesBuildPhase section */ 327 | 328 | /* Begin PBXShellScriptBuildPhase section */ 329 | 5A2C64BF7F87028F0CEBCC44 /* [CP] Embed Pods Frameworks */ = { 330 | isa = PBXShellScriptBuildPhase; 331 | buildActionMask = 2147483647; 332 | files = ( 333 | ); 334 | inputPaths = ( 335 | ); 336 | name = "[CP] Embed Pods Frameworks"; 337 | outputPaths = ( 338 | ); 339 | runOnlyForDeploymentPostprocessing = 0; 340 | shellPath = /bin/sh; 341 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-AWSBased_SwiftAppDemo/Pods-AWSBased_SwiftAppDemo-frameworks.sh\"\n"; 342 | showEnvVarsInLog = 0; 343 | }; 344 | 932A9BEA1D92E14800A9687B /* ShellScript */ = { 345 | isa = PBXShellScriptBuildPhase; 346 | buildActionMask = 2147483647; 347 | files = ( 348 | ); 349 | inputPaths = ( 350 | ); 351 | outputPaths = ( 352 | ); 353 | runOnlyForDeploymentPostprocessing = 0; 354 | shellPath = /bin/sh; 355 | shellScript = "\"$PODS_ROOT/R.swift/rswift\" \"$SRCROOT\""; 356 | }; 357 | A47BC82274A88C3EE612CBD1 /* [CP] Copy Pods Resources */ = { 358 | isa = PBXShellScriptBuildPhase; 359 | buildActionMask = 2147483647; 360 | files = ( 361 | ); 362 | inputPaths = ( 363 | ); 364 | name = "[CP] Copy Pods Resources"; 365 | outputPaths = ( 366 | ); 367 | runOnlyForDeploymentPostprocessing = 0; 368 | shellPath = /bin/sh; 369 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-AWSBased_SwiftAppDemo/Pods-AWSBased_SwiftAppDemo-resources.sh\"\n"; 370 | showEnvVarsInLog = 0; 371 | }; 372 | D89D0ECE91AF67170F6A39BF /* [CP] Check Pods Manifest.lock */ = { 373 | isa = PBXShellScriptBuildPhase; 374 | buildActionMask = 2147483647; 375 | files = ( 376 | ); 377 | inputPaths = ( 378 | ); 379 | name = "[CP] Check Pods Manifest.lock"; 380 | outputPaths = ( 381 | ); 382 | runOnlyForDeploymentPostprocessing = 0; 383 | shellPath = /bin/sh; 384 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; 385 | showEnvVarsInLog = 0; 386 | }; 387 | /* End PBXShellScriptBuildPhase section */ 388 | 389 | /* Begin PBXSourcesBuildPhase section */ 390 | 932A9BBB1D92DC0B00A9687B /* Sources */ = { 391 | isa = PBXSourcesBuildPhase; 392 | buildActionMask = 2147483647; 393 | files = ( 394 | 939B64CE1D94658F00353756 /* FBSessionProvider.swift in Sources */, 395 | 932A9BE21D92DC6B00A9687B /* LoginViewController.swift in Sources */, 396 | 932A9BE01D92DC6B00A9687B /* CreateAccountViewController.swift in Sources */, 397 | 939B64D41D94670900353756 /* AWSEnvironment.swift in Sources */, 398 | 932A9BDF1D92DC6B00A9687B /* RootFlowCoordinator.swift in Sources */, 399 | 932A9BDD1D92DC6B00A9687B /* LoginFlowCoordinator.swift in Sources */, 400 | 932A9BE31D92DC6B00A9687B /* MainViewController.swift in Sources */, 401 | 939B64D81D946FEA00353756 /* AmazonClientManager.swift in Sources */, 402 | 939B64DC1D953D4800353756 /* SignupUserPoolSessionProvider.swift in Sources */, 403 | 939B64D01D94659C00353756 /* UserPoolSessionProvider.swift in Sources */, 404 | 939B64D21D9465ED00353756 /* CustomIdentityProvider.swift in Sources */, 405 | 932A9BC31D92DC0B00A9687B /* AppDelegate.swift in Sources */, 406 | 932A9BE91D92DCF300A9687B /* CognitoUser.swift in Sources */, 407 | 939B64A21D92F16700353756 /* R.generated.swift in Sources */, 408 | 939B64C21D94326E00353756 /* LoginViewModel.swift in Sources */, 409 | 939B64C61D943C8400353756 /* SignupViewModel.swift in Sources */, 410 | 939B64D61D946D5B00353756 /* AmazonSessionProvider.swift in Sources */, 411 | 939B64BF1D94301700353756 /* Facebook.swift in Sources */, 412 | 939B64DE1D953E8A00353756 /* LoginUserPoolSessionProvider.swift in Sources */, 413 | 939B64C91D94415100353756 /* UIViewController_ex.swift in Sources */, 414 | 932A9BDE1D92DC6B00A9687B /* MainFlowCoordinator.swift in Sources */, 415 | 939B64CB1D94538C00353756 /* MainViewModel.swift in Sources */, 416 | ); 417 | runOnlyForDeploymentPostprocessing = 0; 418 | }; 419 | /* End PBXSourcesBuildPhase section */ 420 | 421 | /* Begin PBXVariantGroup section */ 422 | 932A9BC61D92DC0B00A9687B /* Main.storyboard */ = { 423 | isa = PBXVariantGroup; 424 | children = ( 425 | 932A9BC71D92DC0B00A9687B /* Base */, 426 | ); 427 | name = Main.storyboard; 428 | path = ..; 429 | sourceTree = ""; 430 | }; 431 | 932A9BCB1D92DC0B00A9687B /* LaunchScreen.storyboard */ = { 432 | isa = PBXVariantGroup; 433 | children = ( 434 | 932A9BCC1D92DC0B00A9687B /* Base */, 435 | ); 436 | name = LaunchScreen.storyboard; 437 | path = ..; 438 | sourceTree = ""; 439 | }; 440 | /* End PBXVariantGroup section */ 441 | 442 | /* Begin XCBuildConfiguration section */ 443 | 932A9BCF1D92DC0B00A9687B /* Debug */ = { 444 | isa = XCBuildConfiguration; 445 | buildSettings = { 446 | ALWAYS_SEARCH_USER_PATHS = NO; 447 | CLANG_ANALYZER_NONNULL = YES; 448 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 449 | CLANG_CXX_LIBRARY = "libc++"; 450 | CLANG_ENABLE_MODULES = YES; 451 | CLANG_ENABLE_OBJC_ARC = YES; 452 | CLANG_WARN_BOOL_CONVERSION = YES; 453 | CLANG_WARN_CONSTANT_CONVERSION = YES; 454 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 455 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 456 | CLANG_WARN_EMPTY_BODY = YES; 457 | CLANG_WARN_ENUM_CONVERSION = YES; 458 | CLANG_WARN_INFINITE_RECURSION = YES; 459 | CLANG_WARN_INT_CONVERSION = YES; 460 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 461 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 462 | CLANG_WARN_UNREACHABLE_CODE = YES; 463 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 464 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 465 | COPY_PHASE_STRIP = NO; 466 | DEBUG_INFORMATION_FORMAT = dwarf; 467 | ENABLE_STRICT_OBJC_MSGSEND = YES; 468 | ENABLE_TESTABILITY = YES; 469 | GCC_C_LANGUAGE_STANDARD = gnu99; 470 | GCC_DYNAMIC_NO_PIC = NO; 471 | GCC_NO_COMMON_BLOCKS = YES; 472 | GCC_OPTIMIZATION_LEVEL = 0; 473 | GCC_PREPROCESSOR_DEFINITIONS = ( 474 | "DEBUG=1", 475 | "$(inherited)", 476 | ); 477 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 478 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 479 | GCC_WARN_UNDECLARED_SELECTOR = YES; 480 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 481 | GCC_WARN_UNUSED_FUNCTION = YES; 482 | GCC_WARN_UNUSED_VARIABLE = YES; 483 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 484 | MTL_ENABLE_DEBUG_INFO = YES; 485 | ONLY_ACTIVE_ARCH = YES; 486 | SDKROOT = iphoneos; 487 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 488 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 489 | }; 490 | name = Debug; 491 | }; 492 | 932A9BD01D92DC0B00A9687B /* Release */ = { 493 | isa = XCBuildConfiguration; 494 | buildSettings = { 495 | ALWAYS_SEARCH_USER_PATHS = NO; 496 | CLANG_ANALYZER_NONNULL = YES; 497 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 498 | CLANG_CXX_LIBRARY = "libc++"; 499 | CLANG_ENABLE_MODULES = YES; 500 | CLANG_ENABLE_OBJC_ARC = YES; 501 | CLANG_WARN_BOOL_CONVERSION = YES; 502 | CLANG_WARN_CONSTANT_CONVERSION = YES; 503 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 504 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 505 | CLANG_WARN_EMPTY_BODY = YES; 506 | CLANG_WARN_ENUM_CONVERSION = YES; 507 | CLANG_WARN_INFINITE_RECURSION = YES; 508 | CLANG_WARN_INT_CONVERSION = YES; 509 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 510 | CLANG_WARN_SUSPICIOUS_MOVES = YES; 511 | CLANG_WARN_UNREACHABLE_CODE = YES; 512 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 513 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 514 | COPY_PHASE_STRIP = NO; 515 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 516 | ENABLE_NS_ASSERTIONS = NO; 517 | ENABLE_STRICT_OBJC_MSGSEND = YES; 518 | GCC_C_LANGUAGE_STANDARD = gnu99; 519 | GCC_NO_COMMON_BLOCKS = YES; 520 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 521 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 522 | GCC_WARN_UNDECLARED_SELECTOR = YES; 523 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 524 | GCC_WARN_UNUSED_FUNCTION = YES; 525 | GCC_WARN_UNUSED_VARIABLE = YES; 526 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 527 | MTL_ENABLE_DEBUG_INFO = NO; 528 | SDKROOT = iphoneos; 529 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 530 | VALIDATE_PRODUCT = YES; 531 | }; 532 | name = Release; 533 | }; 534 | 932A9BD21D92DC0B00A9687B /* Debug */ = { 535 | isa = XCBuildConfiguration; 536 | baseConfigurationReference = 27A4355CF7DBBF1D3B63EE8E /* Pods-AWSBased_SwiftAppDemo.debug.xcconfig */; 537 | buildSettings = { 538 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 539 | DEVELOPMENT_TEAM = ""; 540 | INFOPLIST_FILE = AWSBased_SwiftAppDemo/Info.plist; 541 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 542 | PRODUCT_BUNDLE_IDENTIFIER = "com.hilltrix.AWSBased-SwiftAppDemo"; 543 | PRODUCT_NAME = "$(TARGET_NAME)"; 544 | SWIFT_VERSION = 3.0; 545 | }; 546 | name = Debug; 547 | }; 548 | 932A9BD31D92DC0B00A9687B /* Release */ = { 549 | isa = XCBuildConfiguration; 550 | baseConfigurationReference = 12E72487A0875A8311B192A9 /* Pods-AWSBased_SwiftAppDemo.release.xcconfig */; 551 | buildSettings = { 552 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 553 | DEVELOPMENT_TEAM = ""; 554 | INFOPLIST_FILE = AWSBased_SwiftAppDemo/Info.plist; 555 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 556 | PRODUCT_BUNDLE_IDENTIFIER = "com.hilltrix.AWSBased-SwiftAppDemo"; 557 | PRODUCT_NAME = "$(TARGET_NAME)"; 558 | SWIFT_VERSION = 3.0; 559 | }; 560 | name = Release; 561 | }; 562 | /* End XCBuildConfiguration section */ 563 | 564 | /* Begin XCConfigurationList section */ 565 | 932A9BBA1D92DC0B00A9687B /* Build configuration list for PBXProject "AWSBased_SwiftAppDemo" */ = { 566 | isa = XCConfigurationList; 567 | buildConfigurations = ( 568 | 932A9BCF1D92DC0B00A9687B /* Debug */, 569 | 932A9BD01D92DC0B00A9687B /* Release */, 570 | ); 571 | defaultConfigurationIsVisible = 0; 572 | defaultConfigurationName = Release; 573 | }; 574 | 932A9BD11D92DC0B00A9687B /* Build configuration list for PBXNativeTarget "AWSBased_SwiftAppDemo" */ = { 575 | isa = XCConfigurationList; 576 | buildConfigurations = ( 577 | 932A9BD21D92DC0B00A9687B /* Debug */, 578 | 932A9BD31D92DC0B00A9687B /* Release */, 579 | ); 580 | defaultConfigurationIsVisible = 0; 581 | defaultConfigurationName = Release; 582 | }; 583 | /* End XCConfigurationList section */ 584 | }; 585 | rootObject = 932A9BB71D92DC0B00A9687B /* Project object */; 586 | } 587 | -------------------------------------------------------------------------------- /AWSBased_SwiftAppDemo/Views/Login.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 34 | 44 | 45 | 46 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 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 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 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 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 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 | --------------------------------------------------------------------------------