Skip to content

Commit c1d216e

Browse files
committed
1.0.0
1 parent 5b06bfa commit c1d216e

27 files changed

+2108
-0
lines changed

PasscodeKit.podspec

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
Pod::Spec.new do |s|
2+
s.name = 'PasscodeKit'
3+
s.version = '1.0.0'
4+
s.license = 'MIT'
5+
6+
s.summary = 'A lightweight and easy-to-use Passcode Kit for iOS.'
7+
s.homepage = 'https://relatedcode.com'
8+
s.author = { 'Related Code' => 'info@relatedcode.com' }
9+
10+
s.source = { :git => 'https://github.com/relatedcode/PasscodeKit.git', :tag => s.version }
11+
s.source_files = 'PasscodeKit/Sources/*.swift'
12+
13+
s.pod_target_xcconfig = { 'SWIFT_VERSION' => '5.0' }
14+
15+
s.swift_version = '5.0'
16+
s.platform = :ios, '12.0'
17+
s.requires_arc = true
18+
end
Lines changed: 292 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,292 @@
1+
//
2+
// Copyright (c) 2021 Related Code - https://relatedcode.com
3+
//
4+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
5+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
6+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
7+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
8+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
9+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
10+
// THE SOFTWARE.
11+
12+
import UIKit
13+
import CryptoKit
14+
15+
//-----------------------------------------------------------------------------------------------------------------------------------------------
16+
@objc public protocol PasscodeKitDelegate {
17+
18+
@objc optional func passcodeCreated(_ passcode: String)
19+
@objc optional func passcodeChanged(_ passcode: String)
20+
@objc optional func passcodeRemoved()
21+
22+
@objc optional func passcodeCheckedButDisabled()
23+
@objc optional func passcodeEnteredSuccessfully()
24+
@objc optional func passcodeMaximumFailedAttempts()
25+
}
26+
27+
//-----------------------------------------------------------------------------------------------------------------------------------------------
28+
public class PasscodeKit: NSObject {
29+
30+
static let shared: PasscodeKit = {
31+
let instance = PasscodeKit()
32+
return instance
33+
}()
34+
35+
static var passcodeLength = 4
36+
static var allowedFailedAttempts = 3
37+
38+
static var textColor = UIColor.darkText
39+
static var backgroundColor = UIColor.lightGray
40+
41+
static var failedTextColor = UIColor.white
42+
static var failedBackgroundColor = UIColor.systemRed
43+
44+
static var titleEnterPasscode = "Enter Passcode"
45+
static var titleCreatePasscode = "Create Passcode"
46+
static var titleChangePasscode = "Change Passcode"
47+
static var titleRemovePasscode = "Remove Passcode"
48+
49+
static var textEnterPasscode = "Enter your passcode"
50+
static var textVerifyPasscode = "Verify your passcode"
51+
static var textEnterOldPasscode = "Enter your old passcode"
52+
static var textEnterNewPasscode = "Enter your new passcode"
53+
static var textVerifyNewPasscode = "Verify your new passcode"
54+
static var textFailedPasscode = "%d Failed Passcode Attempts"
55+
static var textPasscodeMismatch = "Passcodes did not match. Try again."
56+
static var textTouchIDAccessReason = "Please use Touch ID to unlock the app"
57+
58+
public static var delegate: PasscodeKitDelegate?
59+
60+
//-------------------------------------------------------------------------------------------------------------------------------------------
61+
public override init() {
62+
63+
super.init()
64+
65+
if #available(iOS 13.0, *) {
66+
PasscodeKit.textColor = UIColor.label
67+
PasscodeKit.backgroundColor = UIColor.systemGroupedBackground
68+
} else {
69+
PasscodeKit.backgroundColor = UIColor.groupTableViewBackground
70+
}
71+
}
72+
73+
//-------------------------------------------------------------------------------------------------------------------------------------------
74+
public class func start() {
75+
76+
shared.start()
77+
}
78+
79+
//-------------------------------------------------------------------------------------------------------------------------------------------
80+
public class func dismiss() {
81+
82+
if (PasscodeKit.enabled()) {
83+
if let navigationController = shared.topViewController() as? UINavigationController {
84+
if let presentedView = navigationController.viewControllers.first {
85+
if (presentedView is PasscodeKitVerify) {
86+
presentedView.dismiss(animated: true)
87+
}
88+
}
89+
}
90+
}
91+
}
92+
}
93+
94+
//-----------------------------------------------------------------------------------------------------------------------------------------------
95+
extension PasscodeKit {
96+
97+
//-------------------------------------------------------------------------------------------------------------------------------------------
98+
private func start() {
99+
100+
let didFinishLaunching = UIApplication.didFinishLaunchingNotification
101+
let willEnterForeground = UIApplication.willEnterForegroundNotification
102+
103+
NotificationCenter.default.addObserver(self, selector: #selector(verifyPasscode), name: didFinishLaunching, object: nil)
104+
NotificationCenter.default.addObserver(self, selector: #selector(verifyPasscode), name: willEnterForeground, object: nil)
105+
}
106+
107+
//-------------------------------------------------------------------------------------------------------------------------------------------
108+
@objc private func verifyPasscode() {
109+
110+
if (PasscodeKit.enabled()) {
111+
if let viewController = topViewController() {
112+
if (noPasscodePresented(viewController)) {
113+
presentPasscodeVerify(viewController)
114+
}
115+
}
116+
} else {
117+
PasscodeKit.delegate?.passcodeCheckedButDisabled?()
118+
}
119+
}
120+
121+
//-------------------------------------------------------------------------------------------------------------------------------------------
122+
private func presentPasscodeVerify(_ viewController: UIViewController) {
123+
124+
DispatchQueue.main.async {
125+
let passcodeKitVerify = PasscodeKitVerify()
126+
passcodeKitVerify.delegate = PasscodeKit.delegate
127+
let navController = PasscodeKitNavController(rootViewController: passcodeKitVerify)
128+
viewController.present(navController, animated: false)
129+
}
130+
}
131+
132+
//-------------------------------------------------------------------------------------------------------------------------------------------
133+
private func noPasscodePresented(_ viewController: UIViewController) -> Bool {
134+
135+
var result = true
136+
if let navigationController = viewController as? UINavigationController {
137+
if let presentedView = navigationController.viewControllers.first {
138+
if (presentedView is PasscodeKitCreate) { result = false }
139+
if (presentedView is PasscodeKitChange) { result = false }
140+
if (presentedView is PasscodeKitRemove) { result = false }
141+
if (presentedView is PasscodeKitVerify) { result = false }
142+
}
143+
}
144+
return result
145+
}
146+
147+
//-------------------------------------------------------------------------------------------------------------------------------------------
148+
private func topViewController() -> UIViewController? {
149+
150+
var keyWindow: UIWindow?
151+
152+
if #available(iOS 13.0, *) {
153+
keyWindow = UIApplication.shared.windows.first { $0.isKeyWindow }
154+
} else {
155+
keyWindow = UIApplication.shared.keyWindow
156+
}
157+
158+
var viewController = keyWindow?.rootViewController
159+
while (viewController?.presentedViewController != nil) {
160+
viewController = viewController?.presentedViewController
161+
}
162+
return viewController
163+
}
164+
}
165+
166+
//-----------------------------------------------------------------------------------------------------------------------------------------------
167+
extension PasscodeKit {
168+
169+
//-------------------------------------------------------------------------------------------------------------------------------------------
170+
public class func createPasscode(_ viewController: UIViewController) {
171+
172+
let passcodeKitCreate = PasscodeKitCreate()
173+
passcodeKitCreate.delegate = viewController as? PasscodeKitDelegate
174+
let navController = PasscodeKitNavController(rootViewController: passcodeKitCreate)
175+
viewController.present(navController, animated: true)
176+
}
177+
178+
//-------------------------------------------------------------------------------------------------------------------------------------------
179+
public class func changePasscode(_ viewController: UIViewController) {
180+
181+
let passcodeKitChange = PasscodeKitChange()
182+
passcodeKitChange.delegate = viewController as? PasscodeKitDelegate
183+
let navController = PasscodeKitNavController(rootViewController: passcodeKitChange)
184+
viewController.present(navController, animated: true)
185+
}
186+
187+
//-------------------------------------------------------------------------------------------------------------------------------------------
188+
public class func removePasscode(_ viewController: UIViewController) {
189+
190+
let passcodeKitRemove = PasscodeKitRemove()
191+
passcodeKitRemove.delegate = viewController as? PasscodeKitDelegate
192+
let navController = PasscodeKitNavController(rootViewController: passcodeKitRemove)
193+
viewController.present(navController, animated: true)
194+
}
195+
}
196+
197+
// MARK: - Passcode methods
198+
//-----------------------------------------------------------------------------------------------------------------------------------------------
199+
extension PasscodeKit {
200+
201+
//-------------------------------------------------------------------------------------------------------------------------------------------
202+
public class func enabled() -> Bool {
203+
204+
return (UserDefaults.standard.string(forKey: "PasscodeValue") != nil)
205+
}
206+
207+
//-------------------------------------------------------------------------------------------------------------------------------------------
208+
public class func verify(_ passcode: String) -> Bool {
209+
210+
if (passcode != "") {
211+
return (UserDefaults.standard.string(forKey: "PasscodeValue") == sha256(passcode))
212+
}
213+
return (UserDefaults.standard.string(forKey: "PasscodeValue") == nil)
214+
}
215+
216+
//-------------------------------------------------------------------------------------------------------------------------------------------
217+
public class func update(_ passcode: String) {
218+
219+
if (passcode != "") {
220+
UserDefaults.standard.set(sha256(passcode), forKey: "PasscodeValue")
221+
} else {
222+
UserDefaults.standard.removeObject(forKey: "PasscodeValue")
223+
UserDefaults.standard.removeObject(forKey: "PasscodeBiometric")
224+
}
225+
}
226+
227+
//-------------------------------------------------------------------------------------------------------------------------------------------
228+
public class func remove() {
229+
230+
UserDefaults.standard.removeObject(forKey: "PasscodeValue")
231+
UserDefaults.standard.removeObject(forKey: "PasscodeBiometric")
232+
}
233+
234+
//-------------------------------------------------------------------------------------------------------------------------------------------
235+
public class func biometric() -> Bool {
236+
237+
return UserDefaults.standard.bool(forKey: "PasscodeBiometric")
238+
}
239+
240+
//-------------------------------------------------------------------------------------------------------------------------------------------
241+
public class func biometric(_ value: Bool) {
242+
243+
UserDefaults.standard.set(value, forKey: "PasscodeBiometric")
244+
}
245+
246+
//-------------------------------------------------------------------------------------------------------------------------------------------
247+
private class func sha256(_ text: String) -> String {
248+
249+
if #available(iOS 13.0, *) {
250+
let data = Data(text.utf8)
251+
let hash = SHA256.hash(data: data)
252+
return hash.compactMap { String(format: "%02x", $0) }.joined()
253+
}
254+
return text
255+
}
256+
}
257+
258+
// MARK: - PasscodeKitNavController
259+
//-----------------------------------------------------------------------------------------------------------------------------------------------
260+
class PasscodeKitNavController: UINavigationController {
261+
262+
//-------------------------------------------------------------------------------------------------------------------------------------------
263+
override func viewDidLoad() {
264+
265+
super.viewDidLoad()
266+
267+
if #available(iOS 13.0, *) {
268+
self.isModalInPresentation = true
269+
self.modalPresentationStyle = .fullScreen
270+
}
271+
272+
navigationBar.isTranslucent = false
273+
}
274+
275+
//-------------------------------------------------------------------------------------------------------------------------------------------
276+
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
277+
278+
return .portrait
279+
}
280+
281+
//-------------------------------------------------------------------------------------------------------------------------------------------
282+
override var preferredInterfaceOrientationForPresentation: UIInterfaceOrientation {
283+
284+
return .portrait
285+
}
286+
287+
//-------------------------------------------------------------------------------------------------------------------------------------------
288+
override var shouldAutorotate: Bool {
289+
290+
return false
291+
}
292+
}

0 commit comments

Comments
 (0)