├── .gitignore ├── Example ├── Podfile ├── Podfile.lock ├── Pods │ ├── Headers │ │ ├── Private │ │ │ └── SmileTouchID │ │ │ │ ├── SmileAuthenticator.h │ │ │ │ ├── SmileKeychainWrapper.h │ │ │ │ ├── SmilePasswordContainerView.h │ │ │ │ ├── SmilePasswordView.h │ │ │ │ └── SmileSettingVC.h │ │ └── Public │ │ │ └── SmileTouchID │ │ │ ├── SmileAuthenticator.h │ │ │ ├── SmileKeychainWrapper.h │ │ │ ├── SmilePasswordContainerView.h │ │ │ ├── SmilePasswordView.h │ │ │ └── SmileSettingVC.h │ ├── Local Podspecs │ │ └── SmileTouchID.podspec.json │ ├── Manifest.lock │ ├── Pods.xcodeproj │ │ ├── project.pbxproj │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Pods-Smile TouchID-SmileTouchID.xcscheme │ ├── SmileTouchID │ │ ├── LICENSE │ │ ├── README.md │ │ └── SmileAuth │ │ │ ├── Assets │ │ │ ├── SmileSettingVC.storyboard │ │ │ ├── smile_Touch_ID@2x.png │ │ │ └── smile_Touch_ID@3x.png │ │ │ └── Classes │ │ │ ├── SmileAuthenticator.h │ │ │ ├── SmileAuthenticator.m │ │ │ ├── SmileKeychainWrapper.h │ │ │ ├── SmileKeychainWrapper.m │ │ │ ├── SmilePasswordContainerView.h │ │ │ ├── SmilePasswordContainerView.m │ │ │ ├── SmilePasswordView.h │ │ │ ├── SmilePasswordView.m │ │ │ ├── SmileSettingVC.h │ │ │ └── SmileSettingVC.m │ └── Target Support Files │ │ ├── Pods-Smile TouchID-SmileTouchID │ │ ├── Pods-Smile TouchID-SmileTouchID-Private.xcconfig │ │ ├── Pods-Smile TouchID-SmileTouchID-dummy.m │ │ ├── Pods-Smile TouchID-SmileTouchID-prefix.pch │ │ └── Pods-Smile TouchID-SmileTouchID.xcconfig │ │ └── Pods-Smile TouchID │ │ ├── Pods-Smile TouchID-acknowledgements.markdown │ │ ├── Pods-Smile TouchID-acknowledgements.plist │ │ ├── Pods-Smile TouchID-dummy.m │ │ ├── Pods-Smile TouchID-environment.h │ │ ├── Pods-Smile TouchID-resources.sh │ │ ├── Pods-Smile TouchID.debug.xcconfig │ │ └── Pods-Smile TouchID.release.xcconfig ├── TouchID.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata ├── TouchID.xcworkspace │ └── contents.xcworkspacedata ├── TouchID │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Base.lproj │ │ ├── Localizable.strings │ │ └── Main.storyboard │ ├── Images.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── icon_60@2x.png │ │ │ ├── icon_60@3x.png │ │ │ ├── icon_76.png │ │ │ └── icon_76@2x.png │ │ ├── LaunchImage.launchimage │ │ │ ├── Contents.json │ │ │ ├── iPhone 4.png │ │ │ ├── iPhone 5.png │ │ │ ├── iPhone 6 Plus.png │ │ │ └── iPhone 6.png │ │ ├── backgroundImage.imageset │ │ │ ├── Contents.json │ │ │ └── backgroundImage@2x.png │ │ ├── my_Logo.imageset │ │ │ ├── Contents.json │ │ │ ├── my_Logo@2x.png │ │ │ └── my_Logo@3x.png │ │ ├── my_Touch_ID.imageset │ │ │ ├── Contents.json │ │ │ ├── my_Touch_ID@2x.png │ │ │ └── my_Touch_ID@3x.png │ │ └── nightMode_BG.imageset │ │ │ ├── Contents.json │ │ │ └── nightMode_BG@2x.png │ ├── Info.plist │ ├── SmileMainVC.h │ ├── SmileMainVC.m │ └── main.m ├── TouchIDTests │ ├── Info.plist │ └── TouchIDTests.m └── demo_gif │ ├── customize1.png │ ├── demo1.gif │ ├── demo2.gif │ ├── demo5.png │ ├── demo6.png │ ├── night_mode1.png │ ├── passlength.png │ ├── promo_banner_s.png │ ├── rotate.gif │ ├── step2.png │ ├── step3.png │ └── step4.png ├── LICENSE ├── README.md ├── SmileAuth ├── Assets │ ├── SmileSettingVC.storyboard │ ├── smile_Touch_ID@2x.png │ └── smile_Touch_ID@3x.png └── Classes │ ├── SmileAuthenticator.h │ ├── SmileAuthenticator.m │ ├── SmileKeychainWrapper.h │ ├── SmileKeychainWrapper.m │ ├── SmilePasswordContainerView.h │ ├── SmilePasswordContainerView.m │ ├── SmilePasswordView.h │ ├── SmilePasswordView.m │ ├── SmileSettingVC.h │ └── SmileSettingVC.m └── SmileTouchID.podspec /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | build/ 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata 15 | *.xccheckout 16 | profile 17 | *.moved-aside 18 | DerivedData 19 | *.hmap 20 | *.ipa 21 | 22 | # Bundler 23 | .bundle 24 | 25 | # We recommend against adding the Pods directory to your .gitignore. However 26 | # you should judge for yourself, the pros and cons are mentioned at: 27 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 28 | # 29 | # Note: if you ignore the Pods directory, make sure to uncomment 30 | # `pod install` in .travis.yml 31 | # 32 | # Pods/ 33 | -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '7.0' 2 | 3 | target 'Smile TouchID' do 4 | 5 | pod 'SmileTouchID', :path => "../" 6 | 7 | #pod 'SmileTouchID' 8 | 9 | end 10 | 11 | target 'TouchIDTests' do 12 | 13 | end 14 | 15 | -------------------------------------------------------------------------------- /Example/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - SmileTouchID (0.1.0) 3 | 4 | DEPENDENCIES: 5 | - SmileTouchID (from `../`) 6 | 7 | EXTERNAL SOURCES: 8 | SmileTouchID: 9 | :path: "../" 10 | 11 | SPEC CHECKSUMS: 12 | SmileTouchID: 6e6afe5e2cf83ea4a897b82142caadd957d79d72 13 | 14 | COCOAPODS: 0.37.2 15 | -------------------------------------------------------------------------------- /Example/Pods/Headers/Private/SmileTouchID/SmileAuthenticator.h: -------------------------------------------------------------------------------- 1 | ../../../../../SmileAuth/Classes/SmileAuthenticator.h -------------------------------------------------------------------------------- /Example/Pods/Headers/Private/SmileTouchID/SmileKeychainWrapper.h: -------------------------------------------------------------------------------- 1 | ../../../../../SmileAuth/Classes/SmileKeychainWrapper.h -------------------------------------------------------------------------------- /Example/Pods/Headers/Private/SmileTouchID/SmilePasswordContainerView.h: -------------------------------------------------------------------------------- 1 | ../../../../../SmileAuth/Classes/SmilePasswordContainerView.h -------------------------------------------------------------------------------- /Example/Pods/Headers/Private/SmileTouchID/SmilePasswordView.h: -------------------------------------------------------------------------------- 1 | ../../../../../SmileAuth/Classes/SmilePasswordView.h -------------------------------------------------------------------------------- /Example/Pods/Headers/Private/SmileTouchID/SmileSettingVC.h: -------------------------------------------------------------------------------- 1 | ../../../../../SmileAuth/Classes/SmileSettingVC.h -------------------------------------------------------------------------------- /Example/Pods/Headers/Public/SmileTouchID/SmileAuthenticator.h: -------------------------------------------------------------------------------- 1 | ../../../../../SmileAuth/Classes/SmileAuthenticator.h -------------------------------------------------------------------------------- /Example/Pods/Headers/Public/SmileTouchID/SmileKeychainWrapper.h: -------------------------------------------------------------------------------- 1 | ../../../../../SmileAuth/Classes/SmileKeychainWrapper.h -------------------------------------------------------------------------------- /Example/Pods/Headers/Public/SmileTouchID/SmilePasswordContainerView.h: -------------------------------------------------------------------------------- 1 | ../../../../../SmileAuth/Classes/SmilePasswordContainerView.h -------------------------------------------------------------------------------- /Example/Pods/Headers/Public/SmileTouchID/SmilePasswordView.h: -------------------------------------------------------------------------------- 1 | ../../../../../SmileAuth/Classes/SmilePasswordView.h -------------------------------------------------------------------------------- /Example/Pods/Headers/Public/SmileTouchID/SmileSettingVC.h: -------------------------------------------------------------------------------- 1 | ../../../../../SmileAuth/Classes/SmileSettingVC.h -------------------------------------------------------------------------------- /Example/Pods/Local Podspecs/SmileTouchID.podspec.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "SmileTouchID", 3 | "version": "0.1.0", 4 | "summary": "A Library for configure Touch ID & passcode conveniently", 5 | "description": " 1. Handle all complicated things about Touch ID & Passcode. You just need to write a few simple code to integrate Touch ID & Passcode to your app.\n 2. Get elegant animation automatically and adaptive UI.\n 3. Can customize the color,touch id icon and background image to fit your app style.\n 4. Can customize the passcode digit to 6 or 10, or any number, automatically handle other things for you.\n 5. Support iOS7 and later.\n", 6 | "homepage": "https://github.com/liu044100/SmileTouchID", 7 | "screenshots": [ 8 | "https://raw.githubusercontent.com/liu044100/SmileTouchID/master/Example/demo_gif/demo1.gif", 9 | "https://raw.githubusercontent.com/liu044100/SmileTouchID/master/Example/demo_gif/demo2.gif" 10 | ], 11 | "license": "MIT", 12 | "authors": { 13 | "Rain": "liu044100@gmail.com" 14 | }, 15 | "social_media_url": "https://dribbble.com/yuchenliu", 16 | "platforms": { 17 | "ios": "7.0" 18 | }, 19 | "requires_arc": true, 20 | "source": { 21 | "git": "https://github.com/liu044100/SmileTouchID.git", 22 | "tag": "0.1.0" 23 | }, 24 | "source_files": "SmileAuth/Classes/*", 25 | "resources": [ 26 | "SmileAuth/Assets/*" 27 | ], 28 | "public_header_files": "SmileAuth/Classes/*.h", 29 | "frameworks": "UIKit", 30 | "weak_frameworks": "LocalAuthentication" 31 | } 32 | -------------------------------------------------------------------------------- /Example/Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - SmileTouchID (0.1.0) 3 | 4 | DEPENDENCIES: 5 | - SmileTouchID (from `../`) 6 | 7 | EXTERNAL SOURCES: 8 | SmileTouchID: 9 | :path: "../" 10 | 11 | SPEC CHECKSUMS: 12 | SmileTouchID: 6e6afe5e2cf83ea4a897b82142caadd957d79d72 13 | 14 | COCOAPODS: 0.37.2 15 | -------------------------------------------------------------------------------- /Example/Pods/Pods.xcodeproj/xcshareddata/xcschemes/Pods-Smile TouchID-SmileTouchID.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 66 | 67 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /Example/Pods/SmileTouchID/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 LIU YUCHEN 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 | -------------------------------------------------------------------------------- /Example/Pods/SmileTouchID/README.md: -------------------------------------------------------------------------------- 1 | # SmileTouchID 2 | 3 | [![GitHub Issues](http://img.shields.io/github/issues/liu044100/SmileTouchID.svg?style=flat)](https://github.com/liu044100/SmileTouchID/issues) 4 | [![Version](https://img.shields.io/cocoapods/v/SmileTouchID.svg?style=flat)](http://cocoadocs.org/docsets/SmileTouchID) 5 | [![License](https://img.shields.io/cocoapods/l/SmileTouchID.svg?style=flat)](http://cocoadocs.org/docsets/SmileTouchID) 6 | [![Platform](https://img.shields.io/cocoapods/p/SmileTouchID.svg?style=flat)](http://cocoadocs.org/docsets/SmileTouchID) 7 | 8 | A library for integrate Touch ID & Passcode to iOS App conveniently. 9 | 10 | ![](https://raw.githubusercontent.com/liu044100/SmileTouchID/master/Example/demo_gif/promo_banner_s.png) 11 | 12 | #What can it do for you? 13 | 14 | 15 | ##### 1. Handle all complicated things about Touch ID & Passcode. You just need to write a few simple code to integrate Touch ID & Passcode to your app. 16 | 17 | 18 | For example, handle the device that not support Touch ID, instead of Touch ID, use Passcode for authentication. 19 | 20 | handle the whole process about securing the app (the user change passcode or turn passcode off), 21 | 22 | 23 | ``` 24 | if ([SmileAuthenticator hasPassword]) { 25 | [SmileAuthenticator sharedInstance].securityType = INPUT_TOUCHID; 26 | [[SmileAuthenticator sharedInstance] presentAuthViewController]; 27 | } 28 | ``` 29 | 30 | 31 | 32 | ##### 2. Get elegant animation and adaptive UI automatically. 33 | 34 | 35 | ![](https://raw.githubusercontent.com/liu044100/SmileTouchID/master/Example/demo_gif/demo1.gif) 36 | ![](https://raw.githubusercontent.com/liu044100/SmileTouchID/master/Example/demo_gif/demo2.gif) 37 | ![](https://raw.githubusercontent.com/liu044100/SmileTouchID/master/Example/demo_gif/rotate.gif) 38 | 39 | 40 | 41 | ##### 3. Can customize the color,Touch ID icon and background image to fit your app style. For example, you can customize like the below image. 42 | 43 | ``` 44 | [SmileAuthenticator sharedInstance].tintColor = [UIColor purpleColor]; 45 | [SmileAuthenticator sharedInstance].touchIDIconName = @"my_Touch_ID"; 46 | [SmileAuthenticator sharedInstance].appLogoName = @"my_Logo"; 47 | [SmileAuthenticator sharedInstance].navibarTranslucent = YES; 48 | [SmileAuthenticator sharedInstance].backgroundImage = [UIImage imageNamed:@"backgroundImage"]; 49 | 50 | ``` 51 | 52 | ![](https://raw.githubusercontent.com/liu044100/SmileTouchID/master/Example/demo_gif/customize1.png) 53 | 54 | You can use the property `nightMode` to change all the UI element to black style, like the below image. 55 | 56 | ``` 57 | [SmileAuthenticator sharedInstance].nightMode = YES; 58 | [SmileAuthenticator sharedInstance].backgroundImage = [UIImage imageNamed:@"nightMode_BG"]; 59 | 60 | ``` 61 | 62 | ![](https://raw.githubusercontent.com/liu044100/SmileTouchID/master/Example/demo_gif/night_mode1.png) 63 | 64 | ##### 4. Can customize the passcode digit to 6 or 10, or any number, automatically handle other things for you. 65 | 66 | ``` 67 | [SmileAuthenticator sharedInstance].passcodeDigit = 6; 68 | ``` 69 | 70 | If you want to try this feature in the demo app, before you try to change `passcodeDigit` to new number, make sure turn off the passcode switch, because the keychain will save your old passcode even you delete the app, so you have to clear the old passcode in keychain, and then change new digit for test. 71 | 72 | ![](https://raw.githubusercontent.com/liu044100/SmileTouchID/master/Example/demo_gif/passlength.png) 73 | 74 | 75 | ##### 5. Support iOS7 and later. 76 | 77 | In iOS7, because Apple had not given the TouchID API to developers, only use Passcode for authentication. 78 | 79 | 80 | #Theoretical Introduction 81 | 82 | The main class is the `SmileAuthenticator`. It has a property `SecurityType` that has four types: `INPUT_ONCE`, `INPUT_TWICE`, `INPUT_THREE`, `INPUT_TOUCHID`. The reason for this name is that show the user input times. 83 | 84 | 85 | **`INPUT_ONCE`:** For the user turn the passcode switch off, user need input their passcode only one time for turn the password off. 86 | 87 | 88 | **`INPUT_TWICE`:** For the user turn the password switch on, user need input their password once and re-enter their password one more time for confirm it to match each other. 89 | 90 | 91 | **`INPUT_THREE`:** For the user change the password, user need input their old passcode one time, then input their new passcode one time and re-enter one time for confirm, a total of three times. 92 | 93 | 94 | **`INPUT_TOUCHID`:** For the user open the app, user can use touch ID or input passcode to unlock. 95 | 96 | 97 | Use `[[SmileAuthenticator sharedInstance] presentAuthViewController]` to present view for authentication. 98 | 99 | 100 | #How to use it for your project? 101 | 102 | **Step 1.** SmileTouchID is available through use [CocoaPods](http://cocoapods.org). To install 103 | it, simply add the following line to your Podfile: 104 | 105 | ``` 106 | pod 'SmileTouchID' 107 | 108 | ``` 109 | Or you can drag the `SmileAuth` fold to your project. 110 | 111 | **Step 2.** Import `SmileAuthenticator.h` to your `AppDelegate.m`, and add below line to `didFinishLaunchingWithOptions`. 112 | 113 | ``` 114 | [SmileAuthenticator sharedInstance].rootVC = self.window.rootViewController; 115 | ``` 116 | 117 | ![](https://raw.githubusercontent.com/liu044100/SmileTouchID/master/Example/demo_gif/step2.png) 118 | 119 | **Step 3.** In your project root view controller, add below line to `viewDidAppear:`. 120 | 121 | ``` 122 | if ([SmileAuthenticator hasPassword]) { 123 | [SmileAuthenticator sharedInstance].securityType = INPUT_TOUCHID; 124 | [[SmileAuthenticator sharedInstance] presentAuthViewController]; 125 | } 126 | ``` 127 | 128 | 129 | ![](https://raw.githubusercontent.com/liu044100/SmileTouchID/master/Example/demo_gif/step3.png) 130 | 131 | **Step 4.** Configure with your interactive UI parts, set appropriate `securityType`, then call `presentAuthViewController`. 132 | 133 | For example below image show a switch to turn the passcode on/off, when the switch turn on, the `securityType` is `INPUT_TWICE`, when turn off, the `securityType` is `INPUT_ONCE`. A button for change passcode, the `securityType` is `INPUT_THREE`. 134 | 135 | ``` 136 | - (IBAction)changePassword:(id)sender { 137 | [SmileAuthenticator sharedInstance].securityType = INPUT_THREE; 138 | [[SmileAuthenticator sharedInstance] presentAuthViewController]; 139 | } 140 | 141 | - (IBAction)passwordSwitch:(UISwitch*)passwordSwitch { 142 | if (passwordSwitch.on) { 143 | [SmileAuthenticator sharedInstance].securityType = INPUT_TWICE; 144 | } else { 145 | [SmileAuthenticator sharedInstance].securityType = INPUT_ONCE; 146 | } 147 | 148 | [[SmileAuthenticator sharedInstance] presentAuthViewController]; 149 | } 150 | 151 | ``` 152 | 153 | ![](https://raw.githubusercontent.com/liu044100/SmileTouchID/master/Example/demo_gif/step4.png) 154 | 155 | 156 | Update your interactive UI parts in `viewWillAppear`. 157 | 158 | For example, below code show update the switch and button based on `[SmileAuthenticator hasPassword]`. 159 | 160 | ``` 161 | -(void)viewWillAppear:(BOOL)animated{ 162 | [super viewWillAppear:animated]; 163 | 164 | if ([SmileAuthenticator hasPassword]) { 165 | self.mySwitch.on = YES; 166 | self.changePasswordButton.hidden = NO; 167 | } else { 168 | self.mySwitch.on = NO; 169 | self.changePasswordButton.hidden = YES; 170 | } 171 | } 172 | ``` 173 | 174 | 175 | **Step 5.** Build your project, very simple :) 176 | 177 | #About Delegate callback 178 | 179 | `SmileAuthenticator` has a delegate that can get more information about the process of authentication. 180 | 181 | The delegate name is `AuthenticatorDelegate`. It has four optional method. 182 | 183 | ``` 184 | @protocol AuthenticatorDelegate 185 | @optional 186 | /*!The method is called when AuthViewController be presented*/ 187 | -(void)AuthViewControllerPresented; 188 | @optional 189 | /*!The method is called when AuthViewController be dismissed*/ 190 | -(void)AuthViewControllerDismssed; 191 | @optional 192 | /*!The method is called when user success authentication by using Touch ID & Passcode*/ 193 | -(void)userSuccessAuthentication; 194 | @optional 195 | /*!The method is called when authentication failed*/ 196 | -(void)userFailAuthenticationWithCount:(NSInteger)failCount; 197 | @end 198 | ``` 199 | # Localization 200 | Sorry about it, but you have to do it yourself. Add below line to your `Localizable.strings`. For detail please see the example demo app. 201 | 202 | ``` 203 | /* 204 | UNIVERSAL PARTS 205 | */ 206 | "SMILE_REASON" = "Are you device owner?"; 207 | "SMILE_INPUT_FAILED" = "%ld Failed Passcode Attempt. Try again."; 208 | "SMILE_INPUT_DESCRIPTION" = "Enter %ld digit passcode"; 209 | "SMILE_INPUT_NOT_MATCH" = "Passcode not match. Try again."; 210 | "SMILE_INPUT_RE-ENTER" = "Re-enter your %ld digit Passcode"; 211 | 212 | /* 213 | INPUT_TOUCHID 214 | */ 215 | "SMILE_INPUT_TOUCHID_TITLE" = "Enter Passcode"; 216 | 217 | 218 | /* 219 | INPUT_ONCE 220 | */ 221 | "SMILE_INPUT_ONCE_TITLE" = "Turn off Passcode"; 222 | 223 | 224 | /* 225 | INPUT_TWICE 226 | */ 227 | "SMILE_INPUT_TWICE_TITLE" = "Set Passcode"; 228 | 229 | 230 | /* 231 | INPUT_THREE 232 | */ 233 | "SMILE_INPUT_THREE_TITLE" = "Change Passcode"; 234 | "SMILE_INPUT_THREE_STEP_1" = "Enter your new %ld digit Passcode"; 235 | "SMILE_INPUT_THREE_STEP_2" = "Enter your old %ld digit Passcode"; 236 | 237 | ``` 238 | 239 | # Contributions 240 | 241 | * Warmly welcome to submit a pull request. 242 | 243 | # Contact 244 | 245 | * If you have some advice or find some issue, please contact me. 246 | * Email [me](liu044100@gmail.com) 247 | 248 | # Thanks 249 | Thanks for raywenderlich's tutorial about [securing iOS User Data](http://www.raywenderlich.com/92667/securing-ios-data-keychain-touch-id-1password), I am inspired by this tutorial. 250 | 251 | # License 252 | 253 | SmileTouchID is available under the MIT license. See the LICENSE file for more info. 254 | -------------------------------------------------------------------------------- /Example/Pods/SmileTouchID/SmileAuth/Assets/SmileSettingVC.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 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 56 | 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 | -------------------------------------------------------------------------------- /Example/Pods/SmileTouchID/SmileAuth/Assets/smile_Touch_ID@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liu044100/SmileTouchID/5b6584ffd4b8f1062a380a7f19f290525351a7e4/Example/Pods/SmileTouchID/SmileAuth/Assets/smile_Touch_ID@2x.png -------------------------------------------------------------------------------- /Example/Pods/SmileTouchID/SmileAuth/Assets/smile_Touch_ID@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liu044100/SmileTouchID/5b6584ffd4b8f1062a380a7f19f290525351a7e4/Example/Pods/SmileTouchID/SmileAuth/Assets/smile_Touch_ID@3x.png -------------------------------------------------------------------------------- /Example/Pods/SmileTouchID/SmileAuth/Classes/SmileAuthenticator.h: -------------------------------------------------------------------------------- 1 | // 2 | // SmileAuthenticator.h 3 | // TouchID 4 | // 5 | // Created by ryu-ushin on 5/25/15. 6 | // Copyright (c) 2015 rain. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "SmileKeychainWrapper.h" 12 | #import "SmileSettingVC.h" 13 | 14 | typedef void(^AuthCompletionBlock)(); 15 | typedef void(^AuthErrorBlock)(LAError); 16 | 17 | /*! 18 | @typedef SecurityType 19 | 20 | @brief A struct about the SecurityType. 21 | 22 | @discussion 23 | There are four types, pass the SecurtiyType to destination View Controller for appropriate security interface. 24 | */ 25 | typedef NS_ENUM(int, SecurityType) { 26 | /*! For the user turn off the password switch, user need input their password once for turn the password off. */ 27 | INPUT_ONCE, 28 | /*! For the user turn on the password switch, user need input their password twice to confirm their password match each other. */ 29 | INPUT_TWICE, 30 | /*! For the user change the password, user need input their password three times for change to new password. */ 31 | INPUT_THREE, 32 | /*! For the user open app, user can use touch ID or input password. */ 33 | INPUT_TOUCHID, 34 | }; 35 | 36 | @protocol SmileAuthenticatorDelegate; 37 | 38 | @interface SmileAuthenticator : NSObject 39 | 40 | @property (nonatomic, copy) NSString * localizedReason; 41 | @property (nonatomic, strong) SmileKeychainWrapper *keychainWrapper; 42 | @property (nonatomic, assign) SecurityType securityType; 43 | @property (nonatomic, strong) UIViewController *rootVC; 44 | @property (nonatomic, weak) id delegate; 45 | /*!@brief For customization, use this property to customize tint color. The default color is pink.*/ 46 | @property (nonatomic, strong) UIColor *tintColor; 47 | /*!@brief For customization, use this property to customize description label text color. The default color is black, if nightMode on, the color is white.*/ 48 | @property (nonatomic, strong) UIColor *descriptionTextColor; 49 | /*!@brief For customization, use this property to customize Touch ID icon. The default icon is the Apple official pink Touch ID icon.*/ 50 | @property (nonatomic, strong) NSString *touchIDIconName; 51 | /*!@brief For customization, use this property to set the app logo to UI.*/ 52 | @property (nonatomic, strong) NSString *appLogoName; 53 | /*!@brief For customization, use this property to set the backgroundImage.*/ 54 | @property (nonatomic, strong) UIImage *backgroundImage; 55 | /*!@brief For customization, use this property to change passcode digit. The default digit is 4.*/ 56 | @property (nonatomic) NSInteger passcodeDigit; 57 | /*!@brief For customization, if set it to Yes, change UINavigationBar to transparent, the default value is No.*/ 58 | @property (nonatomic) BOOL navibarTranslucent; 59 | /*!@brief For customization, if set it to Yes, change to a black style UI, the default value is No.*/ 60 | @property (nonatomic) BOOL nightMode; 61 | /*!@brief For customization, if set it to Yes, add parallax effect to password circle views, the default value is Yes.*/ 62 | @property (nonatomic) BOOL parallaxMode; 63 | 64 | +(SmileAuthenticator*)sharedInstance; 65 | + (BOOL)canAuthenticateWithError:(NSError **) error; 66 | +(BOOL)hasPassword; 67 | +(BOOL)isSamePassword:(NSString *)userInput; 68 | +(void)clearPassword; 69 | 70 | -(void)userSetPassword:(NSString*)newPassword; 71 | -(void)authenticateWithSuccess:(AuthCompletionBlock) authSuccessBlock andFailure:(AuthErrorBlock) failureBlock; 72 | 73 | -(void)presentAuthViewController; 74 | -(void)authViewControllerDismissed; 75 | -(void)touchID_OR_PasswordAuthSuccess; 76 | -(void)touchID_OR_PasswordAuthFail:(NSInteger)failCount; 77 | -(void)touchID_OR_PasswordTurnOff; 78 | -(void)touchID_OR_PasswordTurnOn; 79 | -(void)touchID_OR_PasswordChange; 80 | @end 81 | 82 | @protocol SmileAuthenticatorDelegate 83 | @optional 84 | /*!The method is called when AuthViewController be presented*/ 85 | -(void)AuthViewControllerPresented; 86 | @optional 87 | /*!The method is called when AuthViewController be dismissed*/ 88 | -(void)AuthViewControllerDismssed; 89 | @optional 90 | /*!The method is called when user success authentication by using Touch ID & Passcode*/ 91 | -(void)userSuccessAuthentication; 92 | @optional 93 | /*!The method is called when authentication failed*/ 94 | -(void)userFailAuthenticationWithCount:(NSInteger)failCount; 95 | @optional 96 | /*!The method is called when user turn password on.*/ 97 | -(void)userTurnPasswordOn; 98 | @optional 99 | /*!The method is called when user turn password off.*/ 100 | -(void)userTurnPasswordOff; 101 | @optional 102 | /*!The method is called when user change password.*/ 103 | -(void)userChangePassword; 104 | @end 105 | -------------------------------------------------------------------------------- /Example/Pods/SmileTouchID/SmileAuth/Classes/SmileAuthenticator.m: -------------------------------------------------------------------------------- 1 | // 2 | // SmileAuthenticator.m 3 | // TouchID 4 | // 5 | // Created by ryu-ushin on 5/25/15. 6 | // Copyright (c) 2015 rain. All rights reserved. 7 | // 8 | 9 | #import "SmileAuthenticator.h" 10 | 11 | #define kPasswordLength 4 12 | #define kTouchIDIcon @"smile_Touch_ID" 13 | 14 | static NSString *kKeyChainObjectKey = @"v_Data"; 15 | static NSString *kStoryBoardName = @"SmileSettingVC"; 16 | static NSString *kSmileSettingNaviID = @"smileSettingsNavi"; 17 | 18 | #define SmileTouchID_DispatchMainThread(block, ...) if(block) dispatch_async(dispatch_get_main_queue(), ^{ block(__VA_ARGS__); }) 19 | 20 | @interface SmileAuthenticator() 21 | 22 | @property (nonatomic, assign) LAPolicy policy; 23 | @property (nonatomic, strong) LAContext * context; 24 | 25 | @end 26 | 27 | 28 | @implementation SmileAuthenticator{ 29 | BOOL _isAuthenticated; 30 | BOOL _didReturnFromBackground; 31 | BOOL _isShowLogin; 32 | } 33 | 34 | #pragma mark -getter 35 | 36 | -(NSInteger)passcodeDigit{ 37 | if (!_passcodeDigit || _passcodeDigit < 0) { 38 | return kPasswordLength; 39 | } else { 40 | return _passcodeDigit; 41 | } 42 | } 43 | 44 | -(NSString *)touchIDIconName{ 45 | if (!_touchIDIconName.length) { 46 | return kTouchIDIcon; 47 | } else { 48 | return _touchIDIconName; 49 | } 50 | } 51 | 52 | #pragma mark - dealloc 53 | 54 | -(void)dealloc{ 55 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 56 | } 57 | 58 | #pragma mark - for Delegate 59 | 60 | -(void)touchID_OR_PasswordAuthSuccess{ 61 | if ([self.delegate respondsToSelector:@selector(userSuccessAuthentication)]) { 62 | [self.delegate userSuccessAuthentication]; 63 | } 64 | } 65 | 66 | -(void)touchID_OR_PasswordAuthFail:(NSInteger)failCount{ 67 | if ([self.delegate respondsToSelector:@selector(userFailAuthenticationWithCount:)]) { 68 | [self.delegate userFailAuthenticationWithCount:failCount]; 69 | } 70 | } 71 | 72 | -(void)touchID_OR_PasswordTurnOff{ 73 | if ([self.delegate respondsToSelector:@selector(userTurnPasswordOff)]) { 74 | [self.delegate userTurnPasswordOff]; 75 | } 76 | } 77 | 78 | -(void)touchID_OR_PasswordTurnOn{ 79 | if ([self.delegate respondsToSelector:@selector(userTurnPasswordOn)]) { 80 | [self.delegate userTurnPasswordOn]; 81 | } 82 | } 83 | 84 | -(void)touchID_OR_PasswordChange{ 85 | if ([self.delegate respondsToSelector:@selector(userChangePassword)]) { 86 | [self.delegate userChangePassword]; 87 | } 88 | } 89 | 90 | -(void)presentAuthViewController{ 91 | 92 | if (self.securityType != INPUT_TOUCHID) { 93 | _isAuthenticated = NO; 94 | } 95 | 96 | if (!_isAuthenticated) { 97 | 98 | BOOL isAnimated = YES; 99 | 100 | if (self.securityType == INPUT_TOUCHID) { 101 | isAnimated = NO; 102 | } 103 | 104 | //dimiss all presentedViewController, for example, if user is editing password 105 | if (self.rootVC.presentedViewController) { 106 | [self.rootVC.presentedViewController dismissViewControllerAnimated:NO completion:nil]; 107 | } 108 | 109 | if ([self.delegate respondsToSelector:@selector(AuthViewControllerPresented)]) { 110 | [self.delegate AuthViewControllerPresented]; 111 | } 112 | 113 | _isShowLogin = YES; 114 | 115 | UIStoryboard *storyboard = [UIStoryboard storyboardWithName:kStoryBoardName bundle: nil]; 116 | 117 | UINavigationController *naviVC = [storyboard instantiateViewControllerWithIdentifier:kSmileSettingNaviID]; 118 | 119 | [self.rootVC presentViewController:naviVC animated:isAnimated completion:nil]; 120 | } 121 | } 122 | 123 | -(void)authViewControllerDismissed{ 124 | if ([self.delegate respondsToSelector:@selector(AuthViewControllerDismssed)]) { 125 | [self.delegate AuthViewControllerDismssed]; 126 | } 127 | 128 | _isAuthenticated = true; 129 | _isShowLogin = NO; 130 | } 131 | 132 | #pragma mark - NSNotificationCenter 133 | 134 | -(void)configureNotification{ 135 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appDidEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil]; 136 | 137 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appWillEnterForeground:) name:UIApplicationWillEnterForegroundNotification object:nil]; 138 | } 139 | 140 | -(void)appDidEnterBackground:(NSNotification*)notification{ 141 | _isAuthenticated = NO; 142 | _didReturnFromBackground = YES; 143 | } 144 | 145 | -(void)appWillEnterForeground:(NSNotification*)notification{ 146 | if (_didReturnFromBackground && !_isShowLogin) { 147 | if ([SmileAuthenticator hasPassword]) { 148 | //show login vc 149 | self.securityType = INPUT_TOUCHID; 150 | [self presentAuthViewController]; 151 | } 152 | } 153 | } 154 | 155 | #pragma mark - init 156 | 157 | +(SmileAuthenticator *)sharedInstance{ 158 | static id sharedInstance = nil; 159 | static dispatch_once_t onceToken; 160 | dispatch_once(&onceToken, ^{ 161 | sharedInstance = [[self alloc] init]; 162 | }); 163 | 164 | return sharedInstance; 165 | } 166 | 167 | 168 | -(instancetype)init{ 169 | if (self = [super init]) { 170 | self.context = [[LAContext alloc] init]; 171 | self.policy = LAPolicyDeviceOwnerAuthenticationWithBiometrics; 172 | self.localizedReason = NSLocalizedString(@"SMILE_REASON", nil); 173 | self.keychainWrapper = [[SmileKeychainWrapper alloc] init]; 174 | self.securityType = INPUT_TWICE; 175 | self.parallaxMode = YES; 176 | 177 | [self configureNotification]; 178 | } 179 | return self; 180 | } 181 | 182 | #pragma mark - TouchID 183 | 184 | + (BOOL) canAuthenticateWithError:(NSError **) error 185 | { 186 | if ([NSClassFromString(@"LAContext") class]) { 187 | if ([[SmileAuthenticator sharedInstance].context canEvaluatePolicy:[SmileAuthenticator sharedInstance].policy error:error]) { 188 | return YES; 189 | } 190 | return NO; 191 | } 192 | return NO; 193 | } 194 | 195 | -(void)authenticateWithSuccess:(AuthCompletionBlock)authSuccessBlock andFailure:(AuthErrorBlock)failureBlock{ 196 | 197 | NSError *authError = nil; 198 | 199 | self.context = [[LAContext alloc] init]; 200 | 201 | if ([SmileAuthenticator canAuthenticateWithError:&authError]) { 202 | [self.context evaluatePolicy:self.policy localizedReason:self.localizedReason reply:^(BOOL success, NSError *error) { 203 | if (success) { 204 | SmileTouchID_DispatchMainThread(^(){ 205 | authSuccessBlock(); 206 | }); 207 | } 208 | 209 | else { 210 | switch (error.code) { 211 | case LAErrorAuthenticationFailed: 212 | 213 | NSLog(@"LAErrorAuthenticationFailed"); 214 | 215 | break; 216 | 217 | case LAErrorUserCancel: 218 | 219 | NSLog(@"LAErrorUserCancel"); 220 | 221 | break; 222 | 223 | case LAErrorUserFallback: 224 | 225 | NSLog(@"LAErrorUserFallback"); 226 | 227 | break; 228 | 229 | case LAErrorSystemCancel: 230 | 231 | NSLog(@"LAErrorSystemCancel"); 232 | 233 | break; 234 | 235 | case LAErrorPasscodeNotSet: 236 | 237 | NSLog(@"LAErrorPasscodeNotSet"); 238 | 239 | break; 240 | 241 | case LAErrorTouchIDNotAvailable: 242 | 243 | NSLog(@"LAErrorTouchIDNotAvailable"); 244 | 245 | break; 246 | 247 | case LAErrorTouchIDNotEnrolled: 248 | 249 | NSLog(@"LAErrorTouchIDNotEnrolled"); 250 | 251 | break; 252 | 253 | default: 254 | break; 255 | } 256 | 257 | SmileTouchID_DispatchMainThread(^(){ 258 | failureBlock((LAError) error.code); 259 | }); 260 | } 261 | }]; 262 | } 263 | 264 | else { 265 | failureBlock((LAError) authError.code); 266 | } 267 | } 268 | 269 | #pragma mark - Utility 270 | 271 | +(BOOL)hasPassword { 272 | 273 | if ([(NSString*)[[SmileAuthenticator sharedInstance].keychainWrapper myObjectForKey:kKeyChainObjectKey] length] > 0) { 274 | return YES; 275 | } 276 | 277 | return NO; 278 | } 279 | 280 | +(BOOL)isSamePassword:(NSString *)userInput{ 281 | //use this line to log password, if you forgot it. 282 | // NSLog(@"the password -> %@", [[SmileAuthenticator sharedInstance].keychainWrapper myObjectForKey:kKeyChainObjectKey]); 283 | if ([userInput isEqualToString:[[SmileAuthenticator sharedInstance].keychainWrapper myObjectForKey:kKeyChainObjectKey]]) { 284 | return YES; 285 | } 286 | 287 | return NO; 288 | } 289 | 290 | -(void)userSetPassword:(NSString*)newPassword{ 291 | [self.keychainWrapper mySetObject:newPassword forKey:(__bridge id)(kSecValueData)]; 292 | [self.keychainWrapper writeToKeychain]; 293 | } 294 | 295 | +(void)clearPassword{ 296 | [[SmileAuthenticator sharedInstance].keychainWrapper resetKeychainItem]; 297 | [[SmileAuthenticator sharedInstance].keychainWrapper writeToKeychain]; 298 | } 299 | 300 | @end 301 | -------------------------------------------------------------------------------- /Example/Pods/SmileTouchID/SmileAuth/Classes/SmileKeychainWrapper.h: -------------------------------------------------------------------------------- 1 | // 2 | // KeychainWrapper.h 3 | // Apple's Keychain Services Programming Guide 4 | // 5 | // Created by Tim Mitra on 11/17/14. 6 | // Copyright (c) 2014 Apple. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface SmileKeychainWrapper : NSObject 13 | 14 | - (void)mySetObject:(id)inObject forKey:(id)key; 15 | - (id)myObjectForKey:(id)key; 16 | - (void)writeToKeychain; 17 | - (void)resetKeychainItem; 18 | @end 19 | -------------------------------------------------------------------------------- /Example/Pods/SmileTouchID/SmileAuth/Classes/SmileKeychainWrapper.m: -------------------------------------------------------------------------------- 1 | // 2 | // KeychainWrapper.h 3 | // Apple's Keychain Services Programming Guide 4 | // 5 | // Created by Tim Mitra on 11/17/14. 6 | // Copyright (c) 2014 Apple. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //Unique string used to identify the keychain item: 12 | static const UInt8 kKeychainItemIdentifier[] = "com.apple.dts.KeychainUI\0"; 13 | 14 | @interface SmileKeychainWrapper () 15 | 16 | @property (nonatomic, strong) NSMutableDictionary *keychainData; 17 | @property (nonatomic, strong) NSMutableDictionary *genericPasswordQuery; 18 | 19 | @end 20 | 21 | @interface SmileKeychainWrapper (PrivateMethods) 22 | 23 | //The following two methods translate dictionaries between the format used by 24 | // the view controller (NSString *) and the Keychain Services API: 25 | - (NSMutableDictionary *)secItemFormatToDictionary:(NSDictionary *)dictionaryToConvert; 26 | - (NSMutableDictionary *)dictionaryToSecItemFormat:(NSDictionary *)dictionaryToConvert; 27 | // Method used to write data to the keychain: 28 | - (void)writeToKeychain; 29 | 30 | @end 31 | 32 | 33 | @implementation SmileKeychainWrapper 34 | 35 | - (instancetype)init 36 | { 37 | self = [super init]; 38 | 39 | if (self) { 40 | 41 | 42 | OSStatus keychainErr = noErr; 43 | // Set up the keychain search dictionary: 44 | _genericPasswordQuery = [[NSMutableDictionary alloc] init]; 45 | 46 | // This keychain item is a generic password. 47 | [_genericPasswordQuery setObject:(__bridge id)kSecClassGenericPassword 48 | forKey:(__bridge id)kSecClass]; 49 | 50 | // The kSecAttrGeneric attribute is used to store a unique string that is used 51 | // to easily identify and find this keychain item. The string is first 52 | // converted to an NSData object: 53 | NSData *keychainItemID = [NSData dataWithBytes:kKeychainItemIdentifier 54 | length:strlen((const char *)kKeychainItemIdentifier)]; 55 | [_genericPasswordQuery setObject:keychainItemID forKey:(__bridge id)kSecAttrGeneric]; 56 | 57 | // Return the attributes of the first match only: 58 | [_genericPasswordQuery setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit]; 59 | 60 | // Return the attributes of the keychain item (the password is 61 | // acquired in the secItemFormatToDictionary: method): 62 | [_genericPasswordQuery setObject:(__bridge id)kCFBooleanTrue 63 | forKey:(__bridge id)kSecReturnAttributes]; 64 | 65 | //Initialize the dictionary used to hold return data from the keychain: 66 | CFMutableDictionaryRef outDictionary = nil; 67 | // If the keychain item exists, return the attributes of the item: 68 | keychainErr = SecItemCopyMatching((__bridge CFDictionaryRef)_genericPasswordQuery, 69 | (CFTypeRef *)&outDictionary); 70 | 71 | if (keychainErr == noErr) { 72 | // Convert the data dictionary into the format used by the view controller: 73 | self.keychainData = [self secItemFormatToDictionary:(__bridge_transfer NSMutableDictionary *)outDictionary]; 74 | } else if (keychainErr == errSecItemNotFound) { 75 | // Put default values into the keychain if no matching 76 | // keychain item is found: 77 | [self resetKeychainItem]; 78 | if (outDictionary) CFRelease(outDictionary); 79 | } else { 80 | // Any other error is unexpected. 81 | NSAssert(NO, @"Serious error.\n"); 82 | if (outDictionary) CFRelease(outDictionary); 83 | } 84 | } 85 | 86 | return self; 87 | } 88 | 89 | // Implement the mySetObject:forKey method, which writes attributes to the keychain: 90 | - (void)mySetObject:(id)inObject forKey:(id)key 91 | { 92 | if (inObject == nil) return; 93 | id currentObject = [_keychainData objectForKey:key]; 94 | if (![currentObject isEqual:inObject]) 95 | { 96 | [_keychainData setObject:inObject forKey:key]; 97 | [self writeToKeychain]; 98 | } 99 | } 100 | 101 | // Implement the myObjectForKey: method, which reads an attribute value from a dictionary: 102 | - (id)myObjectForKey:(id)key 103 | { 104 | return [_keychainData objectForKey:key]; 105 | } 106 | 107 | // Reset the values in the keychain item, or create a new item if it 108 | // doesn't already exist: 109 | 110 | - (void)resetKeychainItem 111 | { 112 | if (!_keychainData) //Allocate the keychainData dictionary if it doesn't exist yet. 113 | { 114 | self.keychainData = [[NSMutableDictionary alloc] init]; 115 | } 116 | else if (_keychainData) 117 | { 118 | // Format the data in the keychainData dictionary into the format needed for a query 119 | // and put it into tmpDictionary: 120 | NSMutableDictionary *tmpDictionary = 121 | [self dictionaryToSecItemFormat:_keychainData]; 122 | // Delete the keychain item in preparation for resetting the values: 123 | // OSStatus errorcode = 124 | SecItemDelete((__bridge CFDictionaryRef)tmpDictionary); 125 | // NSLog(@"keychain error code -> %d", (int)errorcode); 126 | // NSAssert(errorcode == noErr, @"Problem deleting current keychain item." ); 127 | } 128 | 129 | // Default generic data for Keychain Item: 130 | [_keychainData setObject:@"" forKey:(__bridge id)kSecAttrLabel]; 131 | [_keychainData setObject:@"" forKey:(__bridge id)kSecAttrDescription]; 132 | [_keychainData setObject:@"" forKey:(__bridge id)kSecAttrAccount]; 133 | [_keychainData setObject:@"" forKey:(__bridge id)kSecAttrService]; 134 | [_keychainData setObject:@"" forKey:(__bridge id)kSecAttrComment]; 135 | [_keychainData setObject:@"" forKey:(__bridge id)kSecValueData]; 136 | } 137 | 138 | 139 | // Implement the dictionaryToSecItemFormat: method, which takes the attributes that 140 | // you want to add to the keychain item and sets up a dictionary in the format 141 | // needed by Keychain Services: 142 | - (NSMutableDictionary *)dictionaryToSecItemFormat:(NSDictionary *)dictionaryToConvert 143 | { 144 | // This method must be called with a properly populated dictionary 145 | // containing all the right key/value pairs for a keychain item search. 146 | 147 | // Create the return dictionary: 148 | NSMutableDictionary *returnDictionary = 149 | [NSMutableDictionary dictionaryWithDictionary:dictionaryToConvert]; 150 | 151 | // Add the keychain item class and the generic attribute: 152 | NSData *keychainItemID = [NSData dataWithBytes:kKeychainItemIdentifier 153 | length:strlen((const char *)kKeychainItemIdentifier)]; 154 | [returnDictionary setObject:keychainItemID forKey:(__bridge id)kSecAttrGeneric]; 155 | [returnDictionary setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass]; 156 | 157 | // Convert the password NSString to NSData to fit the API paradigm: 158 | NSString *passwordString = [dictionaryToConvert objectForKey:(__bridge id)kSecValueData]; 159 | [returnDictionary setObject:[passwordString dataUsingEncoding:NSUTF8StringEncoding] 160 | forKey:(__bridge id)kSecValueData]; 161 | return returnDictionary; 162 | } 163 | 164 | // Implement the secItemFormatToDictionary: method, which takes the attribute dictionary 165 | // obtained from the keychain item, acquires the password from the keychain, and 166 | // adds it to the attribute dictionary: 167 | - (NSMutableDictionary *)secItemFormatToDictionary:(NSDictionary *)dictionaryToConvert 168 | { 169 | // This method must be called with a properly populated dictionary 170 | // containing all the right key/value pairs for the keychain item. 171 | 172 | // Create a return dictionary populated with the attributes: 173 | NSMutableDictionary *returnDictionary = [NSMutableDictionary 174 | dictionaryWithDictionary:dictionaryToConvert]; 175 | 176 | // To acquire the password data from the keychain item, 177 | // first add the search key and class attribute required to obtain the password: 178 | [returnDictionary setObject:(__bridge id)kCFBooleanTrue forKey:(__bridge id)kSecReturnData]; 179 | [returnDictionary setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass]; 180 | // Then call Keychain Services to get the password: 181 | CFDataRef passwordData = NULL; 182 | OSStatus keychainError = noErr; // 183 | keychainError = SecItemCopyMatching((__bridge CFDictionaryRef)returnDictionary, 184 | (CFTypeRef *)&passwordData); 185 | if (keychainError == noErr) 186 | { 187 | // Remove the kSecReturnData key; we don't need it anymore: 188 | [returnDictionary removeObjectForKey:(__bridge id)kSecReturnData]; 189 | 190 | // Convert the password to an NSString and add it to the return dictionary: 191 | NSString *password = [[NSString alloc] initWithBytes:[(__bridge_transfer NSData *)passwordData bytes] 192 | length:[(__bridge NSData *)passwordData length] encoding:NSUTF8StringEncoding]; 193 | [returnDictionary setObject:password forKey:(__bridge id)kSecValueData]; 194 | } 195 | // Don't do anything if nothing is found. 196 | else if (keychainError == errSecItemNotFound) { 197 | NSAssert(NO, @"Nothing was found in the keychain.\n"); 198 | if (passwordData) CFRelease(passwordData); 199 | } 200 | // Any other error is unexpected. 201 | else 202 | { 203 | NSAssert(NO, @"Serious error.\n"); 204 | if (passwordData) CFRelease(passwordData); 205 | } 206 | 207 | return returnDictionary; 208 | } 209 | 210 | // could be in a class 211 | - (void)writeToKeychain { 212 | 213 | CFDictionaryRef attributes = nil; 214 | NSMutableDictionary *updateItem = nil; 215 | 216 | // If the keychain item already exists, modify it: 217 | if (SecItemCopyMatching((__bridge CFDictionaryRef)_genericPasswordQuery, 218 | (CFTypeRef *)&attributes) == noErr) 219 | { 220 | // First, get the attributes returned from the keychain and add them to the 221 | // dictionary that controls the update: 222 | updateItem = [NSMutableDictionary dictionaryWithDictionary:(__bridge_transfer NSDictionary *)attributes]; 223 | 224 | // Second, get the class value from the generic password query dictionary and 225 | // add it to the updateItem dictionary: 226 | [updateItem setObject:[_genericPasswordQuery objectForKey:(__bridge id)kSecClass] 227 | forKey:(__bridge id)kSecClass]; 228 | 229 | // Finally, set up the dictionary that contains new values for the attributes: 230 | NSMutableDictionary *tempCheck = [self dictionaryToSecItemFormat:_keychainData]; 231 | //Remove the class--it's not a keychain attribute: 232 | [tempCheck removeObjectForKey:(__bridge id)kSecClass]; 233 | 234 | // You can update only a single keychain item at a time. 235 | //OSStatus errorcode = 236 | SecItemUpdate( 237 | (__bridge CFDictionaryRef)updateItem, 238 | (__bridge CFDictionaryRef)tempCheck); 239 | // NSAssert(errorcode == noErr, @"Couldn't update the Keychain Item." ); 240 | } 241 | else 242 | { 243 | // No previous item found; add the new item. 244 | // The new value was added to the keychainData dictionary in the mySetObject routine, 245 | // and the other values were added to the keychainData dictionary previously. 246 | // No pointer to the newly-added items is needed, so pass NULL for the second parameter: 247 | // OSStatus errorcode = 248 | SecItemAdd( 249 | (__bridge CFDictionaryRef)[self dictionaryToSecItemFormat:_keychainData], 250 | NULL); 251 | // NSAssert(errorcode == noErr, @"Couldn't add the Keychain Item." ); 252 | if (attributes) CFRelease(attributes); 253 | } 254 | 255 | } 256 | 257 | 258 | @end 259 | -------------------------------------------------------------------------------- /Example/Pods/SmileTouchID/SmileAuth/Classes/SmilePasswordContainerView.h: -------------------------------------------------------------------------------- 1 | // 2 | // SmilePasswordContainerView.h 3 | // TouchID 4 | // 5 | // Created by yuchen liu on 5/27/15. 6 | // Copyright (c) 2015 rain. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "SmilePasswordView.h" 11 | 12 | 13 | @protocol SmileContainerLayoutDelegate; 14 | 15 | //IB_DESIGNABLE 16 | 17 | @interface SmilePasswordContainerView : UIView 18 | //IBInspectable 19 | @property (nonatomic, strong) UIColor *mainColor; 20 | @property (nonatomic, strong) SmilePasswordView *smilePasswordView; 21 | @property (nonatomic, weak) id delegate; 22 | 23 | @end 24 | 25 | @protocol SmileContainerLayoutDelegate 26 | @required 27 | -(void)smileContainerLayoutSubview; 28 | @end 29 | -------------------------------------------------------------------------------- /Example/Pods/SmileTouchID/SmileAuth/Classes/SmilePasswordContainerView.m: -------------------------------------------------------------------------------- 1 | // 2 | // SmilePasswordContainerView.m 3 | // TouchID 4 | // 5 | // Created by yuchen liu on 5/27/15. 6 | // Copyright (c) 2015 rain. All rights reserved. 7 | // 8 | 9 | #import "SmilePasswordContainerView.h" 10 | #import "SmileAuthenticator.h" 11 | 12 | @interface SmilePasswordContainerView() 13 | 14 | @end 15 | 16 | @implementation SmilePasswordContainerView { 17 | 18 | } 19 | 20 | /* 21 | // Only override drawRect: if you perform custom drawing. 22 | // An empty implementation adversely affects performance during animation. 23 | - (void)drawRect:(CGRect)rect { 24 | // Drawing code 25 | } 26 | */ 27 | 28 | -(void)configureAndAddSmilePasswordView{ 29 | 30 | if (self.smilePasswordView) { 31 | [self.smilePasswordView removeFromSuperview]; 32 | self.smilePasswordView = nil; 33 | } 34 | 35 | self.backgroundColor = [UIColor clearColor]; 36 | 37 | NSInteger count = [SmileAuthenticator sharedInstance].passcodeDigit; 38 | 39 | if ([SmileAuthenticator sharedInstance].tintColor) { 40 | self.mainColor = [SmileAuthenticator sharedInstance].tintColor; 41 | } 42 | 43 | self.smilePasswordView = [[SmilePasswordView alloc] initWithCircleColor:self.mainColor circleCount:count frame:self.bounds]; 44 | 45 | [self addSubview:self.smilePasswordView]; 46 | 47 | if (self.delegate) { 48 | [self.delegate smileContainerLayoutSubview]; 49 | } 50 | } 51 | 52 | #if TARGET_INTERFACE_BUILDER 53 | 54 | -(void)willMoveToSuperview:(UIView *)newSuperview{ 55 | [self configureAndAddSmilePasswordView]; 56 | } 57 | 58 | #else 59 | -(void)awakeFromNib{ 60 | [super awakeFromNib]; 61 | } 62 | 63 | -(void)layoutSubviews{ 64 | 65 | [super layoutSubviews]; 66 | 67 | [self configureAndAddSmilePasswordView]; 68 | } 69 | 70 | #endif 71 | 72 | @end 73 | -------------------------------------------------------------------------------- /Example/Pods/SmileTouchID/SmileAuth/Classes/SmilePasswordView.h: -------------------------------------------------------------------------------- 1 | // 2 | // SmilePasswordView.h 3 | // TouchID 4 | // 5 | // Created by yuchen liu on 5/27/15. 6 | // Copyright (c) 2015 rain. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SmilePasswordView : UIView 12 | 13 | @property (nonatomic) UIColor *circleColor; 14 | 15 | @property (nonatomic, strong) NSMutableArray *posiArray; 16 | 17 | @property (nonatomic) NSInteger dotCount; 18 | 19 | -(instancetype)initWithCircleColor:(UIColor*)circleColor circleCount:(NSInteger)count frame:(CGRect)frame; 20 | 21 | -(void)shakeAnimationWithCompletion:(dispatch_block_t)completion; 22 | -(void)slideToLeftAnimationWithCompletion:(dispatch_block_t)completion; 23 | 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /Example/Pods/SmileTouchID/SmileAuth/Classes/SmilePasswordView.m: -------------------------------------------------------------------------------- 1 | // 2 | // SmilePasswordView.m 3 | // TouchID 4 | // 5 | // Created by yuchen liu on 5/27/15. 6 | // Copyright (c) 2015 rain. All rights reserved. 7 | // 8 | 9 | #import "SmilePasswordView.h" 10 | 11 | #define SCREEN_WIDTH ([[UIScreen mainScreen] bounds].size.width) 12 | 13 | #define SmileTouchID_DispatchMainThread(block, ...) if(block) dispatch_async(dispatch_get_main_queue(), ^{ block(__VA_ARGS__); }) 14 | 15 | static CGFloat kLineWidthConst = 12.0; 16 | static CGFloat kDotRadiusConst = 5.0; 17 | static CGFloat kMAX_RadiusConst = 32.0; 18 | 19 | @interface SmilePasswordView() 20 | 21 | @property (nonatomic) NSInteger count; 22 | @property (nonatomic) CGFloat radius; 23 | @property (nonatomic) CGFloat spacing; 24 | 25 | @end 26 | 27 | @implementation SmilePasswordView{ 28 | BOOL _direction; 29 | NSInteger _shakeCount; 30 | } 31 | 32 | /* 33 | // Only override drawRect: if you perform custom drawing. 34 | // An empty implementation adversely affects performance during animation. 35 | - (void)drawRect:(CGRect)rect { 36 | // Drawing code 37 | } 38 | */ 39 | 40 | - (void)drawRect:(CGRect)rect 41 | { 42 | [super drawRect:rect]; 43 | 44 | if (!self.posiArray) { 45 | self.posiArray = [NSMutableArray new]; 46 | } else { 47 | [self.posiArray removeAllObjects]; 48 | } 49 | 50 | CGFloat centerX = CGRectGetMidX(self.bounds); 51 | CGFloat centerY = CGRectGetMidY(self.bounds); 52 | 53 | [self.circleColor setFill]; 54 | // CGContextFillRect(UIGraphicsGetCurrentContext(), rect); 55 | [self.circleColor setStroke]; 56 | 57 | CGFloat lineWidth = self.radius/kLineWidthConst; 58 | CGFloat outLineRadius = self.radius - lineWidth; 59 | 60 | BOOL isOdd = self.count%2; 61 | 62 | if (isOdd) { 63 | //3 64 | int middle = (int)(self.count + 1)/2; 65 | 66 | 67 | for (int i = 1; i <= self.count; i++) { 68 | int ii = middle - i; 69 | 70 | CGFloat theXPosi = centerX - (self.radius * 2 + self.spacing) * ii; 71 | 72 | NSNumber *theNumber = [NSNumber numberWithFloat:theXPosi]; 73 | 74 | [self.posiArray addObject:theNumber]; 75 | } 76 | 77 | } else { 78 | //4 79 | int middle = (int)(self.count)/2; 80 | 81 | 82 | for (int i = 1; i <= self.count; i++) { 83 | int ii = middle - i; 84 | 85 | CGFloat theXPosi = centerX - (self.radius * 2 + self.spacing) * ii - self.radius - self.spacing/2; 86 | 87 | NSNumber *theNumber = [NSNumber numberWithFloat:theXPosi]; 88 | 89 | [self.posiArray addObject:theNumber]; 90 | } 91 | } 92 | 93 | for (int i = 0; i < self.posiArray.count; i++) { 94 | NSNumber *xNumber = self.posiArray[i]; 95 | 96 | CGPoint thePosi = CGPointMake(xNumber.floatValue, centerY); 97 | 98 | UIBezierPath *thePath = [self pathWithCenter:thePosi radius:outLineRadius lineWidth:lineWidth]; 99 | [thePath stroke]; 100 | 101 | CGFloat dotRadius = self.radius/kDotRadiusConst; 102 | 103 | if (i + 1 <= self.dotCount) { 104 | UIBezierPath *dotPath = [UIBezierPath bezierPathWithArcCenter:thePosi radius:dotRadius startAngle:0 endAngle:2 * M_PI clockwise:false]; 105 | [dotPath fill]; 106 | } else { 107 | 108 | } 109 | 110 | } 111 | } 112 | 113 | -(UIBezierPath*)pathWithCenter:(CGPoint)center radius:(CGFloat)radius lineWidth:(CGFloat)lineWidth{ 114 | UIBezierPath *outLinePath = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:0 endAngle:2 * M_PI clockwise:false]; 115 | outLinePath.lineWidth = lineWidth; 116 | 117 | return outLinePath; 118 | } 119 | 120 | -(instancetype)initWithCircleColor:(UIColor*)circleColor circleCount:(NSInteger)count frame:(CGRect)frame{ 121 | 122 | self = [[SmilePasswordView alloc] initWithFrame:frame]; 123 | 124 | self.backgroundColor = [UIColor clearColor]; 125 | self.circleColor = circleColor; 126 | self.count = count; 127 | self.radius = [self getCircleRadius]; 128 | 129 | return self; 130 | } 131 | 132 | -(instancetype)initWithFrame:(CGRect)frame{ 133 | if (self = [super initWithFrame:frame]) { 134 | 135 | } 136 | 137 | return self; 138 | } 139 | 140 | -(id)initWithCoder:(NSCoder *)aDecoder{ 141 | if (self = [super initWithCoder:aDecoder]) { 142 | 143 | } 144 | 145 | return self; 146 | } 147 | 148 | #pragma mark - setter 149 | -(void)setDotCount:(NSInteger)dotCount{ 150 | _dotCount = dotCount; 151 | 152 | [self setNeedsDisplay]; 153 | } 154 | 155 | #pragma mark - getter 156 | 157 | -(CGFloat)spacing{ 158 | 159 | return self.radius/(self.count/2); 160 | } 161 | 162 | #pragma mark - set up 163 | 164 | -(CGFloat)getCircleRadius{ 165 | CGFloat myRadius; 166 | 167 | CGFloat height = CGRectGetHeight(self.bounds); 168 | CGFloat width = CGRectGetWidth(self.bounds); 169 | 170 | if (self.count * height + (self.count - 1) * height/4 > width) { 171 | myRadius = floor((width/(2*self.count + (self.count - 1)/2))); 172 | } else { 173 | myRadius = floor(height/2); 174 | } 175 | 176 | if (myRadius > kMAX_RadiusConst) { 177 | myRadius = kMAX_RadiusConst; 178 | } 179 | 180 | return myRadius; 181 | } 182 | 183 | 184 | #pragma mark - animation 185 | 186 | -(void)shakeAnimationWithCompletion:(dispatch_block_t)completion{ 187 | 188 | NSInteger maxShakeCount = 5; 189 | 190 | CGFloat centerX = CGRectGetMidX(self.bounds); 191 | CGFloat centerY = CGRectGetMidY(self.bounds); 192 | 193 | NSTimeInterval duration = 0.15; 194 | NSInteger moveX = 3; 195 | 196 | if (_shakeCount == 0 || _shakeCount == maxShakeCount) { 197 | duration = duration/2; 198 | moveX = moveX; 199 | } else { 200 | duration = duration; 201 | moveX = 2 * moveX; 202 | } 203 | 204 | [UIView animateWithDuration:duration delay:0.0 usingSpringWithDamping:0.01 initialSpringVelocity:0.35 options:UIViewAnimationOptionCurveEaseInOut animations:^{ 205 | if (!_direction) { 206 | self.center = CGPointMake(centerX + moveX, centerY); 207 | } else { 208 | self.center = CGPointMake(centerX - moveX, centerY); 209 | } 210 | } completion:^(BOOL finished) { 211 | 212 | if (_shakeCount >= maxShakeCount) { 213 | [UIView animateWithDuration:duration delay:0.0 usingSpringWithDamping:0.01 initialSpringVelocity:0.35 options:UIViewAnimationOptionCurveEaseInOut animations:^{ 214 | CGFloat realCenterX = CGRectGetMidX(self.superview.bounds); 215 | self.center = CGPointMake(realCenterX, centerY); 216 | } completion:^(BOOL finished) { 217 | _direction = 0; 218 | _shakeCount = 0; 219 | 220 | SmileTouchID_DispatchMainThread(^(){ 221 | completion(); 222 | }); 223 | }]; 224 | return; 225 | } 226 | 227 | _shakeCount++; 228 | 229 | if (!_direction) { 230 | _direction = 1; 231 | } else { 232 | _direction = 0; 233 | } 234 | 235 | [self shakeAnimationWithCompletion:completion]; 236 | 237 | }]; 238 | } 239 | 240 | -(void)slideToLeftAnimationWithCompletion:(dispatch_block_t)completion{ 241 | 242 | CGFloat centerX = CGRectGetMidX(self.bounds); 243 | CGFloat centerY = CGRectGetMidY(self.bounds); 244 | 245 | [UIView animateWithDuration:0.4 delay:0.0 usingSpringWithDamping:0.9 initialSpringVelocity:5.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{ 246 | if (!_direction) { 247 | self.center = CGPointMake(-centerX, centerY); 248 | } else { 249 | self.center = CGPointMake(centerX, centerY); 250 | } 251 | } completion:^(BOOL finished) { 252 | if (!_direction) { 253 | _direction = 1; 254 | self.center = CGPointMake(3 * centerX, centerY); 255 | [self slideToLeftAnimationWithCompletion:completion]; 256 | } else { 257 | _direction = 0; 258 | 259 | SmileTouchID_DispatchMainThread(^(){ 260 | completion(); 261 | }); 262 | } 263 | }]; 264 | } 265 | 266 | @end 267 | -------------------------------------------------------------------------------- /Example/Pods/SmileTouchID/SmileAuth/Classes/SmileSettingVC.h: -------------------------------------------------------------------------------- 1 | // 2 | // SmileSettingVC.h 3 | // TouchID 4 | // 5 | // Created by ryu-ushin on 5/25/15. 6 | // Copyright (c) 2015 rain. All rights reserved. 7 | // 8 | 9 | #import 10 | @interface SmileSettingVC : UIViewController 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /Example/Pods/SmileTouchID/SmileAuth/Classes/SmileSettingVC.m: -------------------------------------------------------------------------------- 1 | // 2 | // SmileSettingVC.m 3 | // TouchID 4 | // 5 | // Created by ryu-ushin on 5/25/15. 6 | // Copyright (c) 2015 rain. All rights reserved. 7 | // 8 | 9 | #import "SmileSettingVC.h" 10 | #import "SmileAuthenticator.h" 11 | #import "SmilePasswordContainerView.h" 12 | 13 | @interface SmileSettingVC () 14 | 15 | @property (weak, nonatomic) IBOutlet UITextField *passwordField; 16 | @property (weak, nonatomic) IBOutlet UILabel *descLabel; 17 | @property (weak, nonatomic) IBOutlet UIButton *touchIDButton; 18 | @property (weak, nonatomic) IBOutlet SmilePasswordContainerView *passwordView; 19 | @property (weak, nonatomic) IBOutlet UIImageView *bgImageView; 20 | 21 | @end 22 | 23 | @implementation SmileSettingVC{ 24 | BOOL _needTouchID; 25 | BOOL _isAnimating; 26 | NSInteger _inputCount; 27 | NSString *_bufferPassword; 28 | NSString *_newPassword; 29 | NSInteger _passLength; 30 | NSInteger _failCount; 31 | } 32 | 33 | #pragma mark - SmileContainerLayoutDelegate 34 | -(void)smileContainerLayoutSubview{ 35 | self.passwordView.smilePasswordView.dotCount = self.passwordField.text.length; 36 | } 37 | 38 | - (IBAction)dismissSelf:(id)sender { 39 | 40 | [[SmileAuthenticator sharedInstance] authViewControllerDismissed]; 41 | 42 | [self dismissViewControllerAnimated:YES completion:nil]; 43 | } 44 | 45 | - (IBAction)useTouchID:(id)sender { 46 | [self touchIDHandle]; 47 | } 48 | 49 | #pragma mark - TouchID handle 50 | -(void)touchIDHandle{ 51 | switch ([SmileAuthenticator sharedInstance].securityType) { 52 | case INPUT_ONCE: 53 | 54 | [self touchIDForINPUT_ONCE]; 55 | 56 | break; 57 | 58 | case INPUT_THREE: 59 | 60 | [self touchIDForINPUT_THREE]; 61 | 62 | break; 63 | 64 | case INPUT_TOUCHID: 65 | 66 | [self touchIDForINPUT_TOUCHID]; 67 | 68 | break; 69 | 70 | default: 71 | break; 72 | } 73 | } 74 | 75 | -(void)touchIDForINPUT_TOUCHID{ 76 | [SmileAuthenticator sharedInstance].localizedReason = NSLocalizedString(@"SMILE_REASON", nil); 77 | [[SmileAuthenticator sharedInstance] authenticateWithSuccess:^{ 78 | [[SmileAuthenticator sharedInstance] touchID_OR_PasswordAuthSuccess]; 79 | self.passwordView.smilePasswordView.dotCount = [SmileAuthenticator sharedInstance].passcodeDigit; 80 | [self performSelector:@selector(dismissSelf:) withObject:nil afterDelay:0.15]; 81 | } andFailure:^(LAError errorCode) { 82 | [self.passwordField becomeFirstResponder]; 83 | }]; 84 | } 85 | 86 | -(void)touchIDForINPUT_ONCE{ 87 | [SmileAuthenticator sharedInstance].localizedReason = NSLocalizedString(@"SMILE_INPUT_ONCE_TITLE", nil); 88 | [[SmileAuthenticator sharedInstance] authenticateWithSuccess:^{ 89 | [[SmileAuthenticator sharedInstance] touchID_OR_PasswordTurnOff]; 90 | self.passwordView.smilePasswordView.dotCount = [SmileAuthenticator sharedInstance].passcodeDigit; 91 | [self performSelector:@selector(passwordCancleComplete) withObject:nil afterDelay:0.15]; 92 | } andFailure:^(LAError errorCode) { 93 | [self.passwordField becomeFirstResponder]; 94 | }]; 95 | } 96 | 97 | -(void)touchIDForINPUT_THREE{ 98 | [SmileAuthenticator sharedInstance].localizedReason = NSLocalizedString(@"SMILE_INPUT_THREE_TITLE", nil); 99 | [[SmileAuthenticator sharedInstance] authenticateWithSuccess:^{ 100 | self.passwordView.smilePasswordView.dotCount = [SmileAuthenticator sharedInstance].passcodeDigit; 101 | _inputCount ++; 102 | [self performSelector:@selector(enterNewPassword) withObject:nil afterDelay:0.15]; 103 | } andFailure:^(LAError errorCode) { 104 | [self.passwordField becomeFirstResponder]; 105 | }]; 106 | } 107 | 108 | -(void)viewDidAppear:(BOOL)animated{ 109 | [super viewDidAppear:animated]; 110 | 111 | if (_needTouchID) { 112 | [self useTouchID:nil]; 113 | } 114 | } 115 | 116 | - (void)viewDidLoad { 117 | [super viewDidLoad]; 118 | 119 | if ([SmileAuthenticator sharedInstance].navibarTranslucent) { 120 | [self.navigationController.navigationBar setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault]; 121 | [self.navigationController.navigationBar setShadowImage:[UIImage new]]; 122 | [self.navigationController.navigationBar setTranslucent:YES]; 123 | } 124 | 125 | if ([SmileAuthenticator sharedInstance].nightMode) { 126 | [self.navigationController.navigationBar setBarStyle:UIBarStyleBlack]; 127 | [self.passwordField setKeyboardAppearance:UIKeyboardAppearanceDark]; 128 | self.view.backgroundColor = [UIColor blackColor]; 129 | self.descLabel.textColor = [UIColor whiteColor]; 130 | } 131 | 132 | if ([SmileAuthenticator sharedInstance].parallaxMode) { 133 | [self registerEffectForView:self.passwordView depth:15]; 134 | } 135 | 136 | if ([SmileAuthenticator sharedInstance].descriptionTextColor) { 137 | self.descLabel.textColor = [SmileAuthenticator sharedInstance].descriptionTextColor; 138 | } 139 | 140 | if ([SmileAuthenticator sharedInstance].backgroundImage) { 141 | self.bgImageView.image = [SmileAuthenticator sharedInstance].backgroundImage; 142 | } 143 | 144 | self.passwordView.delegate = self; 145 | 146 | //for tint color 147 | if ([SmileAuthenticator sharedInstance].tintColor) { 148 | self.navigationController.navigationBar.tintColor = [SmileAuthenticator sharedInstance].tintColor; 149 | } 150 | 151 | //for touchid image 152 | UIImage *iconImage = [UIImage imageNamed:[SmileAuthenticator sharedInstance].touchIDIconName]; 153 | [self.touchIDButton setImage:iconImage forState:UIControlStateNormal]; 154 | 155 | self.descLabel.text = [NSString stringWithFormat:NSLocalizedString(@"SMILE_INPUT_DESCRIPTION", nil), (long)[SmileAuthenticator sharedInstance].passcodeDigit]; 156 | 157 | switch ([SmileAuthenticator sharedInstance].securityType) { 158 | case INPUT_ONCE: 159 | self.touchIDButton.hidden = NO; 160 | self.navigationItem.title = NSLocalizedString(@"SMILE_INPUT_ONCE_TITLE", nil); 161 | 162 | break; 163 | 164 | case INPUT_TWICE: 165 | 166 | self.navigationItem.title = NSLocalizedString(@"SMILE_INPUT_TWICE_TITLE", nil); 167 | 168 | break; 169 | 170 | case INPUT_THREE: 171 | self.touchIDButton.hidden = NO; 172 | self.navigationItem.title = NSLocalizedString(@"SMILE_INPUT_THREE_TITLE", nil); 173 | self.descLabel.text = [NSString stringWithFormat:NSLocalizedString(@"SMILE_INPUT_THREE_STEP_1_DESCRIPTION", nil), (long)[SmileAuthenticator sharedInstance].passcodeDigit]; 174 | 175 | break; 176 | 177 | case INPUT_TOUCHID: 178 | 179 | self.touchIDButton.hidden = NO; 180 | 181 | if (![SmileAuthenticator sharedInstance].appLogoName.length) { 182 | self.navigationItem.title = NSLocalizedString(@"SMILE_INPUT_TOUCHID_TITLE", nil); 183 | } else { 184 | self.navigationItem.titleView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:[SmileAuthenticator sharedInstance].appLogoName]]; 185 | } 186 | 187 | break; 188 | 189 | default: 190 | break; 191 | } 192 | 193 | //hide bar button 194 | if ([SmileAuthenticator sharedInstance].securityType == INPUT_TOUCHID) { 195 | if (self.navigationItem.rightBarButtonItem) { 196 | [self.navigationItem.rightBarButtonItem setTintColor:[UIColor clearColor]]; 197 | [self.navigationItem.rightBarButtonItem setEnabled:NO]; 198 | } 199 | 200 | //begin check canAuthenticate 201 | NSError *error = nil; 202 | if ([SmileAuthenticator canAuthenticateWithError:&error]) { 203 | _needTouchID = YES; 204 | } else { 205 | self.touchIDButton.hidden = YES; 206 | [self.passwordField becomeFirstResponder]; 207 | } 208 | 209 | } else if ([SmileAuthenticator sharedInstance].securityType == INPUT_ONCE | [SmileAuthenticator sharedInstance].securityType == INPUT_THREE) { 210 | 211 | //begin check canAuthenticate 212 | NSError *error = nil; 213 | if ([SmileAuthenticator canAuthenticateWithError:&error]) { 214 | _needTouchID = YES; 215 | } else { 216 | self.touchIDButton.hidden = YES; 217 | [self.passwordField becomeFirstResponder]; 218 | } 219 | } 220 | 221 | else { 222 | [self.passwordField becomeFirstResponder]; 223 | } 224 | 225 | self.passwordField.delegate = self; 226 | [self.passwordField addTarget:self action:@selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged]; 227 | 228 | _passLength =[SmileAuthenticator sharedInstance].passcodeDigit; 229 | } 230 | 231 | - (void)didReceiveMemoryWarning { 232 | [super didReceiveMemoryWarning]; 233 | // Dispose of any resources that can be recreated. 234 | } 235 | 236 | #pragma mark - animation 237 | 238 | -(void)slideAnimation{ 239 | _isAnimating = YES; 240 | 241 | if (!self.touchIDButton.hidden) { 242 | self.touchIDButton.hidden = YES; 243 | } 244 | 245 | [self.passwordView.smilePasswordView slideToLeftAnimationWithCompletion:^{ 246 | _isAnimating = NO; 247 | if(![self.passwordField isFirstResponder]){ 248 | [self.passwordField becomeFirstResponder]; 249 | }; 250 | }]; 251 | 252 | } 253 | 254 | -(void)shakeAnimation{ 255 | _isAnimating = YES; 256 | [self.passwordView.smilePasswordView shakeAnimationWithCompletion:^{ 257 | _isAnimating = NO; 258 | }]; 259 | } 260 | 261 | #pragma mark - handle user input 262 | -(void)clearText { 263 | self.passwordField.text = @""; 264 | self.passwordView.smilePasswordView.dotCount = 0; 265 | } 266 | 267 | -(void)passwordInputComplete{ 268 | [[SmileAuthenticator sharedInstance] userSetPassword: _newPassword]; 269 | [self dismissSelf:nil]; 270 | } 271 | 272 | -(void)passwordCancleComplete{ 273 | [SmileAuthenticator clearPassword]; 274 | [self dismissSelf:nil]; 275 | } 276 | 277 | -(void)passwordWrong{ 278 | _inputCount = 0; 279 | 280 | [self clearText]; 281 | 282 | _failCount++; 283 | 284 | [self shakeAnimation]; 285 | 286 | [[SmileAuthenticator sharedInstance] touchID_OR_PasswordAuthFail:_failCount]; 287 | 288 | self.descLabel.text = [NSString stringWithFormat:NSLocalizedString(@"SMILE_INPUT_FAILED", nil), (long)_failCount]; 289 | } 290 | 291 | -(void)passwordNotMatch{ 292 | 293 | _inputCount = _inputCount -2; 294 | 295 | [self clearText]; 296 | [self shakeAnimation]; 297 | 298 | self.descLabel.text = NSLocalizedString(@"SMILE_INPUT_NOT_MATCH", nil); 299 | } 300 | 301 | -(void)reEnterPassword{ 302 | 303 | _bufferPassword = _newPassword; 304 | [self clearText]; 305 | 306 | [self slideAnimation]; 307 | 308 | self.descLabel.text = [NSString stringWithFormat:NSLocalizedString(@"SMILE_INPUT_RE-ENTER", nil), (long)[SmileAuthenticator sharedInstance].passcodeDigit]; 309 | } 310 | 311 | -(void)enterNewPassword{ 312 | [self clearText]; 313 | [self slideAnimation]; 314 | self.descLabel.text = [NSString stringWithFormat:NSLocalizedString(@"SMILE_INPUT_THREE_STEP_2_DESCRIPTION", nil), (long)[SmileAuthenticator sharedInstance].passcodeDigit]; 315 | } 316 | 317 | -(void)handleINPUT_TOUCHID{ 318 | if ([SmileAuthenticator isSamePassword:_newPassword]) { 319 | [[SmileAuthenticator sharedInstance] touchID_OR_PasswordAuthSuccess]; 320 | [self passwordInputComplete]; 321 | } else { 322 | [self passwordWrong]; 323 | } 324 | } 325 | 326 | -(void)handleINPUT_ONCE{ 327 | if ([SmileAuthenticator isSamePassword:_newPassword]) { 328 | [[SmileAuthenticator sharedInstance] touchID_OR_PasswordTurnOff]; 329 | [self passwordCancleComplete]; 330 | } else { 331 | [self passwordWrong]; 332 | } 333 | } 334 | 335 | -(void)handleINPUT_TWICE{ 336 | if (_inputCount == 1) { 337 | [self reEnterPassword]; 338 | } else if (_inputCount == 2) { 339 | if ([_bufferPassword isEqualToString:_newPassword]) { 340 | [[SmileAuthenticator sharedInstance] touchID_OR_PasswordTurnOn]; 341 | [self passwordInputComplete]; 342 | } else { 343 | [self passwordNotMatch]; 344 | } 345 | } 346 | } 347 | 348 | -(void)handleINPUT_THREE{ 349 | if (_inputCount == 1) { 350 | if ([SmileAuthenticator isSamePassword:_newPassword]) { 351 | [self enterNewPassword]; 352 | } else { 353 | [self passwordWrong]; 354 | } 355 | } else if (_inputCount == 2) { 356 | [self reEnterPassword]; 357 | } else if (_inputCount == 3) { 358 | if ([_bufferPassword isEqualToString:_newPassword]) { 359 | [[SmileAuthenticator sharedInstance] touchID_OR_PasswordChange]; 360 | [self passwordInputComplete]; 361 | } else { 362 | [self passwordNotMatch]; 363 | } 364 | } 365 | } 366 | 367 | -(void)handleUserInput{ 368 | switch ([SmileAuthenticator sharedInstance].securityType) { 369 | case INPUT_ONCE: 370 | 371 | [self handleINPUT_ONCE]; 372 | 373 | break; 374 | 375 | case INPUT_TWICE: 376 | 377 | _inputCount++; 378 | 379 | [self handleINPUT_TWICE]; 380 | 381 | break; 382 | 383 | case INPUT_THREE: 384 | 385 | _inputCount++; 386 | 387 | [self handleINPUT_THREE]; 388 | 389 | break; 390 | 391 | case INPUT_TOUCHID: 392 | 393 | [self handleINPUT_TOUCHID]; 394 | 395 | break; 396 | 397 | default: 398 | break; 399 | } 400 | 401 | } 402 | 403 | #pragma mark - UITextFieldDelegate 404 | 405 | -(void)textFieldDidChange:(UITextField*)textField{ 406 | 407 | self.passwordView.smilePasswordView.dotCount = textField.text.length; 408 | 409 | if (textField.text.length == _passLength) { 410 | 411 | _newPassword = textField.text; 412 | 413 | [self performSelector:@selector(handleUserInput) withObject:nil afterDelay:0.3]; 414 | } 415 | } 416 | 417 | -(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{ 418 | 419 | if (textField.text.length >= _passLength) { 420 | return NO; 421 | } 422 | 423 | return !_isAnimating; 424 | } 425 | 426 | 427 | #pragma mark - PrivateMethod - Parallax 428 | 429 | - (void)registerEffectForView:(UIView *)aView depth:(CGFloat)depth; 430 | { 431 | UIInterpolatingMotionEffect *effectX; 432 | UIInterpolatingMotionEffect *effectY; 433 | effectX = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.x" 434 | type:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis]; 435 | effectY = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.y" 436 | type:UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis]; 437 | 438 | 439 | effectX.maximumRelativeValue = @(depth); 440 | effectX.minimumRelativeValue = @(-depth); 441 | effectY.maximumRelativeValue = @(depth); 442 | effectY.minimumRelativeValue = @(-depth); 443 | 444 | UIMotionEffectGroup *group = [[UIMotionEffectGroup alloc] init]; 445 | group.motionEffects =@[effectX, effectY]; 446 | 447 | [aView addMotionEffect:group] ; 448 | } 449 | 450 | 451 | /* 452 | #pragma mark - Navigation 453 | 454 | // In a storyboard-based application, you will often want to do a little preparation before navigation 455 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { 456 | // Get the new view controller using [segue destinationViewController]. 457 | // Pass the selected object to the new view controller. 458 | } 459 | */ 460 | 461 | @end 462 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-Smile TouchID-SmileTouchID/Pods-Smile TouchID-SmileTouchID-Private.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods-Smile TouchID-SmileTouchID.xcconfig" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/SmileTouchID" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/SmileTouchID" 4 | OTHER_LDFLAGS = ${PODS_SMILE_TOUCHID_SMILETOUCHID_OTHER_LDFLAGS} -ObjC 5 | PODS_ROOT = ${SRCROOT} 6 | SKIP_INSTALL = YES -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-Smile TouchID-SmileTouchID/Pods-Smile TouchID-SmileTouchID-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_Smile_TouchID_SmileTouchID : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_Smile_TouchID_SmileTouchID 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-Smile TouchID-SmileTouchID/Pods-Smile TouchID-SmileTouchID-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #endif 4 | 5 | #import "Pods-Smile TouchID-environment.h" 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-Smile TouchID-SmileTouchID/Pods-Smile TouchID-SmileTouchID.xcconfig: -------------------------------------------------------------------------------- 1 | PODS_SMILE_TOUCHID_SMILETOUCHID_OTHER_LDFLAGS = -framework "UIKit" -weak_framework "LocalAuthentication" -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-Smile TouchID/Pods-Smile TouchID-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## SmileTouchID 5 | 6 | The MIT License (MIT) 7 | 8 | Copyright (c) 2015 LIU YUCHEN 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | 28 | Generated by CocoaPods - http://cocoapods.org 29 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-Smile TouchID/Pods-Smile TouchID-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | The MIT License (MIT) 18 | 19 | Copyright (c) 2015 LIU YUCHEN 20 | 21 | Permission is hereby granted, free of charge, to any person obtaining a copy 22 | of this software and associated documentation files (the "Software"), to deal 23 | in the Software without restriction, including without limitation the rights 24 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 25 | copies of the Software, and to permit persons to whom the Software is 26 | furnished to do so, subject to the following conditions: 27 | 28 | The above copyright notice and this permission notice shall be included in all 29 | copies or substantial portions of the Software. 30 | 31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 32 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 33 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 34 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 35 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 36 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 37 | SOFTWARE. 38 | 39 | Title 40 | SmileTouchID 41 | Type 42 | PSGroupSpecifier 43 | 44 | 45 | FooterText 46 | Generated by CocoaPods - http://cocoapods.org 47 | Title 48 | 49 | Type 50 | PSGroupSpecifier 51 | 52 | 53 | StringsTable 54 | Acknowledgements 55 | Title 56 | Acknowledgements 57 | 58 | 59 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-Smile TouchID/Pods-Smile TouchID-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_Smile_TouchID : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_Smile_TouchID 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-Smile TouchID/Pods-Smile TouchID-environment.h: -------------------------------------------------------------------------------- 1 | 2 | // To check if a library is compiled with CocoaPods you 3 | // can use the `COCOAPODS` macro definition which is 4 | // defined in the xcconfigs so it is available in 5 | // headers also when they are imported in the client 6 | // project. 7 | 8 | 9 | // SmileTouchID 10 | #define COCOAPODS_POD_AVAILABLE_SmileTouchID 11 | #define COCOAPODS_VERSION_MAJOR_SmileTouchID 0 12 | #define COCOAPODS_VERSION_MINOR_SmileTouchID 1 13 | #define COCOAPODS_VERSION_PATCH_SmileTouchID 0 14 | 15 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-Smile TouchID/Pods-Smile TouchID-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 5 | 6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 7 | > "$RESOURCES_TO_COPY" 8 | 9 | XCASSET_FILES=() 10 | 11 | realpath() { 12 | DIRECTORY=$(cd "${1%/*}" && pwd) 13 | FILENAME="${1##*/}" 14 | echo "$DIRECTORY/$FILENAME" 15 | } 16 | 17 | install_resource() 18 | { 19 | case $1 in 20 | *.storyboard) 21 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile ${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .storyboard`.storyboardc ${PODS_ROOT}/$1 --sdk ${SDKROOT}" 22 | ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .storyboard`.storyboardc" "${PODS_ROOT}/$1" --sdk "${SDKROOT}" 23 | ;; 24 | *.xib) 25 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile ${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .xib`.nib ${PODS_ROOT}/$1 --sdk ${SDKROOT}" 26 | ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .xib`.nib" "${PODS_ROOT}/$1" --sdk "${SDKROOT}" 27 | ;; 28 | *.framework) 29 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 30 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 31 | echo "rsync -av ${PODS_ROOT}/$1 ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 32 | rsync -av "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 33 | ;; 34 | *.xcdatamodel) 35 | echo "xcrun momc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1"`.mom\"" 36 | xcrun momc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodel`.mom" 37 | ;; 38 | *.xcdatamodeld) 39 | echo "xcrun momc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodeld`.momd\"" 40 | xcrun momc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodeld`.momd" 41 | ;; 42 | *.xcmappingmodel) 43 | echo "xcrun mapc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcmappingmodel`.cdm\"" 44 | xcrun mapc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcmappingmodel`.cdm" 45 | ;; 46 | *.xcassets) 47 | ABSOLUTE_XCASSET_FILE=$(realpath "${PODS_ROOT}/$1") 48 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 49 | ;; 50 | /*) 51 | echo "$1" 52 | echo "$1" >> "$RESOURCES_TO_COPY" 53 | ;; 54 | *) 55 | echo "${PODS_ROOT}/$1" 56 | echo "${PODS_ROOT}/$1" >> "$RESOURCES_TO_COPY" 57 | ;; 58 | esac 59 | } 60 | if [[ "$CONFIGURATION" == "Debug" ]]; then 61 | install_resource "../../SmileAuth/Assets/smile_Touch_ID@2x.png" 62 | install_resource "../../SmileAuth/Assets/smile_Touch_ID@3x.png" 63 | install_resource "../../SmileAuth/Assets/SmileSettingVC.storyboard" 64 | fi 65 | if [[ "$CONFIGURATION" == "Release" ]]; then 66 | install_resource "../../SmileAuth/Assets/smile_Touch_ID@2x.png" 67 | install_resource "../../SmileAuth/Assets/smile_Touch_ID@3x.png" 68 | install_resource "../../SmileAuth/Assets/SmileSettingVC.storyboard" 69 | fi 70 | 71 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 72 | if [[ "${ACTION}" == "install" ]]; then 73 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 74 | fi 75 | rm -f "$RESOURCES_TO_COPY" 76 | 77 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] 78 | then 79 | case "${TARGETED_DEVICE_FAMILY}" in 80 | 1,2) 81 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 82 | ;; 83 | 1) 84 | TARGET_DEVICE_ARGS="--target-device iphone" 85 | ;; 86 | 2) 87 | TARGET_DEVICE_ARGS="--target-device ipad" 88 | ;; 89 | *) 90 | TARGET_DEVICE_ARGS="--target-device mac" 91 | ;; 92 | esac 93 | 94 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 95 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 96 | while read line; do 97 | if [[ $line != "`realpath $PODS_ROOT`*" ]]; then 98 | XCASSET_FILES+=("$line") 99 | fi 100 | done <<<"$OTHER_XCASSETS" 101 | 102 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${IPHONEOS_DEPLOYMENT_TARGET}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 103 | fi 104 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-Smile TouchID/Pods-Smile TouchID.debug.xcconfig: -------------------------------------------------------------------------------- 1 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 2 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/SmileTouchID" 3 | OTHER_CFLAGS = $(inherited) -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/SmileTouchID" 4 | OTHER_LDFLAGS = $(inherited) -ObjC -l"Pods-Smile TouchID-SmileTouchID" -framework "UIKit" -weak_framework "LocalAuthentication" 5 | OTHER_LIBTOOLFLAGS = $(OTHER_LDFLAGS) 6 | PODS_ROOT = ${SRCROOT}/Pods -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-Smile TouchID/Pods-Smile TouchID.release.xcconfig: -------------------------------------------------------------------------------- 1 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 2 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/SmileTouchID" 3 | OTHER_CFLAGS = $(inherited) -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/SmileTouchID" 4 | OTHER_LDFLAGS = $(inherited) -ObjC -l"Pods-Smile TouchID-SmileTouchID" -framework "UIKit" -weak_framework "LocalAuthentication" 5 | OTHER_LIBTOOLFLAGS = $(OTHER_LDFLAGS) 6 | PODS_ROOT = ${SRCROOT}/Pods -------------------------------------------------------------------------------- /Example/TouchID.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/TouchID.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Example/TouchID/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // TouchID 4 | // 5 | // Created by ryu-ushin on 5/25/15. 6 | // Copyright (c) 2015 rain. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | 16 | @end 17 | 18 | -------------------------------------------------------------------------------- /Example/TouchID/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // TouchID 4 | // 5 | // Created by ryu-ushin on 5/25/15. 6 | // Copyright (c) 2015 rain. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import "SmileAuthenticator.h" 11 | 12 | @interface AppDelegate () 13 | 14 | @end 15 | 16 | @implementation AppDelegate{ 17 | UIImageView *_coverImageView; 18 | } 19 | 20 | #define kBG_Image @"backgroundImage" 21 | 22 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 23 | 24 | [self configureForFirstLaunchAddCoverImage]; 25 | 26 | [SmileAuthenticator sharedInstance].rootVC = self.window.rootViewController; 27 | 28 | //customize 29 | [SmileAuthenticator sharedInstance].passcodeDigit = 4; 30 | [SmileAuthenticator sharedInstance].tintColor = [UIColor purpleColor]; 31 | [SmileAuthenticator sharedInstance].touchIDIconName = @"my_Touch_ID"; 32 | [SmileAuthenticator sharedInstance].appLogoName = @"my_Logo"; 33 | [SmileAuthenticator sharedInstance].navibarTranslucent = YES; 34 | [SmileAuthenticator sharedInstance].backgroundImage = [UIImage imageNamed:kBG_Image]; 35 | //[SmileAuthenticator sharedInstance].timeoutInterval = 10; 36 | 37 | return YES; 38 | } 39 | 40 | -(void)configureForFirstLaunchAddCoverImage{ 41 | //add observer UIWindowDidBecomeKeyNotification for the first launch add the cover image for protecting the user's data. 42 | [[NSNotificationCenter defaultCenter] addObserver:self 43 | selector:@selector(windowDidBecomeVisible:) 44 | name:UIWindowDidBecomeKeyNotification 45 | object:nil]; 46 | //add observer SmileTouchID_Presented_AuthVC_Notification for only remove cover image when the the AuthVC has been presented. 47 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(removeCoverImageView) name:SmileTouchID_Presented_AuthVC_Notification object:nil]; 48 | } 49 | 50 | -(void)windowDidBecomeVisible:(NSNotification*)notif{ 51 | if ([SmileAuthenticator hasPassword]) { 52 | //iOS automatically snapshot screen, so if has password, use the _coverImageView cover the UIWindow for protecting user data. 53 | [self addCoverImageView]; 54 | //remove the observer, because we just need it for the app first launch. 55 | [[NSNotificationCenter defaultCenter] removeObserver:self name:UIWindowDidBecomeKeyNotification object:nil]; 56 | } 57 | } 58 | 59 | -(void)addCoverImageView{ 60 | _coverImageView = [[UIImageView alloc]initWithFrame:[self.window bounds]]; 61 | _coverImageView.contentMode = UIViewContentModeScaleAspectFill; 62 | UIImage *image = [UIImage imageNamed:kBG_Image]; 63 | [_coverImageView setImage:image]; 64 | _coverImageView.alpha = 1.0; 65 | [self.window addSubview:_coverImageView]; 66 | } 67 | 68 | -(void)showCoverImageView{ 69 | if (!_coverImageView) { 70 | [self addCoverImageView]; 71 | } 72 | [UIView animateWithDuration:0.1 animations:^{ 73 | _coverImageView.alpha = 1.0; 74 | }]; 75 | } 76 | 77 | -(void)removeCoverImageView{ 78 | if (_coverImageView) { 79 | [UIView animateWithDuration:0.1 animations:^{ 80 | _coverImageView.alpha = 0.0; 81 | } completion:^(BOOL finished) { 82 | [_coverImageView removeFromSuperview]; 83 | _coverImageView = nil; 84 | }]; 85 | } 86 | } 87 | 88 | - (void)applicationWillResignActive:(UIApplication *)application { 89 | // 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. 90 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 91 | if ([SmileAuthenticator hasPassword] && [SmileAuthenticator sharedInstance].isShowingAuthVC == NO) { 92 | [self showCoverImageView]; 93 | } 94 | } 95 | 96 | - (void)applicationDidBecomeActive:(UIApplication *)application { 97 | // 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. 98 | if ([SmileAuthenticator hasPassword]) { 99 | //if now is authenticated, remove the cover image. 100 | if([SmileAuthenticator sharedInstance].isAuthenticated){ 101 | [self removeCoverImageView]; 102 | } 103 | } 104 | } 105 | 106 | - (void)applicationDidEnterBackground:(UIApplication *)application { 107 | // 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. 108 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 109 | 110 | } 111 | 112 | - (void)applicationWillEnterForeground:(UIApplication *)application { 113 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 114 | } 115 | 116 | - (void)applicationWillTerminate:(UIApplication *)application { 117 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 118 | } 119 | 120 | -(void)SMILE_testHelperMethod{ 121 | BOOL isCustomize = YES; 122 | 123 | if (isCustomize) { 124 | 125 | BOOL nightMode = NO; 126 | 127 | if (!nightMode) { 128 | //customize 129 | [SmileAuthenticator sharedInstance].passcodeDigit = 6; 130 | [SmileAuthenticator sharedInstance].tintColor = [UIColor purpleColor]; 131 | [SmileAuthenticator sharedInstance].touchIDIconName = @"my_Touch_ID"; 132 | [SmileAuthenticator sharedInstance].appLogoName = @"my_Logo"; 133 | [SmileAuthenticator sharedInstance].navibarTranslucent = YES; 134 | [SmileAuthenticator sharedInstance].backgroundImage = [UIImage imageNamed:@"backgroundImage"]; 135 | } else { 136 | [SmileAuthenticator sharedInstance].passcodeDigit = 6; 137 | [SmileAuthenticator sharedInstance].tintColor = [UIColor purpleColor]; 138 | [SmileAuthenticator sharedInstance].touchIDIconName = @"my_Touch_ID"; 139 | [SmileAuthenticator sharedInstance].appLogoName = @"my_Logo"; 140 | [SmileAuthenticator sharedInstance].navibarTranslucent = NO; 141 | [SmileAuthenticator sharedInstance].nightMode = YES; 142 | [SmileAuthenticator sharedInstance].backgroundImage = [UIImage imageNamed:@"nightMode_BG"]; 143 | } 144 | } 145 | } 146 | 147 | @end 148 | -------------------------------------------------------------------------------- /Example/TouchID/Base.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Localizable.strings 3 | TouchID 4 | 5 | Created by ryu-ushin on 6/11/15. 6 | Copyright (c) 2015 rain. All rights reserved. 7 | */ 8 | 9 | /* 10 | UNIVERSAL PARTS 11 | */ 12 | "SMILE_REASON" = "Are you device owner?"; 13 | "SMILE_INPUT_FAILED" = "%ld Failed Passcode Attempt. Try again."; 14 | "SMILE_INPUT_DESCRIPTION" = "Enter %ld digit passcode"; 15 | "SMILE_INPUT_NOT_MATCH" = "Passcode not match. Try again."; 16 | "SMILE_INPUT_RE-ENTER" = "Re-enter your %ld digit Passcode"; 17 | 18 | /* 19 | INPUT_TOUCHID 20 | */ 21 | "SMILE_INPUT_TOUCHID_TITLE" = "Enter Passcode"; 22 | 23 | 24 | /* 25 | INPUT_ONCE 26 | */ 27 | "SMILE_INPUT_ONCE_TITLE" = "Turn off Passcode"; 28 | 29 | 30 | /* 31 | INPUT_TWICE 32 | */ 33 | "SMILE_INPUT_TWICE_TITLE" = "Set Passcode"; 34 | 35 | 36 | /* 37 | INPUT_THREE 38 | */ 39 | "SMILE_INPUT_THREE_TITLE" = "Change Passcode"; 40 | "SMILE_INPUT_THREE_STEP_1_DESCRIPTION" = "Enter your old %ld digit Passcode"; 41 | "SMILE_INPUT_THREE_STEP_2_DESCRIPTION" = "Enter your new %ld digit Passcode"; -------------------------------------------------------------------------------- /Example/TouchID/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 38 | 47 | 48 | 49 | 50 | 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 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /Example/TouchID/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "size" : "60x60", 25 | "idiom" : "iphone", 26 | "filename" : "icon_60@2x.png", 27 | "scale" : "2x" 28 | }, 29 | { 30 | "size" : "60x60", 31 | "idiom" : "iphone", 32 | "filename" : "icon_60@3x.png", 33 | "scale" : "3x" 34 | }, 35 | { 36 | "idiom" : "ipad", 37 | "size" : "29x29", 38 | "scale" : "1x" 39 | }, 40 | { 41 | "idiom" : "ipad", 42 | "size" : "29x29", 43 | "scale" : "2x" 44 | }, 45 | { 46 | "idiom" : "ipad", 47 | "size" : "40x40", 48 | "scale" : "1x" 49 | }, 50 | { 51 | "idiom" : "ipad", 52 | "size" : "40x40", 53 | "scale" : "2x" 54 | }, 55 | { 56 | "size" : "76x76", 57 | "idiom" : "ipad", 58 | "filename" : "icon_76.png", 59 | "scale" : "1x" 60 | }, 61 | { 62 | "size" : "76x76", 63 | "idiom" : "ipad", 64 | "filename" : "icon_76@2x.png", 65 | "scale" : "2x" 66 | }, 67 | { 68 | "idiom" : "ipad", 69 | "size" : "83.5x83.5", 70 | "scale" : "2x" 71 | } 72 | ], 73 | "info" : { 74 | "version" : 1, 75 | "author" : "xcode" 76 | } 77 | } -------------------------------------------------------------------------------- /Example/TouchID/Images.xcassets/AppIcon.appiconset/icon_60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liu044100/SmileTouchID/5b6584ffd4b8f1062a380a7f19f290525351a7e4/Example/TouchID/Images.xcassets/AppIcon.appiconset/icon_60@2x.png -------------------------------------------------------------------------------- /Example/TouchID/Images.xcassets/AppIcon.appiconset/icon_60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liu044100/SmileTouchID/5b6584ffd4b8f1062a380a7f19f290525351a7e4/Example/TouchID/Images.xcassets/AppIcon.appiconset/icon_60@3x.png -------------------------------------------------------------------------------- /Example/TouchID/Images.xcassets/AppIcon.appiconset/icon_76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liu044100/SmileTouchID/5b6584ffd4b8f1062a380a7f19f290525351a7e4/Example/TouchID/Images.xcassets/AppIcon.appiconset/icon_76.png -------------------------------------------------------------------------------- /Example/TouchID/Images.xcassets/AppIcon.appiconset/icon_76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liu044100/SmileTouchID/5b6584ffd4b8f1062a380a7f19f290525351a7e4/Example/TouchID/Images.xcassets/AppIcon.appiconset/icon_76@2x.png -------------------------------------------------------------------------------- /Example/TouchID/Images.xcassets/LaunchImage.launchimage/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "extent" : "full-screen", 5 | "idiom" : "iphone", 6 | "subtype" : "736h", 7 | "filename" : "iPhone 6 Plus.png", 8 | "minimum-system-version" : "8.0", 9 | "orientation" : "portrait", 10 | "scale" : "3x" 11 | }, 12 | { 13 | "extent" : "full-screen", 14 | "idiom" : "iphone", 15 | "subtype" : "667h", 16 | "filename" : "iPhone 6.png", 17 | "minimum-system-version" : "8.0", 18 | "orientation" : "portrait", 19 | "scale" : "2x" 20 | }, 21 | { 22 | "orientation" : "portrait", 23 | "idiom" : "iphone", 24 | "extent" : "full-screen", 25 | "minimum-system-version" : "7.0", 26 | "filename" : "iPhone 4.png", 27 | "scale" : "2x" 28 | }, 29 | { 30 | "extent" : "full-screen", 31 | "idiom" : "iphone", 32 | "subtype" : "retina4", 33 | "filename" : "iPhone 5.png", 34 | "minimum-system-version" : "7.0", 35 | "orientation" : "portrait", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "orientation" : "portrait", 40 | "idiom" : "ipad", 41 | "extent" : "full-screen", 42 | "minimum-system-version" : "7.0", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "orientation" : "landscape", 47 | "idiom" : "ipad", 48 | "extent" : "full-screen", 49 | "minimum-system-version" : "7.0", 50 | "scale" : "1x" 51 | }, 52 | { 53 | "orientation" : "portrait", 54 | "idiom" : "ipad", 55 | "extent" : "full-screen", 56 | "minimum-system-version" : "7.0", 57 | "scale" : "2x" 58 | }, 59 | { 60 | "orientation" : "landscape", 61 | "idiom" : "ipad", 62 | "extent" : "full-screen", 63 | "minimum-system-version" : "7.0", 64 | "scale" : "2x" 65 | } 66 | ], 67 | "info" : { 68 | "version" : 1, 69 | "author" : "xcode" 70 | } 71 | } -------------------------------------------------------------------------------- /Example/TouchID/Images.xcassets/LaunchImage.launchimage/iPhone 4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liu044100/SmileTouchID/5b6584ffd4b8f1062a380a7f19f290525351a7e4/Example/TouchID/Images.xcassets/LaunchImage.launchimage/iPhone 4.png -------------------------------------------------------------------------------- /Example/TouchID/Images.xcassets/LaunchImage.launchimage/iPhone 5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liu044100/SmileTouchID/5b6584ffd4b8f1062a380a7f19f290525351a7e4/Example/TouchID/Images.xcassets/LaunchImage.launchimage/iPhone 5.png -------------------------------------------------------------------------------- /Example/TouchID/Images.xcassets/LaunchImage.launchimage/iPhone 6 Plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liu044100/SmileTouchID/5b6584ffd4b8f1062a380a7f19f290525351a7e4/Example/TouchID/Images.xcassets/LaunchImage.launchimage/iPhone 6 Plus.png -------------------------------------------------------------------------------- /Example/TouchID/Images.xcassets/LaunchImage.launchimage/iPhone 6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liu044100/SmileTouchID/5b6584ffd4b8f1062a380a7f19f290525351a7e4/Example/TouchID/Images.xcassets/LaunchImage.launchimage/iPhone 6.png -------------------------------------------------------------------------------- /Example/TouchID/Images.xcassets/backgroundImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "backgroundImage@2x.png" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/TouchID/Images.xcassets/backgroundImage.imageset/backgroundImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liu044100/SmileTouchID/5b6584ffd4b8f1062a380a7f19f290525351a7e4/Example/TouchID/Images.xcassets/backgroundImage.imageset/backgroundImage@2x.png -------------------------------------------------------------------------------- /Example/TouchID/Images.xcassets/my_Logo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "my_Logo@2x.png" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x", 15 | "filename" : "my_Logo@3x.png" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Example/TouchID/Images.xcassets/my_Logo.imageset/my_Logo@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liu044100/SmileTouchID/5b6584ffd4b8f1062a380a7f19f290525351a7e4/Example/TouchID/Images.xcassets/my_Logo.imageset/my_Logo@2x.png -------------------------------------------------------------------------------- /Example/TouchID/Images.xcassets/my_Logo.imageset/my_Logo@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liu044100/SmileTouchID/5b6584ffd4b8f1062a380a7f19f290525351a7e4/Example/TouchID/Images.xcassets/my_Logo.imageset/my_Logo@3x.png -------------------------------------------------------------------------------- /Example/TouchID/Images.xcassets/my_Touch_ID.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "my_Touch_ID@2x.png" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x", 15 | "filename" : "my_Touch_ID@3x.png" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Example/TouchID/Images.xcassets/my_Touch_ID.imageset/my_Touch_ID@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liu044100/SmileTouchID/5b6584ffd4b8f1062a380a7f19f290525351a7e4/Example/TouchID/Images.xcassets/my_Touch_ID.imageset/my_Touch_ID@2x.png -------------------------------------------------------------------------------- /Example/TouchID/Images.xcassets/my_Touch_ID.imageset/my_Touch_ID@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liu044100/SmileTouchID/5b6584ffd4b8f1062a380a7f19f290525351a7e4/Example/TouchID/Images.xcassets/my_Touch_ID.imageset/my_Touch_ID@3x.png -------------------------------------------------------------------------------- /Example/TouchID/Images.xcassets/nightMode_BG.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "scale" : "2x", 10 | "filename" : "nightMode_BG@2x.png" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /Example/TouchID/Images.xcassets/nightMode_BG.imageset/nightMode_BG@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liu044100/SmileTouchID/5b6584ffd4b8f1062a380a7f19f290525351a7e4/Example/TouchID/Images.xcassets/nightMode_BG.imageset/nightMode_BG@2x.png -------------------------------------------------------------------------------- /Example/TouchID/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 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | Main 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | UISupportedInterfaceOrientations~ipad 40 | 41 | UIInterfaceOrientationPortrait 42 | UIInterfaceOrientationPortraitUpsideDown 43 | UIInterfaceOrientationLandscapeLeft 44 | UIInterfaceOrientationLandscapeRight 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /Example/TouchID/SmileMainVC.h: -------------------------------------------------------------------------------- 1 | // 2 | // MainVC.h 3 | // TouchID 4 | // 5 | // Created by ryu-ushin on 5/25/15. 6 | // Copyright (c) 2015 rain. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SmileMainVC : UIViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Example/TouchID/SmileMainVC.m: -------------------------------------------------------------------------------- 1 | // 2 | // MainVC.m 3 | // TouchID 4 | // 5 | // Created by ryu-ushin on 5/25/15. 6 | // Copyright (c) 2015 rain. All rights reserved. 7 | // 8 | 9 | #import "SmileMainVC.h" 10 | #import "SmileAuthenticator.h" 11 | 12 | @interface SmileMainVC () 13 | @property (weak, nonatomic) IBOutlet UISwitch *mySwitch; 14 | @property (weak, nonatomic) IBOutlet UIButton *changePasswordButton; 15 | 16 | @end 17 | 18 | @implementation SmileMainVC 19 | 20 | - (void)viewDidLoad { 21 | [super viewDidLoad]; 22 | // Do any additional setup after loading the view. 23 | 24 | [SmileAuthenticator sharedInstance].delegate = self; 25 | } 26 | 27 | -(void)viewWillAppear:(BOOL)animated{ 28 | [super viewWillAppear:animated]; 29 | 30 | if ([SmileAuthenticator hasPassword]) { 31 | self.mySwitch.on = YES; 32 | self.changePasswordButton.hidden = NO; 33 | } else { 34 | self.mySwitch.on = NO; 35 | self.changePasswordButton.hidden = YES; 36 | } 37 | } 38 | 39 | -(void)viewDidAppear:(BOOL)animated{ 40 | [super viewDidAppear:animated]; 41 | 42 | if ([SmileAuthenticator hasPassword]) { 43 | [SmileAuthenticator sharedInstance].securityType = INPUT_TOUCHID; 44 | [[SmileAuthenticator sharedInstance] presentAuthViewControllerAnimated:NO]; 45 | } 46 | } 47 | 48 | - (IBAction)changePassword:(id)sender { 49 | [SmileAuthenticator sharedInstance].securityType = INPUT_THREE; 50 | [[SmileAuthenticator sharedInstance] presentAuthViewControllerAnimated:TRUE]; 51 | } 52 | 53 | - (IBAction)passwordSwitch:(UISwitch*)passwordSwitch { 54 | if (passwordSwitch.on) { 55 | [SmileAuthenticator sharedInstance].securityType = INPUT_TWICE; 56 | } else { 57 | [SmileAuthenticator sharedInstance].securityType = INPUT_ONCE; 58 | } 59 | 60 | [[SmileAuthenticator sharedInstance] presentAuthViewControllerAnimated:TRUE]; 61 | } 62 | 63 | #pragma mark - AuthenticatorDelegate 64 | 65 | -(void)userFailAuthenticationWithCount:(NSInteger)failCount{ 66 | NSLog(@"userFailAuthenticationWithCount: %ld", (long)failCount); 67 | } 68 | 69 | -(void)userSuccessAuthentication{ 70 | NSLog(@"userSuccessAuthentication"); 71 | } 72 | 73 | -(void)userTurnPasswordOn{ 74 | NSLog(@"userTurnPasswordOn"); 75 | } 76 | 77 | -(void)userTurnPasswordOff{ 78 | NSLog(@"userTurnPasswordOff"); 79 | } 80 | 81 | -(void)userChangePassword{ 82 | NSLog(@"userChangePassword"); 83 | } 84 | 85 | -(void)AuthViewControllerPresented{ 86 | NSLog(@"presentAuthViewController"); 87 | } 88 | 89 | -(void)AuthViewControllerDismissed:(UIViewController*)previousPresentedVC{ 90 | NSLog(@"dismissAuthViewController, previousPresentedVC: %@", previousPresentedVC); 91 | if (previousPresentedVC) { 92 | [self presentViewController:previousPresentedVC animated:YES completion:nil]; 93 | } 94 | } 95 | 96 | @end 97 | -------------------------------------------------------------------------------- /Example/TouchID/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // TouchID 4 | // 5 | // Created by ryu-ushin on 5/25/15. 6 | // Copyright (c) 2015 rain. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | @autoreleasepool { 14 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Example/TouchIDTests/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 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Example/TouchIDTests/TouchIDTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // TouchIDTests.m 3 | // TouchIDTests 4 | // 5 | // Created by ryu-ushin on 5/25/15. 6 | // Copyright (c) 2015 rain. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface TouchIDTests : XCTestCase 13 | 14 | @end 15 | 16 | @implementation TouchIDTests 17 | 18 | - (void)setUp { 19 | [super setUp]; 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | } 22 | 23 | - (void)tearDown { 24 | // Put teardown code here. This method is called after the invocation of each test method in the class. 25 | [super tearDown]; 26 | } 27 | 28 | - (void)testExample { 29 | // This is an example of a functional test case. 30 | XCTAssert(YES, @"Pass"); 31 | } 32 | 33 | - (void)testPerformanceExample { 34 | // This is an example of a performance test case. 35 | [self measureBlock:^{ 36 | // Put the code you want to measure the time of here. 37 | }]; 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /Example/demo_gif/customize1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liu044100/SmileTouchID/5b6584ffd4b8f1062a380a7f19f290525351a7e4/Example/demo_gif/customize1.png -------------------------------------------------------------------------------- /Example/demo_gif/demo1.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liu044100/SmileTouchID/5b6584ffd4b8f1062a380a7f19f290525351a7e4/Example/demo_gif/demo1.gif -------------------------------------------------------------------------------- /Example/demo_gif/demo2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liu044100/SmileTouchID/5b6584ffd4b8f1062a380a7f19f290525351a7e4/Example/demo_gif/demo2.gif -------------------------------------------------------------------------------- /Example/demo_gif/demo5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liu044100/SmileTouchID/5b6584ffd4b8f1062a380a7f19f290525351a7e4/Example/demo_gif/demo5.png -------------------------------------------------------------------------------- /Example/demo_gif/demo6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liu044100/SmileTouchID/5b6584ffd4b8f1062a380a7f19f290525351a7e4/Example/demo_gif/demo6.png -------------------------------------------------------------------------------- /Example/demo_gif/night_mode1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liu044100/SmileTouchID/5b6584ffd4b8f1062a380a7f19f290525351a7e4/Example/demo_gif/night_mode1.png -------------------------------------------------------------------------------- /Example/demo_gif/passlength.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liu044100/SmileTouchID/5b6584ffd4b8f1062a380a7f19f290525351a7e4/Example/demo_gif/passlength.png -------------------------------------------------------------------------------- /Example/demo_gif/promo_banner_s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liu044100/SmileTouchID/5b6584ffd4b8f1062a380a7f19f290525351a7e4/Example/demo_gif/promo_banner_s.png -------------------------------------------------------------------------------- /Example/demo_gif/rotate.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liu044100/SmileTouchID/5b6584ffd4b8f1062a380a7f19f290525351a7e4/Example/demo_gif/rotate.gif -------------------------------------------------------------------------------- /Example/demo_gif/step2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liu044100/SmileTouchID/5b6584ffd4b8f1062a380a7f19f290525351a7e4/Example/demo_gif/step2.png -------------------------------------------------------------------------------- /Example/demo_gif/step3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liu044100/SmileTouchID/5b6584ffd4b8f1062a380a7f19f290525351a7e4/Example/demo_gif/step3.png -------------------------------------------------------------------------------- /Example/demo_gif/step4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liu044100/SmileTouchID/5b6584ffd4b8f1062a380a7f19f290525351a7e4/Example/demo_gif/step4.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 LIU YUCHEN 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SmileTouchID 2 | 3 | [![GitHub Issues](http://img.shields.io/github/issues/liu044100/SmileTouchID.svg?style=flat)](https://github.com/liu044100/SmileTouchID/issues) 4 | [![Version](https://img.shields.io/cocoapods/v/SmileTouchID.svg?style=flat)](http://cocoadocs.org/docsets/SmileTouchID) 5 | [![License](https://img.shields.io/cocoapods/l/SmileTouchID.svg?style=flat)](http://cocoadocs.org/docsets/SmileTouchID) 6 | [![Platform](https://img.shields.io/cocoapods/p/SmileTouchID.svg?style=flat)](http://cocoadocs.org/docsets/SmileTouchID) 7 | 8 | A library for integrate Touch ID & Passcode to iOS App conveniently. 9 | 10 | 11 | 12 | #What can it do for you? 13 | 14 | 15 | ##### 1. Handle all complicated things about Touch ID & Passcode. You just need to write a few simple code to integrate Touch ID & Passcode to your app. 16 | 17 | 18 | For example, handle the device that not support Touch ID, instead of Touch ID, use Passcode for authentication. 19 | 20 | handle the whole process about securing the app (the user change passcode or turn passcode off), 21 | 22 | 23 | ```Objective-c 24 | if ([SmileAuthenticator hasPassword]) { 25 | [SmileAuthenticator sharedInstance].securityType = INPUT_TOUCHID; 26 | [[SmileAuthenticator sharedInstance] presentAuthViewController]; 27 | } 28 | ``` 29 | 30 | 31 | 32 | ##### 2. Get elegant animation and adaptive UI automatically. 33 | 34 | 35 | ![](https://raw.githubusercontent.com/liu044100/SmileTouchID/master/Example/demo_gif/demo1.gif) 36 | ![](https://raw.githubusercontent.com/liu044100/SmileTouchID/master/Example/demo_gif/demo2.gif) 37 | ![](https://raw.githubusercontent.com/liu044100/SmileTouchID/master/Example/demo_gif/rotate.gif) 38 | 39 | 40 | 41 | ##### 3. Can customize the color,Touch ID icon and background image to fit your app style. For example, you can customize like the below image. 42 | 43 | ```Objective-c 44 | [SmileAuthenticator sharedInstance].tintColor = [UIColor purpleColor]; 45 | [SmileAuthenticator sharedInstance].touchIDIconName = @"my_Touch_ID"; 46 | [SmileAuthenticator sharedInstance].appLogoName = @"my_Logo"; 47 | [SmileAuthenticator sharedInstance].navibarTranslucent = YES; 48 | [SmileAuthenticator sharedInstance].backgroundImage = [UIImage imageNamed:@"backgroundImage"]; 49 | 50 | ``` 51 | 52 | 53 | You can use the property `nightMode` to change all the UI element to black style, like the below image. 54 | 55 | ```Objective-c 56 | [SmileAuthenticator sharedInstance].nightMode = YES; 57 | [SmileAuthenticator sharedInstance].backgroundImage = [UIImage imageNamed:@"nightMode_BG"]; 58 | 59 | ``` 60 | 61 | 62 | ##### 4. Can customize the passcode digit to 6 or 10, or any number, automatically handle other things for you. 63 | 64 | ```Objective-c 65 | [SmileAuthenticator sharedInstance].passcodeDigit = 6; 66 | ``` 67 | 68 | If you want to try this feature in the demo app, before you try to change `passcodeDigit` to new number, make sure turn off the passcode switch, because the keychain will save your old passcode even you delete the app, so you have to clear the old passcode in keychain, and then change new digit for test. 69 | 70 | 71 | 72 | ##### 5. Support iOS7 and later. 73 | 74 | In iOS7, because Apple had not given the TouchID API to developers, only use Passcode for authentication. 75 | 76 | 77 | #Theoretical Introduction 78 | 79 | The main class is the `SmileAuthenticator`. It has a property `SecurityType` that has four types: `INPUT_ONCE`, `INPUT_TWICE`, `INPUT_THREE`, `INPUT_TOUCHID`. The reason for this name is that show the user input times. 80 | 81 | 82 | **`INPUT_ONCE`:** For the user turn the passcode switch off, user need input their passcode only one time for turn the password off. 83 | 84 | 85 | **`INPUT_TWICE`:** For the user turn the password switch on, user need input their password once and re-enter their password one more time for confirm it to match each other. 86 | 87 | 88 | **`INPUT_THREE`:** For the user change the password, user need input their old passcode one time, then input their new passcode one time and re-enter one time for confirm, a total of three times. 89 | 90 | 91 | **`INPUT_TOUCHID`:** For the user open the app, user can use touch ID or input passcode to unlock. 92 | 93 | 94 | Use `[[SmileAuthenticator sharedInstance] presentAuthViewController]` to present view for authentication. 95 | 96 | 97 | #How to use it for your project? 98 | 99 | **Step 1.** SmileTouchID is available through use [CocoaPods](http://cocoapods.org). To install 100 | it, simply add the following line to your Podfile: 101 | 102 | ```Ruby 103 | pod 'SmileTouchID' 104 | 105 | ``` 106 | Or you can drag the `SmileAuth` folder to your project. 107 | 108 | **Step 2.** Import `SmileAuthenticator.h` to your `AppDelegate.m`, and add below line to `didFinishLaunchingWithOptions`. 109 | 110 | ```Objective-c 111 | [SmileAuthenticator sharedInstance].rootVC = self.window.rootViewController; 112 | ``` 113 | 114 | ![](https://raw.githubusercontent.com/liu044100/SmileTouchID/master/Example/demo_gif/step2.png) 115 | 116 | **Step 3.** In your project root view controller, add below line to `viewDidAppear:`. 117 | 118 | ```Objective-c 119 | if ([SmileAuthenticator hasPassword]) { 120 | [SmileAuthenticator sharedInstance].securityType = INPUT_TOUCHID; 121 | [[SmileAuthenticator sharedInstance] presentAuthViewController]; 122 | } 123 | ``` 124 | 125 | 126 | ![](https://raw.githubusercontent.com/liu044100/SmileTouchID/master/Example/demo_gif/step3.png) 127 | 128 | **Step 4.** Configure with your interactive UI parts, set appropriate `securityType`, then call `presentAuthViewController`. 129 | 130 | For example below image show a switch to turn the passcode on/off, when the switch turn on, the `securityType` is `INPUT_TWICE`, when turn off, the `securityType` is `INPUT_ONCE`. A button for change passcode, the `securityType` is `INPUT_THREE`. 131 | 132 | ```Objective-c 133 | - (IBAction)changePassword:(id)sender { 134 | [SmileAuthenticator sharedInstance].securityType = INPUT_THREE; 135 | [[SmileAuthenticator sharedInstance] presentAuthViewController]; 136 | } 137 | 138 | - (IBAction)passwordSwitch:(UISwitch*)passwordSwitch { 139 | if (passwordSwitch.on) { 140 | [SmileAuthenticator sharedInstance].securityType = INPUT_TWICE; 141 | } else { 142 | [SmileAuthenticator sharedInstance].securityType = INPUT_ONCE; 143 | } 144 | 145 | [[SmileAuthenticator sharedInstance] presentAuthViewController]; 146 | } 147 | 148 | ``` 149 | 150 | ![](https://raw.githubusercontent.com/liu044100/SmileTouchID/master/Example/demo_gif/step4.png) 151 | 152 | 153 | Update your interactive UI parts in `viewWillAppear`. 154 | 155 | For example, below code show update the switch and button based on `[SmileAuthenticator hasPassword]`. 156 | 157 | ```Objective-c 158 | -(void)viewWillAppear:(BOOL)animated{ 159 | [super viewWillAppear:animated]; 160 | 161 | if ([SmileAuthenticator hasPassword]) { 162 | self.mySwitch.on = YES; 163 | self.changePasswordButton.hidden = NO; 164 | } else { 165 | self.mySwitch.on = NO; 166 | self.changePasswordButton.hidden = YES; 167 | } 168 | } 169 | ``` 170 | 171 | 172 | **Step 5.** Build your project, very simple :) 173 | 174 | #About Delegate callback 175 | 176 | `SmileAuthenticator` has a delegate that can get more information about the process of authentication. 177 | 178 | The delegate name is `SmileAuthenticatorDelegate`. It has four optional method. 179 | 180 | ```Objective-c 181 | @protocol SmileAuthenticatorDelegate 182 | @optional 183 | /*!The method is called when AuthViewController be presented*/ 184 | -(void)AuthViewControllerPresented; 185 | @optional 186 | /*!The method is called when AuthViewController be dismissed*/ 187 | -(void)AuthViewControllerDismssed; 188 | @optional 189 | /*!The method is called when user success authentication by using Touch ID & Passcode*/ 190 | -(void)userSuccessAuthentication; 191 | @optional 192 | /*!The method is called when authentication failed*/ 193 | -(void)userFailAuthenticationWithCount:(NSInteger)failCount; 194 | @optional 195 | /*!The method is called when user turn password on.*/ 196 | -(void)userTurnPasswordOn; 197 | @optional 198 | /*!The method is called when user turn password off.*/ 199 | -(void)userTurnPasswordOff; 200 | @optional 201 | /*!The method is called when user change password.*/ 202 | -(void)userChangePassword; 203 | ``` 204 | # Localization 205 | Sorry about it, but you have to do it yourself. Add below line to your `Localizable.strings`. For detail please see the example demo app. 206 | 207 | ```Objective-c 208 | /* 209 | UNIVERSAL PARTS 210 | */ 211 | "SMILE_REASON" = "Are you device owner?"; 212 | "SMILE_INPUT_FAILED" = "%ld Failed Passcode Attempt. Try again."; 213 | "SMILE_INPUT_DESCRIPTION" = "Enter %ld digit passcode"; 214 | "SMILE_INPUT_NOT_MATCH" = "Passcode not match. Try again."; 215 | "SMILE_INPUT_RE-ENTER" = "Re-enter your %ld digit Passcode"; 216 | 217 | /* 218 | INPUT_TOUCHID 219 | */ 220 | "SMILE_INPUT_TOUCHID_TITLE" = "Enter Passcode"; 221 | 222 | 223 | /* 224 | INPUT_ONCE 225 | */ 226 | "SMILE_INPUT_ONCE_TITLE" = "Turn off Passcode"; 227 | 228 | 229 | /* 230 | INPUT_TWICE 231 | */ 232 | "SMILE_INPUT_TWICE_TITLE" = "Set Passcode"; 233 | 234 | 235 | /* 236 | INPUT_THREE 237 | */ 238 | "SMILE_INPUT_THREE_TITLE" = "Change Passcode"; 239 | "SMILE_INPUT_THREE_STEP_1_DESCRIPTION" = "Enter your old %ld digit Passcode"; 240 | "SMILE_INPUT_THREE_STEP_2_DESCRIPTION" = "Enter your new %ld digit Passcode"; 241 | 242 | ``` 243 | # Tips For Security 244 | 245 | Because iOS automatically snapshot screen, so when the app resign the active & the user have turned the passcode on, You can use a cover image to cover the UI for protecting user data when the user double tap home button, or the app enter the background. 246 | 247 | For more detail, please check the [example project](https://github.com/liu044100/SmileTouchID/tree/master/Example). 248 | 249 | ```Objective-c 250 | - (void)applicationWillResignActive:(UIApplication *)application { 251 | if ([SmileAuthenticator hasPassword] && [SmileAuthenticator sharedInstance].isShowingAuthVC == NO) { 252 | [self showCoverImageView]; 253 | } 254 | } 255 | 256 | - (void)applicationDidBecomeActive:(UIApplication *)application { 257 | if ([SmileAuthenticator hasPassword]) { 258 | //if now is authenticated, remove the cover image. 259 | if([SmileAuthenticator sharedInstance].isAuthenticated){ 260 | [self removeCoverImageView]; 261 | } 262 | } 263 | } 264 | ``` 265 | 266 | 267 | 268 | # Contributions 269 | 270 | * Warmly welcome to submit a pull request. 271 | 272 | # Contact 273 | 274 | * If you have some advice or find some issue, please contact me. 275 | * Email [me](liu044100@gmail.com) 276 | 277 | # Thanks 278 | Thanks for raywenderlich's tutorial about [securing iOS User Data](http://www.raywenderlich.com/92667/securing-ios-data-keychain-touch-id-1password), I am inspired by this tutorial. 279 | 280 | # License 281 | 282 | SmileTouchID is available under the MIT license. See the LICENSE file for more info. 283 | -------------------------------------------------------------------------------- /SmileAuth/Assets/SmileSettingVC.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 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 56 | 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 | -------------------------------------------------------------------------------- /SmileAuth/Assets/smile_Touch_ID@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liu044100/SmileTouchID/5b6584ffd4b8f1062a380a7f19f290525351a7e4/SmileAuth/Assets/smile_Touch_ID@2x.png -------------------------------------------------------------------------------- /SmileAuth/Assets/smile_Touch_ID@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liu044100/SmileTouchID/5b6584ffd4b8f1062a380a7f19f290525351a7e4/SmileAuth/Assets/smile_Touch_ID@3x.png -------------------------------------------------------------------------------- /SmileAuth/Classes/SmileAuthenticator.h: -------------------------------------------------------------------------------- 1 | // 2 | // SmileAuthenticator.h 3 | // TouchID 4 | // 5 | // Created by ryu-ushin on 5/25/15. 6 | // Copyright (c) 2015 rain. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "SmileKeychainWrapper.h" 12 | #import "SmileSettingVC.h" 13 | 14 | typedef void(^AuthCompletionBlock)(); 15 | typedef void(^AuthErrorBlock)(LAError); 16 | 17 | /*! 18 | @typedef SecurityType 19 | 20 | @brief A struct about the SecurityType. 21 | 22 | @discussion 23 | There are four types, pass the SecurtiyType to destination View Controller for appropriate security interface. 24 | */ 25 | typedef NS_ENUM(int, SecurityType) { 26 | /*! For the user turn off the password switch, user need input their password once for turn the password off. */ 27 | INPUT_ONCE, 28 | /*! For the user turn on the password switch, user need input their password twice to confirm their password match each other. */ 29 | INPUT_TWICE, 30 | /*! For the user change the password, user need input their password three times for change to new password. */ 31 | INPUT_THREE, 32 | /*! For the user open app, user can use touch ID or input password. */ 33 | INPUT_TOUCHID, 34 | }; 35 | 36 | /*!@brief The notification is posted when the present AuthVC completion.*/ 37 | #define SmileTouchID_Presented_AuthVC_Notification @"SmileTouchID_Presented_AuthVC" 38 | 39 | @protocol SmileAuthenticatorDelegate; 40 | 41 | @interface SmileAuthenticator : NSObject 42 | 43 | @property (nonatomic, copy) NSString * localizedReason; 44 | @property (nonatomic, strong) SmileKeychainWrapper *keychainWrapper; 45 | @property (nonatomic, assign) SecurityType securityType; 46 | /*!@brief This property show whether the AuthVC is presenting or not.*/ 47 | @property (nonatomic, readonly) BOOL isShowingAuthVC; 48 | /*!@brief This property show whether now is authenticated or not.*/ 49 | @property (nonatomic, readonly) BOOL isAuthenticated; 50 | @property (nonatomic, strong) UIViewController *rootVC; 51 | @property (nonatomic, weak) id delegate; 52 | /*!@brief For customization, use this property to customize tint color. The default color is pink.*/ 53 | @property (nonatomic, strong) UIColor *tintColor; 54 | /*!@brief For customization, use this property to customize description label text color. The default color is black, if nightMode on, the color is white.*/ 55 | @property (nonatomic, strong) UIColor *descriptionTextColor; 56 | /*!@brief For customization, use this property to customize Touch ID icon. The default icon is the Apple official pink Touch ID icon.*/ 57 | @property (nonatomic, strong) NSString *touchIDIconName; 58 | /*!@brief For customization, use this property to set the app logo to UI.*/ 59 | @property (nonatomic, strong) NSString *appLogoName; 60 | /*!@brief For customization, use this property to set the backgroundImage.*/ 61 | @property (nonatomic, strong) UIImage *backgroundImage; 62 | /*!@brief For customization, use this property to change passcode digit. The default digit is 4.*/ 63 | @property (nonatomic) NSInteger passcodeDigit; 64 | /*!@brief For customization, if set it to Yes, change UINavigationBar to transparent, the default value is No.*/ 65 | @property (nonatomic) BOOL navibarTranslucent; 66 | /*!@brief For customization, if set it to Yes, change to a black style UI, the default value is No.*/ 67 | @property (nonatomic) BOOL nightMode; 68 | /*!@brief For customization, if set it to Yes, add parallax effect to password circle views, the default value is Yes.*/ 69 | @property (nonatomic) BOOL parallaxMode; 70 | /*!@brief The timeout interval, in seconds, for show password or touch ID UI again. The default timeout interval is 0 seconds, it means password or touch ID will be showed immediately. */ 71 | @property (nonatomic, assign) NSTimeInterval timeoutInterval; 72 | 73 | +(SmileAuthenticator*)sharedInstance; 74 | + (BOOL)canAuthenticateWithError:(NSError **) error; 75 | +(BOOL)hasPassword; 76 | +(BOOL)isSamePassword:(NSString *)userInput; 77 | +(void)clearPassword; 78 | /*!@brief Manually change authentication status, e.g., when your app has a payment view, you want to show authentication view when visit the payment view.*/ 79 | -(void)changeAuthentication:(BOOL)newAuth; 80 | 81 | 82 | -(void)userSetPassword:(NSString*)newPassword; 83 | -(void)authenticateWithSuccess:(AuthCompletionBlock) authSuccessBlock andFailure:(AuthErrorBlock) failureBlock; 84 | 85 | -(void)presentAuthViewControllerAnimated:(BOOL)animated; 86 | -(void)authViewControllerWillDismissed; 87 | -(void)authViewControllerDidDismissed; 88 | -(void)touchID_OR_PasswordAuthSuccess; 89 | -(void)touchID_OR_PasswordAuthFail:(NSInteger)failCount; 90 | -(void)touchID_OR_PasswordTurnOff; 91 | -(void)touchID_OR_PasswordTurnOn; 92 | -(void)touchID_OR_PasswordChange; 93 | @end 94 | 95 | @protocol SmileAuthenticatorDelegate 96 | @optional 97 | /*!The method is called when AuthViewController be presented*/ 98 | -(void)AuthViewControllerPresented; 99 | @optional 100 | /*!The method is called when AuthViewController be dismissed*/ 101 | -(void)AuthViewControllerDismissed:(UIViewController*)previousPresentedVC; 102 | @optional 103 | /*!The method is called when user success authentication by using Touch ID & Passcode*/ 104 | -(void)userSuccessAuthentication; 105 | @optional 106 | /*!The method is called when authentication failed*/ 107 | -(void)userFailAuthenticationWithCount:(NSInteger)failCount; 108 | @optional 109 | /*!The method is called when user turn password on.*/ 110 | -(void)userTurnPasswordOn; 111 | @optional 112 | /*!The method is called when user turn password off.*/ 113 | -(void)userTurnPasswordOff; 114 | @optional 115 | /*!The method is called when user change password.*/ 116 | -(void)userChangePassword; 117 | @end 118 | -------------------------------------------------------------------------------- /SmileAuth/Classes/SmileAuthenticator.m: -------------------------------------------------------------------------------- 1 | // 2 | // SmileAuthenticator.m 3 | // TouchID 4 | // 5 | // Created by ryu-ushin on 5/25/15. 6 | // Copyright (c) 2015 rain. All rights reserved. 7 | // 8 | 9 | #import "SmileAuthenticator.h" 10 | 11 | #define kPasswordLength 4 12 | #define kTouchIDIcon @"smile_Touch_ID" 13 | 14 | static NSString *kStoryBoardName = @"SmileSettingVC"; 15 | 16 | #define SmileTouchID_DispatchMainThread(block, ...) if(block) dispatch_async(dispatch_get_main_queue(), ^{ block(__VA_ARGS__); }) 17 | 18 | @interface SmileAuthenticator() 19 | 20 | @property (nonatomic, assign) LAPolicy policy; 21 | @property (nonatomic, strong) LAContext * context; 22 | @property (nonatomic, readwrite) BOOL isShowingAuthVC; 23 | @property (nonatomic, readwrite) BOOL isAuthenticated; 24 | @property (nonatomic, strong) UIViewController *previousPresentedVC; 25 | @property (nonatomic, strong) NSDate *previousAuthenticatedDate; 26 | 27 | @end 28 | 29 | 30 | @implementation SmileAuthenticator{ 31 | BOOL _didReturnFromBackground; 32 | } 33 | 34 | #pragma mark -getter 35 | 36 | -(void)setPasscodeDigit:(NSInteger)passcodeDigit{ 37 | NSInteger buffer = [self lengthOfPassword]; 38 | if (buffer > 0) { 39 | _passcodeDigit = buffer; 40 | } else if (!passcodeDigit || passcodeDigit < 0) { 41 | _passcodeDigit = kPasswordLength; 42 | } else if (_passcodeDigit != passcodeDigit) { 43 | _passcodeDigit = passcodeDigit; 44 | } 45 | } 46 | 47 | -(NSString *)touchIDIconName{ 48 | if (!_touchIDIconName.length) { 49 | return kTouchIDIcon; 50 | } else { 51 | return _touchIDIconName; 52 | } 53 | } 54 | 55 | #pragma mark - dealloc 56 | 57 | -(void)dealloc{ 58 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 59 | } 60 | 61 | #pragma mark - for Delegate 62 | 63 | -(void)touchID_OR_PasswordAuthSuccess{ 64 | self.previousAuthenticatedDate = [NSDate date]; 65 | if ([self.delegate respondsToSelector:@selector(userSuccessAuthentication)]) { 66 | [self.delegate userSuccessAuthentication]; 67 | } 68 | } 69 | 70 | -(void)touchID_OR_PasswordAuthFail:(NSInteger)failCount{ 71 | if ([self.delegate respondsToSelector:@selector(userFailAuthenticationWithCount:)]) { 72 | [self.delegate userFailAuthenticationWithCount:failCount]; 73 | } 74 | } 75 | 76 | -(void)touchID_OR_PasswordTurnOff{ 77 | if ([self.delegate respondsToSelector:@selector(userTurnPasswordOff)]) { 78 | [self.delegate userTurnPasswordOff]; 79 | } 80 | } 81 | 82 | -(void)touchID_OR_PasswordTurnOn{ 83 | if ([self.delegate respondsToSelector:@selector(userTurnPasswordOn)]) { 84 | [self.delegate userTurnPasswordOn]; 85 | } 86 | } 87 | 88 | -(void)touchID_OR_PasswordChange{ 89 | if ([self.delegate respondsToSelector:@selector(userChangePassword)]) { 90 | [self.delegate userChangePassword]; 91 | } 92 | } 93 | 94 | -(void)presentAuthViewControllerAnimated:(BOOL)animated{ 95 | 96 | if (self.securityType != INPUT_TOUCHID) { 97 | _isAuthenticated = NO; 98 | } else { 99 | if (self.previousAuthenticatedDate && !_isAuthenticated) { 100 | NSTimeInterval intervalSincePreviousAuthenticated = fabs(self.previousAuthenticatedDate.timeIntervalSinceNow); 101 | _isAuthenticated = (intervalSincePreviousAuthenticated < self.timeoutInterval); 102 | } 103 | } 104 | 105 | if (!_isAuthenticated) { 106 | 107 | //dimiss all presentedViewController, for example, if user is editing password 108 | if (self.rootVC.presentedViewController) { 109 | self.previousPresentedVC = self.rootVC.presentedViewController; 110 | [self.rootVC.presentedViewController dismissViewControllerAnimated:NO completion:nil]; 111 | } 112 | 113 | if ([self.delegate respondsToSelector:@selector(AuthViewControllerPresented)]) { 114 | [self.delegate AuthViewControllerPresented]; 115 | } 116 | 117 | self.isShowingAuthVC = YES; 118 | 119 | NSBundle *bundle = [NSBundle bundleForClass: self.class]; 120 | 121 | UIStoryboard *storyboard = [UIStoryboard storyboardWithName:kStoryBoardName bundle: bundle]; 122 | 123 | UINavigationController *naviVC = [storyboard instantiateInitialViewController]; 124 | 125 | [self.rootVC presentViewController:naviVC animated:animated completion:^{ 126 | [[NSNotificationCenter defaultCenter] postNotificationName:SmileTouchID_Presented_AuthVC_Notification object:nil]; 127 | }]; 128 | } 129 | } 130 | 131 | -(void)authViewControllerDidDismissed{ 132 | if ([self.delegate respondsToSelector:@selector(AuthViewControllerDismissed:)]) { 133 | [self.delegate AuthViewControllerDismissed: self.previousPresentedVC]; 134 | self.previousPresentedVC = nil; 135 | } 136 | } 137 | 138 | -(void)authViewControllerWillDismissed{ 139 | _isAuthenticated = true; 140 | self.isShowingAuthVC = NO; 141 | } 142 | 143 | #pragma mark - NSNotificationCenter 144 | 145 | -(void)configureNotification{ 146 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appDidEnterBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil]; 147 | 148 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appWillEnterForeground:) name:UIApplicationWillEnterForegroundNotification object:nil]; 149 | } 150 | 151 | -(void)appDidEnterBackground:(NSNotification*)notification{ 152 | if (_isAuthenticated) { 153 | _didReturnFromBackground = YES; 154 | _isAuthenticated = NO; 155 | } 156 | } 157 | 158 | -(void)appWillEnterForeground:(NSNotification*)notification{ 159 | if (_didReturnFromBackground && !self.isShowingAuthVC) { 160 | if ([SmileAuthenticator hasPassword]) { 161 | //show login vc 162 | self.securityType = INPUT_TOUCHID; 163 | [self presentAuthViewControllerAnimated:false]; 164 | } 165 | } 166 | } 167 | 168 | #pragma mark - init 169 | 170 | +(SmileAuthenticator *)sharedInstance{ 171 | static id sharedInstance = nil; 172 | static dispatch_once_t onceToken; 173 | dispatch_once(&onceToken, ^{ 174 | sharedInstance = [[self alloc] init]; 175 | }); 176 | 177 | return sharedInstance; 178 | } 179 | 180 | 181 | -(instancetype)init{ 182 | if (self = [super init]) { 183 | self.context = [[LAContext alloc] init]; 184 | self.policy = LAPolicyDeviceOwnerAuthenticationWithBiometrics; 185 | self.localizedReason = NSLocalizedString(@"SMILE_REASON", nil); 186 | self.keychainWrapper = [[SmileKeychainWrapper alloc] init]; 187 | self.securityType = INPUT_TWICE; 188 | self.parallaxMode = YES; 189 | self.passcodeDigit = kPasswordLength; 190 | self.timeoutInterval = 0; 191 | 192 | [self configureNotification]; 193 | } 194 | return self; 195 | } 196 | 197 | #pragma mark - TouchID 198 | 199 | + (BOOL) canAuthenticateWithError:(NSError **) error 200 | { 201 | if ([NSClassFromString(@"LAContext") class]) { 202 | if ([[SmileAuthenticator sharedInstance].context canEvaluatePolicy:[SmileAuthenticator sharedInstance].policy error:error]) { 203 | return YES; 204 | } 205 | return NO; 206 | } 207 | return NO; 208 | } 209 | 210 | -(void)authenticateWithSuccess:(AuthCompletionBlock)authSuccessBlock andFailure:(AuthErrorBlock)failureBlock{ 211 | 212 | NSError *authError = nil; 213 | 214 | self.context = [[LAContext alloc] init]; 215 | 216 | if ([SmileAuthenticator canAuthenticateWithError:&authError]) { 217 | [self.context evaluatePolicy:self.policy localizedReason:self.localizedReason reply:^(BOOL success, NSError *error) { 218 | if (success) { 219 | SmileTouchID_DispatchMainThread(^(){ 220 | authSuccessBlock(); 221 | }); 222 | } 223 | 224 | else { 225 | switch (error.code) { 226 | case LAErrorAuthenticationFailed: 227 | 228 | NSLog(@"LAErrorAuthenticationFailed"); 229 | 230 | break; 231 | 232 | case LAErrorUserCancel: 233 | 234 | NSLog(@"LAErrorUserCancel"); 235 | 236 | break; 237 | 238 | case LAErrorUserFallback: 239 | 240 | NSLog(@"LAErrorUserFallback"); 241 | 242 | break; 243 | 244 | case LAErrorSystemCancel: 245 | 246 | NSLog(@"LAErrorSystemCancel"); 247 | 248 | break; 249 | 250 | case LAErrorPasscodeNotSet: 251 | 252 | NSLog(@"LAErrorPasscodeNotSet"); 253 | 254 | break; 255 | 256 | case LAErrorTouchIDNotAvailable: 257 | 258 | NSLog(@"LAErrorTouchIDNotAvailable"); 259 | 260 | break; 261 | 262 | case LAErrorTouchIDNotEnrolled: 263 | 264 | NSLog(@"LAErrorTouchIDNotEnrolled"); 265 | 266 | break; 267 | 268 | default: 269 | break; 270 | } 271 | 272 | SmileTouchID_DispatchMainThread(^(){ 273 | failureBlock((LAError) error.code); 274 | }); 275 | } 276 | }]; 277 | } 278 | 279 | else { 280 | failureBlock((LAError) authError.code); 281 | } 282 | } 283 | 284 | #pragma mark - Utility 285 | 286 | +(BOOL)hasPassword { 287 | 288 | if ([(NSString*)[[SmileAuthenticator sharedInstance].keychainWrapper myObjectForKey:(__bridge id)(kSecValueData)] length] > 0) { 289 | return YES; 290 | } 291 | 292 | return NO; 293 | } 294 | 295 | -(NSInteger)lengthOfPassword{ 296 | return [[self.keychainWrapper myObjectForKey:(__bridge id)(kSecValueData)] length]; 297 | } 298 | 299 | +(BOOL)isSamePassword:(NSString *)userInput{ 300 | //use this line to log password, if you forgot it. 301 | //NSLog(@"the password -> %@", [[SmileAuthenticator sharedInstance].keychainWrapper myObjectForKey:(__bridge id)(kSecValueData)]); 302 | if ([userInput isEqualToString:[[SmileAuthenticator sharedInstance].keychainWrapper myObjectForKey:(__bridge id)(kSecValueData)]]) { 303 | return YES; 304 | } 305 | 306 | return NO; 307 | } 308 | 309 | -(void)userSetPassword:(NSString*)newPassword{ 310 | [self.keychainWrapper mySetObject:newPassword forKey:(__bridge id)(kSecValueData)]; 311 | [self.keychainWrapper writeToKeychain]; 312 | } 313 | 314 | +(void)clearPassword{ 315 | [[SmileAuthenticator sharedInstance].keychainWrapper resetKeychainItem]; 316 | [[SmileAuthenticator sharedInstance].keychainWrapper writeToKeychain]; 317 | } 318 | 319 | -(void)changeAuthentication:(BOOL)newAuth { 320 | _isAuthenticated = newAuth; 321 | } 322 | @end 323 | -------------------------------------------------------------------------------- /SmileAuth/Classes/SmileKeychainWrapper.h: -------------------------------------------------------------------------------- 1 | // 2 | // KeychainWrapper.h 3 | // Apple's Keychain Services Programming Guide 4 | // 5 | // Created by Tim Mitra on 11/17/14. 6 | // Copyright (c) 2014 Apple. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface SmileKeychainWrapper : NSObject 13 | 14 | - (void)mySetObject:(id)inObject forKey:(id)key; 15 | - (id)myObjectForKey:(id)key; 16 | - (void)writeToKeychain; 17 | - (void)resetKeychainItem; 18 | @end 19 | -------------------------------------------------------------------------------- /SmileAuth/Classes/SmileKeychainWrapper.m: -------------------------------------------------------------------------------- 1 | // 2 | // KeychainWrapper.h 3 | // Apple's Keychain Services Programming Guide 4 | // 5 | // Created by Tim Mitra on 11/17/14. 6 | // Copyright (c) 2014 Apple. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //Unique string used to identify the keychain item: 12 | static const UInt8 kKeychainItemIdentifier[] = "com.apple.dts.KeychainUI\0"; 13 | 14 | @interface SmileKeychainWrapper () 15 | 16 | @property (nonatomic, strong) NSMutableDictionary *keychainData; 17 | @property (nonatomic, strong) NSMutableDictionary *genericPasswordQuery; 18 | 19 | @end 20 | 21 | @interface SmileKeychainWrapper (PrivateMethods) 22 | 23 | //The following two methods translate dictionaries between the format used by 24 | // the view controller (NSString *) and the Keychain Services API: 25 | - (NSMutableDictionary *)secItemFormatToDictionary:(NSDictionary *)dictionaryToConvert; 26 | - (NSMutableDictionary *)dictionaryToSecItemFormat:(NSDictionary *)dictionaryToConvert; 27 | // Method used to write data to the keychain: 28 | - (void)writeToKeychain; 29 | 30 | @end 31 | 32 | 33 | @implementation SmileKeychainWrapper 34 | 35 | - (instancetype)init 36 | { 37 | self = [super init]; 38 | 39 | if (self) { 40 | 41 | 42 | OSStatus keychainErr = noErr; 43 | // Set up the keychain search dictionary: 44 | _genericPasswordQuery = [[NSMutableDictionary alloc] init]; 45 | 46 | // This keychain item is a generic password. 47 | [_genericPasswordQuery setObject:(__bridge id)kSecClassGenericPassword 48 | forKey:(__bridge id)kSecClass]; 49 | 50 | // The kSecAttrGeneric attribute is used to store a unique string that is used 51 | // to easily identify and find this keychain item. The string is first 52 | // converted to an NSData object: 53 | NSData *keychainItemID = [NSData dataWithBytes:kKeychainItemIdentifier 54 | length:strlen((const char *)kKeychainItemIdentifier)]; 55 | [_genericPasswordQuery setObject:keychainItemID forKey:(__bridge id)kSecAttrGeneric]; 56 | 57 | // Return the attributes of the first match only: 58 | [_genericPasswordQuery setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit]; 59 | 60 | // Return the attributes of the keychain item (the password is 61 | // acquired in the secItemFormatToDictionary: method): 62 | [_genericPasswordQuery setObject:(__bridge id)kCFBooleanTrue 63 | forKey:(__bridge id)kSecReturnAttributes]; 64 | 65 | //Initialize the dictionary used to hold return data from the keychain: 66 | CFMutableDictionaryRef outDictionary = nil; 67 | // If the keychain item exists, return the attributes of the item: 68 | keychainErr = SecItemCopyMatching((__bridge CFDictionaryRef)_genericPasswordQuery, 69 | (CFTypeRef *)&outDictionary); 70 | 71 | if (keychainErr == noErr) { 72 | // Convert the data dictionary into the format used by the view controller: 73 | self.keychainData = [self secItemFormatToDictionary:(__bridge_transfer NSMutableDictionary *)outDictionary]; 74 | } else if (keychainErr == errSecItemNotFound) { 75 | // Put default values into the keychain if no matching 76 | // keychain item is found: 77 | [self resetKeychainItem]; 78 | if (outDictionary) CFRelease(outDictionary); 79 | } else { 80 | // Any other error is unexpected. 81 | NSAssert(NO, @"Serious error.\n"); 82 | if (outDictionary) CFRelease(outDictionary); 83 | } 84 | } 85 | 86 | return self; 87 | } 88 | 89 | // Implement the mySetObject:forKey method, which writes attributes to the keychain: 90 | - (void)mySetObject:(id)inObject forKey:(id)key 91 | { 92 | if (inObject == nil) return; 93 | id currentObject = [_keychainData objectForKey:key]; 94 | if (![currentObject isEqual:inObject]) 95 | { 96 | [_keychainData setObject:inObject forKey:key]; 97 | [self writeToKeychain]; 98 | } 99 | } 100 | 101 | // Implement the myObjectForKey: method, which reads an attribute value from a dictionary: 102 | - (id)myObjectForKey:(id)key 103 | { 104 | return [_keychainData objectForKey:key]; 105 | } 106 | 107 | // Reset the values in the keychain item, or create a new item if it 108 | // doesn't already exist: 109 | 110 | - (void)resetKeychainItem 111 | { 112 | if (!_keychainData) //Allocate the keychainData dictionary if it doesn't exist yet. 113 | { 114 | self.keychainData = [[NSMutableDictionary alloc] init]; 115 | } 116 | else if (_keychainData) 117 | { 118 | // Format the data in the keychainData dictionary into the format needed for a query 119 | // and put it into tmpDictionary: 120 | NSMutableDictionary *tmpDictionary = 121 | [self dictionaryToSecItemFormat:_keychainData]; 122 | // Delete the keychain item in preparation for resetting the values: 123 | // OSStatus errorcode = 124 | SecItemDelete((__bridge CFDictionaryRef)tmpDictionary); 125 | // NSLog(@"keychain error code -> %d", (int)errorcode); 126 | // NSAssert(errorcode == noErr, @"Problem deleting current keychain item." ); 127 | } 128 | 129 | // Default generic data for Keychain Item: 130 | [_keychainData setObject:@"" forKey:(__bridge id)kSecAttrLabel]; 131 | [_keychainData setObject:@"" forKey:(__bridge id)kSecAttrDescription]; 132 | [_keychainData setObject:@"" forKey:(__bridge id)kSecAttrAccount]; 133 | [_keychainData setObject:@"" forKey:(__bridge id)kSecAttrService]; 134 | [_keychainData setObject:@"" forKey:(__bridge id)kSecAttrComment]; 135 | [_keychainData setObject:@"" forKey:(__bridge id)kSecValueData]; 136 | } 137 | 138 | 139 | // Implement the dictionaryToSecItemFormat: method, which takes the attributes that 140 | // you want to add to the keychain item and sets up a dictionary in the format 141 | // needed by Keychain Services: 142 | - (NSMutableDictionary *)dictionaryToSecItemFormat:(NSDictionary *)dictionaryToConvert 143 | { 144 | // This method must be called with a properly populated dictionary 145 | // containing all the right key/value pairs for a keychain item search. 146 | 147 | // Create the return dictionary: 148 | NSMutableDictionary *returnDictionary = 149 | [NSMutableDictionary dictionaryWithDictionary:dictionaryToConvert]; 150 | 151 | // Add the keychain item class and the generic attribute: 152 | NSData *keychainItemID = [NSData dataWithBytes:kKeychainItemIdentifier 153 | length:strlen((const char *)kKeychainItemIdentifier)]; 154 | [returnDictionary setObject:keychainItemID forKey:(__bridge id)kSecAttrGeneric]; 155 | [returnDictionary setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass]; 156 | 157 | // Convert the password NSString to NSData to fit the API paradigm: 158 | NSString *passwordString = [dictionaryToConvert objectForKey:(__bridge id)kSecValueData]; 159 | [returnDictionary setObject:[passwordString dataUsingEncoding:NSUTF8StringEncoding] 160 | forKey:(__bridge id)kSecValueData]; 161 | return returnDictionary; 162 | } 163 | 164 | // Implement the secItemFormatToDictionary: method, which takes the attribute dictionary 165 | // obtained from the keychain item, acquires the password from the keychain, and 166 | // adds it to the attribute dictionary: 167 | - (NSMutableDictionary *)secItemFormatToDictionary:(NSDictionary *)dictionaryToConvert 168 | { 169 | // This method must be called with a properly populated dictionary 170 | // containing all the right key/value pairs for the keychain item. 171 | 172 | // Create a return dictionary populated with the attributes: 173 | NSMutableDictionary *returnDictionary = [NSMutableDictionary 174 | dictionaryWithDictionary:dictionaryToConvert]; 175 | 176 | // To acquire the password data from the keychain item, 177 | // first add the search key and class attribute required to obtain the password: 178 | [returnDictionary setObject:(__bridge id)kCFBooleanTrue forKey:(__bridge id)kSecReturnData]; 179 | [returnDictionary setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass]; 180 | // Then call Keychain Services to get the password: 181 | CFDataRef passwordData = NULL; 182 | OSStatus keychainError = noErr; // 183 | keychainError = SecItemCopyMatching((__bridge CFDictionaryRef)returnDictionary, 184 | (CFTypeRef *)&passwordData); 185 | if (keychainError == noErr) 186 | { 187 | // Remove the kSecReturnData key; we don't need it anymore: 188 | [returnDictionary removeObjectForKey:(__bridge id)kSecReturnData]; 189 | 190 | // Convert the password to an NSString and add it to the return dictionary: 191 | NSString *password = [[NSString alloc] initWithBytes:[(__bridge_transfer NSData *)passwordData bytes] 192 | length:[(__bridge NSData *)passwordData length] encoding:NSUTF8StringEncoding]; 193 | [returnDictionary setObject:password forKey:(__bridge id)kSecValueData]; 194 | } 195 | // Don't do anything if nothing is found. 196 | else if (keychainError == errSecItemNotFound) { 197 | NSAssert(NO, @"Nothing was found in the keychain.\n"); 198 | if (passwordData) CFRelease(passwordData); 199 | } 200 | // Any other error is unexpected. 201 | else 202 | { 203 | NSAssert(NO, @"Serious error.\n"); 204 | if (passwordData) CFRelease(passwordData); 205 | } 206 | 207 | return returnDictionary; 208 | } 209 | 210 | // could be in a class 211 | - (void)writeToKeychain { 212 | 213 | CFDictionaryRef attributes = nil; 214 | NSMutableDictionary *updateItem = nil; 215 | 216 | // If the keychain item already exists, modify it: 217 | if (SecItemCopyMatching((__bridge CFDictionaryRef)_genericPasswordQuery, 218 | (CFTypeRef *)&attributes) == noErr) 219 | { 220 | // First, get the attributes returned from the keychain and add them to the 221 | // dictionary that controls the update: 222 | updateItem = [NSMutableDictionary dictionaryWithDictionary:(__bridge_transfer NSDictionary *)attributes]; 223 | 224 | // Second, get the class value from the generic password query dictionary and 225 | // add it to the updateItem dictionary: 226 | [updateItem setObject:[_genericPasswordQuery objectForKey:(__bridge id)kSecClass] 227 | forKey:(__bridge id)kSecClass]; 228 | 229 | // Finally, set up the dictionary that contains new values for the attributes: 230 | NSMutableDictionary *tempCheck = [self dictionaryToSecItemFormat:_keychainData]; 231 | //Remove the class--it's not a keychain attribute: 232 | [tempCheck removeObjectForKey:(__bridge id)kSecClass]; 233 | 234 | // You can update only a single keychain item at a time. 235 | //OSStatus errorcode = 236 | SecItemUpdate( 237 | (__bridge CFDictionaryRef)updateItem, 238 | (__bridge CFDictionaryRef)tempCheck); 239 | // NSAssert(errorcode == noErr, @"Couldn't update the Keychain Item." ); 240 | } 241 | else 242 | { 243 | // No previous item found; add the new item. 244 | // The new value was added to the keychainData dictionary in the mySetObject routine, 245 | // and the other values were added to the keychainData dictionary previously. 246 | // No pointer to the newly-added items is needed, so pass NULL for the second parameter: 247 | // OSStatus errorcode = 248 | SecItemAdd( 249 | (__bridge CFDictionaryRef)[self dictionaryToSecItemFormat:_keychainData], 250 | NULL); 251 | // NSAssert(errorcode == noErr, @"Couldn't add the Keychain Item." ); 252 | if (attributes) CFRelease(attributes); 253 | } 254 | 255 | } 256 | 257 | 258 | @end 259 | -------------------------------------------------------------------------------- /SmileAuth/Classes/SmilePasswordContainerView.h: -------------------------------------------------------------------------------- 1 | // 2 | // SmilePasswordContainerView.h 3 | // TouchID 4 | // 5 | // Created by yuchen liu on 5/27/15. 6 | // Copyright (c) 2015 rain. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "SmilePasswordView.h" 11 | 12 | 13 | @protocol SmileContainerLayoutDelegate; 14 | 15 | //IB_DESIGNABLE 16 | 17 | @interface SmilePasswordContainerView : UIView 18 | //IBInspectable 19 | @property (nonatomic, strong) UIColor *mainColor; 20 | @property (nonatomic, strong) SmilePasswordView *smilePasswordView; 21 | @property (nonatomic, weak) id delegate; 22 | 23 | @end 24 | 25 | @protocol SmileContainerLayoutDelegate 26 | @required 27 | -(void)smileContainerLayoutSubview; 28 | @end 29 | -------------------------------------------------------------------------------- /SmileAuth/Classes/SmilePasswordContainerView.m: -------------------------------------------------------------------------------- 1 | // 2 | // SmilePasswordContainerView.m 3 | // TouchID 4 | // 5 | // Created by yuchen liu on 5/27/15. 6 | // Copyright (c) 2015 rain. All rights reserved. 7 | // 8 | 9 | #import "SmilePasswordContainerView.h" 10 | #import "SmileAuthenticator.h" 11 | 12 | @interface SmilePasswordContainerView() 13 | 14 | @end 15 | 16 | @implementation SmilePasswordContainerView 17 | 18 | -(void)configureAndAddSmilePasswordView{ 19 | 20 | if (self.smilePasswordView) { 21 | [self.smilePasswordView removeFromSuperview]; 22 | self.smilePasswordView = nil; 23 | } 24 | 25 | self.backgroundColor = [UIColor clearColor]; 26 | 27 | NSInteger count = [SmileAuthenticator sharedInstance].passcodeDigit; 28 | 29 | if ([SmileAuthenticator sharedInstance].tintColor) { 30 | self.mainColor = [SmileAuthenticator sharedInstance].tintColor; 31 | } 32 | 33 | self.smilePasswordView = [[SmilePasswordView alloc] initWithCircleColor:self.mainColor circleCount:count frame:self.bounds]; 34 | 35 | [self addSubview:self.smilePasswordView]; 36 | 37 | if (self.delegate) { 38 | [self.delegate smileContainerLayoutSubview]; 39 | } 40 | } 41 | 42 | #if TARGET_INTERFACE_BUILDER 43 | 44 | -(void)willMoveToSuperview:(UIView *)newSuperview{ 45 | [self configureAndAddSmilePasswordView]; 46 | } 47 | 48 | #else 49 | -(void)awakeFromNib{ 50 | [super awakeFromNib]; 51 | } 52 | 53 | -(void)layoutSubviews{ 54 | 55 | [super layoutSubviews]; 56 | 57 | [self configureAndAddSmilePasswordView]; 58 | } 59 | 60 | #endif 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /SmileAuth/Classes/SmilePasswordView.h: -------------------------------------------------------------------------------- 1 | // 2 | // SmilePasswordView.h 3 | // TouchID 4 | // 5 | // Created by yuchen liu on 5/27/15. 6 | // Copyright (c) 2015 rain. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SmilePasswordView : UIView 12 | 13 | @property (nonatomic) UIColor *circleColor; 14 | 15 | @property (nonatomic, strong) NSMutableArray *posiArray; 16 | 17 | @property (nonatomic) NSInteger dotCount; 18 | 19 | -(instancetype)initWithCircleColor:(UIColor*)circleColor circleCount:(NSInteger)count frame:(CGRect)frame; 20 | 21 | -(void)shakeAnimationWithCompletion:(dispatch_block_t)completion; 22 | -(void)slideToLeftAnimationWithCompletion:(dispatch_block_t)completion; 23 | 24 | 25 | @end 26 | -------------------------------------------------------------------------------- /SmileAuth/Classes/SmilePasswordView.m: -------------------------------------------------------------------------------- 1 | // 2 | // SmilePasswordView.m 3 | // TouchID 4 | // 5 | // Created by yuchen liu on 5/27/15. 6 | // Copyright (c) 2015 rain. All rights reserved. 7 | // 8 | 9 | #import "SmilePasswordView.h" 10 | 11 | #define SmileTouchID_DispatchMainThread(block, ...) if(block) dispatch_async(dispatch_get_main_queue(), ^{ block(__VA_ARGS__); }) 12 | 13 | static CGFloat kLineWidthConst = 12.0; 14 | static CGFloat kDotRadiusConst = 5.0; 15 | static CGFloat kMAX_RadiusConst = 32.0; 16 | 17 | @interface SmilePasswordView() 18 | 19 | @property (nonatomic) NSInteger count; 20 | @property (nonatomic) CGFloat radius; 21 | @property (nonatomic) CGFloat spacing; 22 | 23 | @end 24 | 25 | @implementation SmilePasswordView{ 26 | BOOL _direction; 27 | NSInteger _shakeCount; 28 | } 29 | 30 | - (void)drawRect:(CGRect)rect 31 | { 32 | [super drawRect:rect]; 33 | 34 | if (!self.posiArray) { 35 | self.posiArray = [NSMutableArray new]; 36 | } else { 37 | [self.posiArray removeAllObjects]; 38 | } 39 | 40 | CGFloat centerX = CGRectGetMidX(self.bounds); 41 | CGFloat centerY = CGRectGetMidY(self.bounds); 42 | 43 | [self.circleColor setFill]; 44 | // CGContextFillRect(UIGraphicsGetCurrentContext(), rect); 45 | [self.circleColor setStroke]; 46 | 47 | CGFloat lineWidth = self.radius/kLineWidthConst; 48 | CGFloat outLineRadius = self.radius - lineWidth; 49 | 50 | BOOL isOdd = self.count%2; 51 | 52 | if (isOdd) { 53 | //3 54 | int middle = (int)(self.count + 1)/2; 55 | 56 | 57 | for (int i = 1; i <= self.count; i++) { 58 | int ii = middle - i; 59 | 60 | CGFloat theXPosi = centerX - (self.radius * 2 + self.spacing) * ii; 61 | 62 | NSNumber *theNumber = [NSNumber numberWithFloat:theXPosi]; 63 | 64 | [self.posiArray addObject:theNumber]; 65 | } 66 | 67 | } else { 68 | //4 69 | int middle = (int)(self.count)/2; 70 | 71 | 72 | for (int i = 1; i <= self.count; i++) { 73 | int ii = middle - i; 74 | 75 | CGFloat theXPosi = centerX - (self.radius * 2 + self.spacing) * ii - self.radius - self.spacing/2; 76 | 77 | NSNumber *theNumber = [NSNumber numberWithFloat:theXPosi]; 78 | 79 | [self.posiArray addObject:theNumber]; 80 | } 81 | } 82 | 83 | for (int i = 0; i < self.posiArray.count; i++) { 84 | NSNumber *xNumber = self.posiArray[i]; 85 | 86 | CGPoint thePosi = CGPointMake(xNumber.floatValue, centerY); 87 | 88 | UIBezierPath *thePath = [self pathWithCenter:thePosi radius:outLineRadius lineWidth:lineWidth]; 89 | [thePath stroke]; 90 | 91 | CGFloat dotRadius = self.radius/kDotRadiusConst; 92 | 93 | if (i + 1 <= self.dotCount) { 94 | UIBezierPath *dotPath = [UIBezierPath bezierPathWithArcCenter:thePosi radius:dotRadius startAngle:0 endAngle:2 * M_PI clockwise:false]; 95 | [dotPath fill]; 96 | } else { 97 | 98 | } 99 | 100 | } 101 | } 102 | 103 | -(UIBezierPath*)pathWithCenter:(CGPoint)center radius:(CGFloat)radius lineWidth:(CGFloat)lineWidth{ 104 | UIBezierPath *outLinePath = [UIBezierPath bezierPathWithArcCenter:center radius:radius startAngle:0 endAngle:2 * M_PI clockwise:false]; 105 | outLinePath.lineWidth = lineWidth; 106 | 107 | return outLinePath; 108 | } 109 | 110 | -(instancetype)initWithCircleColor:(UIColor*)circleColor circleCount:(NSInteger)count frame:(CGRect)frame{ 111 | 112 | self = [[SmilePasswordView alloc] initWithFrame:frame]; 113 | 114 | self.backgroundColor = [UIColor clearColor]; 115 | self.circleColor = circleColor; 116 | self.count = count; 117 | self.radius = [self getCircleRadius]; 118 | 119 | return self; 120 | } 121 | 122 | -(instancetype)initWithFrame:(CGRect)frame{ 123 | if (self = [super initWithFrame:frame]) { 124 | 125 | } 126 | 127 | return self; 128 | } 129 | 130 | -(id)initWithCoder:(NSCoder *)aDecoder{ 131 | if (self = [super initWithCoder:aDecoder]) { 132 | 133 | } 134 | 135 | return self; 136 | } 137 | 138 | #pragma mark - setter 139 | -(void)setDotCount:(NSInteger)dotCount{ 140 | _dotCount = dotCount; 141 | 142 | [self setNeedsDisplay]; 143 | } 144 | 145 | #pragma mark - getter 146 | 147 | -(CGFloat)spacing{ 148 | 149 | return self.radius/(self.count/2); 150 | } 151 | 152 | #pragma mark - set up 153 | 154 | -(CGFloat)getCircleRadius{ 155 | CGFloat myRadius; 156 | 157 | CGFloat height = CGRectGetHeight(self.bounds); 158 | CGFloat width = CGRectGetWidth(self.bounds); 159 | 160 | if (self.count * height + (self.count - 1) * height/4 > width) { 161 | myRadius = floor((width/(2*self.count + (self.count - 1)/2))); 162 | } else { 163 | myRadius = floor(height/2); 164 | } 165 | 166 | if (myRadius > kMAX_RadiusConst) { 167 | myRadius = kMAX_RadiusConst; 168 | } 169 | 170 | return myRadius; 171 | } 172 | 173 | 174 | #pragma mark - animation 175 | 176 | -(void)shakeAnimationWithCompletion:(dispatch_block_t)completion{ 177 | 178 | NSInteger maxShakeCount = 5; 179 | 180 | CGFloat centerX = CGRectGetMidX(self.bounds); 181 | CGFloat centerY = CGRectGetMidY(self.bounds); 182 | 183 | NSTimeInterval duration = 0.15; 184 | NSInteger moveX = 3; 185 | 186 | if (_shakeCount == 0 || _shakeCount == maxShakeCount) { 187 | duration = duration/2; 188 | moveX = moveX; 189 | } else { 190 | duration = duration; 191 | moveX = 2 * moveX; 192 | } 193 | 194 | [UIView animateWithDuration:duration delay:0.0 usingSpringWithDamping:0.01 initialSpringVelocity:0.35 options:UIViewAnimationOptionCurveEaseInOut animations:^{ 195 | if (!_direction) { 196 | self.center = CGPointMake(centerX + moveX, centerY); 197 | } else { 198 | self.center = CGPointMake(centerX - moveX, centerY); 199 | } 200 | } completion:^(BOOL finished) { 201 | 202 | if (_shakeCount >= maxShakeCount) { 203 | [UIView animateWithDuration:duration delay:0.0 usingSpringWithDamping:0.01 initialSpringVelocity:0.35 options:UIViewAnimationOptionCurveEaseInOut animations:^{ 204 | CGFloat realCenterX = CGRectGetMidX(self.superview.bounds); 205 | self.center = CGPointMake(realCenterX, centerY); 206 | } completion:^(BOOL finished) { 207 | _direction = 0; 208 | _shakeCount = 0; 209 | 210 | SmileTouchID_DispatchMainThread(^(){ 211 | completion(); 212 | }); 213 | }]; 214 | return; 215 | } 216 | 217 | _shakeCount++; 218 | 219 | if (!_direction) { 220 | _direction = 1; 221 | } else { 222 | _direction = 0; 223 | } 224 | 225 | [self shakeAnimationWithCompletion:completion]; 226 | 227 | }]; 228 | } 229 | 230 | -(void)slideToLeftAnimationWithCompletion:(dispatch_block_t)completion{ 231 | 232 | CGFloat centerX = CGRectGetMidX(self.bounds); 233 | CGFloat centerY = CGRectGetMidY(self.bounds); 234 | 235 | [UIView animateWithDuration:0.4 delay:0.0 usingSpringWithDamping:0.9 initialSpringVelocity:5.0 options:UIViewAnimationOptionCurveEaseInOut animations:^{ 236 | if (!_direction) { 237 | self.center = CGPointMake(-centerX, centerY); 238 | } else { 239 | self.center = CGPointMake(centerX, centerY); 240 | } 241 | } completion:^(BOOL finished) { 242 | if (!_direction) { 243 | _direction = 1; 244 | self.center = CGPointMake(3 * centerX, centerY); 245 | [self slideToLeftAnimationWithCompletion:completion]; 246 | } else { 247 | _direction = 0; 248 | 249 | SmileTouchID_DispatchMainThread(^(){ 250 | completion(); 251 | }); 252 | } 253 | }]; 254 | } 255 | 256 | @end 257 | -------------------------------------------------------------------------------- /SmileAuth/Classes/SmileSettingVC.h: -------------------------------------------------------------------------------- 1 | // 2 | // SmileSettingVC.h 3 | // TouchID 4 | // 5 | // Created by ryu-ushin on 5/25/15. 6 | // Copyright (c) 2015 rain. All rights reserved. 7 | // 8 | 9 | #import 10 | @interface SmileSettingVC : UIViewController 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /SmileAuth/Classes/SmileSettingVC.m: -------------------------------------------------------------------------------- 1 | // 2 | // SmileSettingVC.m 3 | // TouchID 4 | // 5 | // Created by ryu-ushin on 5/25/15. 6 | // Copyright (c) 2015 rain. All rights reserved. 7 | // 8 | 9 | #import "SmileSettingVC.h" 10 | #import "SmileAuthenticator.h" 11 | #import "SmilePasswordContainerView.h" 12 | 13 | @interface SmileSettingVC () 14 | 15 | @property (weak, nonatomic) IBOutlet UITextField *passwordField; 16 | @property (weak, nonatomic) IBOutlet UILabel *descLabel; 17 | @property (weak, nonatomic) IBOutlet UIButton *touchIDButton; 18 | @property (weak, nonatomic) IBOutlet SmilePasswordContainerView *passwordView; 19 | @property (weak, nonatomic) IBOutlet UIImageView *bgImageView; 20 | 21 | @end 22 | 23 | @implementation SmileSettingVC{ 24 | BOOL _needTouchID; 25 | BOOL _isAnimating; 26 | NSInteger _inputCount; 27 | NSString *_bufferPassword; 28 | NSString *_newPassword; 29 | NSInteger _passLength; 30 | NSInteger _failCount; 31 | } 32 | 33 | #pragma mark - SmileContainerLayoutDelegate 34 | -(void)smileContainerLayoutSubview{ 35 | self.passwordView.smilePasswordView.dotCount = self.passwordField.text.length; 36 | } 37 | 38 | - (IBAction)dismissSelf:(id)sender { 39 | 40 | [[SmileAuthenticator sharedInstance] authViewControllerWillDismissed]; 41 | 42 | [self dismissViewControllerAnimated:YES completion:^{ 43 | [[SmileAuthenticator sharedInstance] authViewControllerDidDismissed]; 44 | }]; 45 | } 46 | 47 | - (IBAction)useTouchID:(id)sender { 48 | [self touchIDHandle]; 49 | } 50 | 51 | #pragma mark - TouchID handle 52 | -(void)touchIDHandle{ 53 | switch ([SmileAuthenticator sharedInstance].securityType) { 54 | case INPUT_ONCE: 55 | 56 | [self touchIDForINPUT_ONCE]; 57 | 58 | break; 59 | 60 | case INPUT_THREE: 61 | 62 | [self touchIDForINPUT_THREE]; 63 | 64 | break; 65 | 66 | case INPUT_TOUCHID: 67 | 68 | [self touchIDForINPUT_TOUCHID]; 69 | 70 | break; 71 | 72 | default: 73 | break; 74 | } 75 | } 76 | 77 | -(void)showKeyboard{ 78 | dispatch_async(dispatch_get_main_queue(), ^{ 79 | [self.passwordField performSelector:@selector(becomeFirstResponder) withObject:nil afterDelay:0.05]; 80 | }); 81 | } 82 | 83 | -(void)touchIDForINPUT_TOUCHID{ 84 | [SmileAuthenticator sharedInstance].localizedReason = NSLocalizedString(@"SMILE_REASON", nil); 85 | [[SmileAuthenticator sharedInstance] authenticateWithSuccess:^{ 86 | [[SmileAuthenticator sharedInstance] touchID_OR_PasswordAuthSuccess]; 87 | self.passwordView.smilePasswordView.dotCount = [SmileAuthenticator sharedInstance].passcodeDigit; 88 | [self performSelector:@selector(dismissSelf:) withObject:nil afterDelay:0.15]; 89 | } andFailure:^(LAError errorCode) { 90 | [self showKeyboard]; 91 | }]; 92 | } 93 | 94 | -(void)touchIDForINPUT_ONCE{ 95 | [SmileAuthenticator sharedInstance].localizedReason = NSLocalizedString(@"SMILE_INPUT_ONCE_TITLE", nil); 96 | [[SmileAuthenticator sharedInstance] authenticateWithSuccess:^{ 97 | [[SmileAuthenticator sharedInstance] touchID_OR_PasswordTurnOff]; 98 | self.passwordView.smilePasswordView.dotCount = [SmileAuthenticator sharedInstance].passcodeDigit; 99 | [self performSelector:@selector(passwordCancleComplete) withObject:nil afterDelay:0.15]; 100 | } andFailure:^(LAError errorCode) { 101 | [self showKeyboard]; 102 | }]; 103 | } 104 | 105 | -(void)touchIDForINPUT_THREE{ 106 | [SmileAuthenticator sharedInstance].localizedReason = NSLocalizedString(@"SMILE_INPUT_THREE_TITLE", nil); 107 | [[SmileAuthenticator sharedInstance] authenticateWithSuccess:^{ 108 | self.passwordView.smilePasswordView.dotCount = [SmileAuthenticator sharedInstance].passcodeDigit; 109 | _inputCount ++; 110 | [self performSelector:@selector(enterNewPassword) withObject:nil afterDelay:0.15]; 111 | } andFailure:^(LAError errorCode) { 112 | [self showKeyboard]; 113 | }]; 114 | } 115 | 116 | -(void)viewDidAppear:(BOOL)animated{ 117 | [super viewDidAppear:animated]; 118 | 119 | if (_needTouchID) { 120 | [self useTouchID:nil]; 121 | } 122 | } 123 | 124 | - (void)viewDidLoad { 125 | [super viewDidLoad]; 126 | 127 | if ([SmileAuthenticator sharedInstance].navibarTranslucent) { 128 | [self.navigationController.navigationBar setBackgroundImage:[UIImage new] forBarMetrics:UIBarMetricsDefault]; 129 | [self.navigationController.navigationBar setShadowImage:[UIImage new]]; 130 | [self.navigationController.navigationBar setTranslucent:YES]; 131 | } 132 | 133 | if ([SmileAuthenticator sharedInstance].nightMode) { 134 | [self.navigationController.navigationBar setBarStyle:UIBarStyleBlack]; 135 | [self.passwordField setKeyboardAppearance:UIKeyboardAppearanceDark]; 136 | self.view.backgroundColor = [UIColor blackColor]; 137 | self.descLabel.textColor = [UIColor whiteColor]; 138 | } 139 | 140 | if ([SmileAuthenticator sharedInstance].parallaxMode) { 141 | [self registerEffectForView:self.passwordView depth:15]; 142 | } 143 | 144 | if ([SmileAuthenticator sharedInstance].descriptionTextColor) { 145 | self.descLabel.textColor = [SmileAuthenticator sharedInstance].descriptionTextColor; 146 | } 147 | 148 | if ([SmileAuthenticator sharedInstance].backgroundImage) { 149 | self.bgImageView.image = [SmileAuthenticator sharedInstance].backgroundImage; 150 | } 151 | 152 | self.passwordView.delegate = self; 153 | 154 | //for tint color 155 | if ([SmileAuthenticator sharedInstance].tintColor) { 156 | self.navigationController.navigationBar.tintColor = [SmileAuthenticator sharedInstance].tintColor; 157 | } 158 | 159 | //for touchid image 160 | UIImage *iconImage = [UIImage imageNamed:[SmileAuthenticator sharedInstance].touchIDIconName]; 161 | [self.touchIDButton setImage:iconImage forState:UIControlStateNormal]; 162 | 163 | self.descLabel.text = [NSString stringWithFormat:NSLocalizedString(@"SMILE_INPUT_DESCRIPTION", nil), (long)[SmileAuthenticator sharedInstance].passcodeDigit]; 164 | 165 | switch ([SmileAuthenticator sharedInstance].securityType) { 166 | case INPUT_ONCE: 167 | self.touchIDButton.hidden = NO; 168 | self.navigationItem.title = NSLocalizedString(@"SMILE_INPUT_ONCE_TITLE", nil); 169 | 170 | break; 171 | 172 | case INPUT_TWICE: 173 | 174 | self.navigationItem.title = NSLocalizedString(@"SMILE_INPUT_TWICE_TITLE", nil); 175 | 176 | break; 177 | 178 | case INPUT_THREE: 179 | self.touchIDButton.hidden = NO; 180 | self.navigationItem.title = NSLocalizedString(@"SMILE_INPUT_THREE_TITLE", nil); 181 | self.descLabel.text = [NSString stringWithFormat:NSLocalizedString(@"SMILE_INPUT_THREE_STEP_1_DESCRIPTION", nil), (long)[SmileAuthenticator sharedInstance].passcodeDigit]; 182 | 183 | break; 184 | 185 | case INPUT_TOUCHID: 186 | 187 | self.touchIDButton.hidden = NO; 188 | 189 | if (![SmileAuthenticator sharedInstance].appLogoName.length) { 190 | self.navigationItem.title = NSLocalizedString(@"SMILE_INPUT_TOUCHID_TITLE", nil); 191 | } else { 192 | self.navigationItem.titleView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:[SmileAuthenticator sharedInstance].appLogoName]]; 193 | } 194 | 195 | break; 196 | 197 | default: 198 | break; 199 | } 200 | 201 | //hide bar button 202 | if ([SmileAuthenticator sharedInstance].securityType == INPUT_TOUCHID) { 203 | if (self.navigationItem.rightBarButtonItem) { 204 | [self.navigationItem.rightBarButtonItem setTintColor:[UIColor clearColor]]; 205 | [self.navigationItem.rightBarButtonItem setEnabled:NO]; 206 | } 207 | 208 | //begin check canAuthenticate 209 | NSError *error = nil; 210 | if ([SmileAuthenticator canAuthenticateWithError:&error]) { 211 | _needTouchID = YES; 212 | } else { 213 | self.touchIDButton.hidden = YES; 214 | [self.passwordField becomeFirstResponder]; 215 | } 216 | 217 | } else if ([SmileAuthenticator sharedInstance].securityType == INPUT_ONCE | [SmileAuthenticator sharedInstance].securityType == INPUT_THREE) { 218 | 219 | //begin check canAuthenticate 220 | NSError *error = nil; 221 | if ([SmileAuthenticator canAuthenticateWithError:&error]) { 222 | _needTouchID = YES; 223 | } else { 224 | self.touchIDButton.hidden = YES; 225 | [self.passwordField becomeFirstResponder]; 226 | } 227 | } 228 | 229 | else { 230 | [self.passwordField becomeFirstResponder]; 231 | } 232 | 233 | self.passwordField.delegate = self; 234 | [self.passwordField addTarget:self action:@selector(textFieldDidChange:) forControlEvents:UIControlEventEditingChanged]; 235 | 236 | _passLength =[SmileAuthenticator sharedInstance].passcodeDigit; 237 | } 238 | 239 | - (void)didReceiveMemoryWarning { 240 | [super didReceiveMemoryWarning]; 241 | // Dispose of any resources that can be recreated. 242 | } 243 | 244 | #pragma mark - animation 245 | 246 | -(void)slideAnimation{ 247 | _isAnimating = YES; 248 | 249 | if (!self.touchIDButton.hidden) { 250 | self.touchIDButton.hidden = YES; 251 | } 252 | 253 | [self.passwordView.smilePasswordView slideToLeftAnimationWithCompletion:^{ 254 | _isAnimating = NO; 255 | if(![self.passwordField isFirstResponder]){ 256 | [self.passwordField becomeFirstResponder]; 257 | }; 258 | }]; 259 | 260 | } 261 | 262 | -(void)shakeAnimation{ 263 | _isAnimating = YES; 264 | [self.passwordView.smilePasswordView shakeAnimationWithCompletion:^{ 265 | _isAnimating = NO; 266 | }]; 267 | } 268 | 269 | #pragma mark - handle user input 270 | -(void)clearText { 271 | self.passwordField.text = @""; 272 | self.passwordView.smilePasswordView.dotCount = 0; 273 | } 274 | 275 | -(void)passwordInputComplete{ 276 | [[SmileAuthenticator sharedInstance] userSetPassword: _newPassword]; 277 | [self dismissSelf:nil]; 278 | } 279 | 280 | -(void)passwordCancleComplete{ 281 | [SmileAuthenticator clearPassword]; 282 | [self dismissSelf:nil]; 283 | } 284 | 285 | -(void)passwordWrong{ 286 | _inputCount = 0; 287 | 288 | [self clearText]; 289 | 290 | _failCount++; 291 | 292 | [self shakeAnimation]; 293 | 294 | [[SmileAuthenticator sharedInstance] touchID_OR_PasswordAuthFail:_failCount]; 295 | 296 | self.descLabel.text = [NSString stringWithFormat:NSLocalizedString(@"SMILE_INPUT_FAILED", nil), (long)_failCount]; 297 | } 298 | 299 | -(void)passwordNotMatch{ 300 | 301 | _inputCount = _inputCount -2; 302 | 303 | [self clearText]; 304 | [self shakeAnimation]; 305 | 306 | self.descLabel.text = NSLocalizedString(@"SMILE_INPUT_NOT_MATCH", nil); 307 | } 308 | 309 | -(void)reEnterPassword{ 310 | 311 | _bufferPassword = _newPassword; 312 | [self clearText]; 313 | 314 | [self slideAnimation]; 315 | 316 | self.descLabel.text = [NSString stringWithFormat:NSLocalizedString(@"SMILE_INPUT_RE-ENTER", nil), (long)[SmileAuthenticator sharedInstance].passcodeDigit]; 317 | } 318 | 319 | -(void)enterNewPassword{ 320 | [self clearText]; 321 | [self slideAnimation]; 322 | self.descLabel.text = [NSString stringWithFormat:NSLocalizedString(@"SMILE_INPUT_THREE_STEP_2_DESCRIPTION", nil), (long)[SmileAuthenticator sharedInstance].passcodeDigit]; 323 | } 324 | 325 | -(void)handleINPUT_TOUCHID{ 326 | if ([SmileAuthenticator isSamePassword:_newPassword]) { 327 | [[SmileAuthenticator sharedInstance] touchID_OR_PasswordAuthSuccess]; 328 | [self passwordInputComplete]; 329 | } else { 330 | [self passwordWrong]; 331 | } 332 | } 333 | 334 | -(void)handleINPUT_ONCE{ 335 | if ([SmileAuthenticator isSamePassword:_newPassword]) { 336 | [[SmileAuthenticator sharedInstance] touchID_OR_PasswordTurnOff]; 337 | [self passwordCancleComplete]; 338 | } else { 339 | [self passwordWrong]; 340 | } 341 | } 342 | 343 | -(void)handleINPUT_TWICE{ 344 | if (_inputCount == 1) { 345 | [self reEnterPassword]; 346 | } else if (_inputCount == 2) { 347 | if ([_bufferPassword isEqualToString:_newPassword]) { 348 | [[SmileAuthenticator sharedInstance] touchID_OR_PasswordTurnOn]; 349 | [self passwordInputComplete]; 350 | } else { 351 | [self passwordNotMatch]; 352 | } 353 | } 354 | } 355 | 356 | -(void)handleINPUT_THREE{ 357 | if (_inputCount == 1) { 358 | if ([SmileAuthenticator isSamePassword:_newPassword]) { 359 | [self enterNewPassword]; 360 | } else { 361 | [self passwordWrong]; 362 | } 363 | } else if (_inputCount == 2) { 364 | [self reEnterPassword]; 365 | } else if (_inputCount == 3) { 366 | if ([_bufferPassword isEqualToString:_newPassword]) { 367 | [[SmileAuthenticator sharedInstance] touchID_OR_PasswordChange]; 368 | [self passwordInputComplete]; 369 | } else { 370 | [self passwordNotMatch]; 371 | } 372 | } 373 | } 374 | 375 | -(void)handleUserInput{ 376 | switch ([SmileAuthenticator sharedInstance].securityType) { 377 | case INPUT_ONCE: 378 | 379 | [self handleINPUT_ONCE]; 380 | 381 | break; 382 | 383 | case INPUT_TWICE: 384 | 385 | _inputCount++; 386 | 387 | [self handleINPUT_TWICE]; 388 | 389 | break; 390 | 391 | case INPUT_THREE: 392 | 393 | _inputCount++; 394 | 395 | [self handleINPUT_THREE]; 396 | 397 | break; 398 | 399 | case INPUT_TOUCHID: 400 | 401 | [self handleINPUT_TOUCHID]; 402 | 403 | break; 404 | 405 | default: 406 | break; 407 | } 408 | 409 | } 410 | 411 | #pragma mark - UITextFieldDelegate 412 | 413 | -(void)textFieldDidChange:(UITextField*)textField{ 414 | 415 | self.passwordView.smilePasswordView.dotCount = textField.text.length; 416 | 417 | if (textField.text.length == _passLength) { 418 | 419 | _newPassword = textField.text; 420 | 421 | [self performSelector:@selector(handleUserInput) withObject:nil afterDelay:0.3]; 422 | } 423 | } 424 | 425 | -(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{ 426 | 427 | if (textField.text.length >= _passLength) { 428 | return NO; 429 | } 430 | 431 | return !_isAnimating; 432 | } 433 | 434 | 435 | #pragma mark - PrivateMethod - Parallax 436 | 437 | - (void)registerEffectForView:(UIView *)aView depth:(CGFloat)depth; 438 | { 439 | UIInterpolatingMotionEffect *effectX; 440 | UIInterpolatingMotionEffect *effectY; 441 | effectX = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.x" 442 | type:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis]; 443 | effectY = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.y" 444 | type:UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis]; 445 | 446 | 447 | effectX.maximumRelativeValue = @(depth); 448 | effectX.minimumRelativeValue = @(-depth); 449 | effectY.maximumRelativeValue = @(depth); 450 | effectY.minimumRelativeValue = @(-depth); 451 | 452 | UIMotionEffectGroup *group = [[UIMotionEffectGroup alloc] init]; 453 | group.motionEffects =@[effectX, effectY]; 454 | 455 | [aView addMotionEffect:group] ; 456 | } 457 | 458 | @end 459 | -------------------------------------------------------------------------------- /SmileTouchID.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "SmileTouchID" 3 | s.version = "0.1.8" 4 | s.summary = "A Library for configure Touch ID & passcode conveniently" 5 | s.description = <<-DESC 6 | 1. Handle all complicated things about Touch ID & Passcode. You just need to write a few simple code to integrate Touch ID & Passcode to your app. 7 | 2. Get elegant animation automatically and adaptive UI. 8 | 3. Can customize the color,touch id icon and background image to fit your app style. 9 | 4. Can customize the passcode digit to 6 or 10, or any number, automatically handle other things for you. 10 | 5. Support iOS7 and later. 11 | DESC 12 | 13 | s.homepage = "https://github.com/liu044100/SmileTouchID" 14 | s.screenshots = "https://raw.githubusercontent.com/liu044100/SmileTouchID/master/Example/demo_gif/demo1.gif", "https://raw.githubusercontent.com/liu044100/SmileTouchID/master/Example/demo_gif/demo2.gif" 15 | s.license = "MIT" 16 | 17 | s.author = { 'Rain' => 'liu044100@gmail.com' } 18 | s.social_media_url = "https://dribbble.com/yuchenliu" 19 | 20 | 21 | s.platform = :ios, '7.0' 22 | s.requires_arc = true 23 | s.source = { :git => "https://github.com/liu044100/SmileTouchID.git", :tag => s.version.to_s} 24 | s.source_files = 'SmileAuth/Classes/*' 25 | s.resource = ['SmileAuth/Assets/*'] 26 | s.public_header_files = 'SmileAuth/Classes/*.h' 27 | s.frameworks = 'UIKit' 28 | s.weak_framework = 'LocalAuthentication' 29 | 30 | end 31 | --------------------------------------------------------------------------------