├── .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 | [](https://github.com/liu044100/SmileTouchID/issues)
4 | [](http://cocoadocs.org/docsets/SmileTouchID)
5 | [](http://cocoadocs.org/docsets/SmileTouchID)
6 | [](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 | ```
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 | 
36 | 
37 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 | 
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 |
52 |
53 |
54 |
55 |
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 | [](https://github.com/liu044100/SmileTouchID/issues)
4 | [](http://cocoadocs.org/docsets/SmileTouchID)
5 | [](http://cocoadocs.org/docsets/SmileTouchID)
6 | [](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 | 
36 | 
37 | 
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 | 
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 | 
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 | 
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 |
52 |
53 |
54 |
55 |
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 |
--------------------------------------------------------------------------------