투케이2K

130. (ios/swift) Touch ID , Face ID 생체 간편 인증 수행 실시 - 얼굴 인식 , 지문 인식 본문

IOS

130. (ios/swift) Touch ID , Face ID 생체 간편 인증 수행 실시 - 얼굴 인식 , 지문 인식

투케이2K 2022. 3. 16. 13:06
반응형

[개발 환경 설정]

개발 툴 : XCODE

개발 언어 : SWIFT

 

[사전 설정 필요]

 

[ViewController]

import UIKit

class ViewController: UIViewController {
    
    
    // MARK: [뷰 메모리 로드 수행 실시]
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        print("")
        print("===============================")
        print("[ViewController >> viewDidLoad() :: 뷰 메모리 로드 실시]")
        print("===============================")
        print("")
        
        
        // [앱 딜리게이트 노티피케이션 알림 확인 등록]
        NotificationCenter.default.addObserver(
            self, // 뷰 지정
            selector: #selector(simpleAuthNotification(_:)), // userInfo 데이터 있는 경우 : @objc func appDelegateCall(_ notification:NSNotification) {}
            name: NSNotification.Name("simpleAuthNotification"), // 알림 식별자
            object: nil // 객체
        )
    }
    
    
    
    // MARK: - [뷰 종료 상태]
    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        print("")
        print("===============================")
        print("[ViewController >> viewDidDisappear() :: 뷰 종료 상태]")
        print("===============================")
        print("")
        
            
        // [앱 딜리게이트 노티피케이션 알림 확인 해제]
        NotificationCenter.default.removeObserver(
            self, // 뷰 지정
            name: NSNotification.Name("simpleAuthNotification"), // 알림 해제
            object: nil
        )
    }
    
    
    
    // MARK: - [버튼 클릭 이벤트 정의 실시]
    @IBAction func buttonAction(_ sender: Any) {
        
        // [생체 인증 수행 실시]
        self.simpleAuth()
    }
    
    
    
    // MARK: - [생체 인증 수행 실시]
    func simpleAuth(){
        print("")
        print("===============================")
        print("[ViewController >> simpleAuth() : 생체 인증 수행 실시]")
        print("===============================")
        print("")
        
        DispatchQueue.main.async {
            
            // [생체 인증 클래스 객체 생성 실시]
            let a_auth = A_Auth()
            
            
            // [생체 인증 수행 실시]
            if #available(iOS 11.0, *) { // MARK: [특정 ios 버전 이상 사용 가능]
                
                // MARK: [생체 인증 사용 가능 여부 확인]
                let canEvaluateDic : Dictionary<String, Any> = a_auth.canEvaluate()
                
                print("")
                print("===============================")
                print("[ViewController >> simpleAuth() :: 생체 인증 사용 여부 확인]")
                print("결과 :: Face ID / Touch ID 사용 가능 여부 확인")
                print("canEvaluateDic :: \(canEvaluateDic)")
                print("===============================")
                print("")
                
                if (canEvaluateDic["result"] != nil) == true { // [생체 인증 사용 가능한 경우]
                    
                    // MARK: [생체 인증 수행 실시]
                    a_auth.evaluate()
                }
                else { // [생체 인증 사용 불가능한 경우]
                    
                    // [팝업창 알림 표시 수행 실시]
                    self.showAlert(
                        tittle: "생체 사용 가능 여부",
                        content: "\(canEvaluateDic["msg"]!)",
                        okBtb: "확인",
                        noBtn: "취소"
                    )
                }
                    
            } else {
                print("")
                print("===============================")
                print("[ViewController >> simpleAuth() :: 생체 인증 사용 여부 확인]")
                print("결과 :: IOS 버전 11 미만 >> 생체 인증 사용 제한 디바이스")
                print("===============================")
                print("")
            }
        }
    }
    
    
    
    // MARK: - [생체 인증 수행 결과 메시지 확인 실시]
    @objc func simpleAuthNotification(_ notification:NSNotification) {
        print("")
        print("===============================")
        print("[ViewController >> simpleAuthNotification() :: 생체 인증 노티피케이션 수신 확인]")
        print("message :: \(notification.userInfo!["message"]!)")
        print("===============================")
        print("")
        
        // [팝업창 알림 표시 수행 실시]
        self.showAlert(
            tittle: "생체 인증 결과",
            content: "\(notification.userInfo!["message"]!)",
            okBtb: "확인",
            noBtn: "취소"
        )
    }
    
    
    
    // MARK: [alert 팝업창 호출 메소드 정의 실시 : 이벤트 호출 시]
    // 호출 방법 : showAlert(tittle: "title", content: "content", okBtb: "확인", noBtn: "취소")
    func showAlert(tittle:String, content:String, okBtb:String, noBtn:String) {
        DispatchQueue.main.async {
            
            // [UIAlertController 객체 정의 실시]
            let alert = UIAlertController(title: tittle, message: content, preferredStyle: UIAlertController.Style.alert)
            
            // [인풋으로 들어온 확인 버튼이 nil 아닌 경우]
            if(okBtb != "" && okBtb.count>0){
                let okAction = UIAlertAction(title: okBtb, style: .default) { (action) in
                    // [확인 버튼 클릭 이벤트 내용 정의 실시]
                    return
                }
                alert.addAction(okAction) // 버튼 클릭 이벤트 객체 연결
            }
            
            // [인풋으로 들어온 취소 버튼이 nil 아닌 경우]
            if(noBtn != "" && noBtn.count>0){
                let noAction = UIAlertAction(title: noBtn, style: .default) { (action) in
                    // [취소 버튼 클릭 이벤트 내용 정의 실시]
                    return
                }
                alert.addAction(noAction) // 버튼 클릭 이벤트 객체 연결
            }
            
            // [alert 팝업창 활성 실시]
            self.present(alert, animated: false, completion: nil)
        }
    }


} // [클래스 종료]
 

[A_Auth]

import UIKit

// MARK: [import 추가 실시]
import LocalAuthentication



class A_Auth {
    
    
    
    // MARK: [클래스 설명]
    /*
    1. A_Auth : 생체 인증 수행 액티비티
    2. info.plist 권한 등록 필요 :
       - Privacy - Face ID Usage Description
    3. import 추가 필요 :
       - import LocalAuthentication
    */
    
    
    
    
    
    // MARK: - [전역 변수 선언 실시]
    private let context = LAContext() // [생체 인증 객체]
    private let policy: LAPolicy // [deviceOwnerAuthenticationWithBiometrics 객체]
    private let localizedReason: String // [생체 인증 팝업창 내용]
    private var error: NSError? // [에러 발생 타입]
    
    static let POPUP_CONTENT = "생체 인증을 수행합니다." // [생체 인증 팝업창 내용]
    static let POPUP_FALLBACK = "" // [생체 인증 팝업창 버튼 내용]
    static let POPUP_CANCLE = "취소" // [생체 인증 팝업창 버튼 내용]
    
    static let ERROR_CHECK_FAIL = "ERROR CHECK FAIL" // [에러 체크 실패 멘트 저장]
    static let CHECK_SUCCESS = "SUCCESS" // [생체 인증 사용 여부 및 생체 인증 결과 성공 멘트 저장]
    
    
    
    
    
    // MARK: - [클래스 생성자 초기화 실시 : 생체 인증 팝업창 내용 표시 설정]
    init(policy: LAPolicy = .deviceOwnerAuthenticationWithBiometrics,
         localizedReason: String = A_Auth.POPUP_CONTENT,
         localizedFallbackTitle: String = A_Auth.POPUP_FALLBACK,
         localizedCancelTitle: String = A_Auth.POPUP_CANCLE) {
        
        // [전역 변수에 매핑 실시]
        self.policy = policy
        self.localizedReason = localizedReason
        self.context.localizedFallbackTitle = localizedFallbackTitle
        self.context.localizedCancelTitle = localizedCancelTitle
    }
    
    
    
    
    
    // MARK: - [생체 인증 타입 정의]
    enum BiometricType {
        case none
        case touchID
        case faceID
        case unknown
    }
    @available(iOS 11.0, *) // [특정 IOS 버전 이상 사용 가능]
    private func biometricType(for type: LABiometryType) -> BiometricType {
        switch type {
        case .none:
            return .none
        
        case .touchID:
            return .touchID
        
        case .faceID:
            return .faceID
        
        @unknown default:
            return .unknown
        }
    }
    
    
    
    
    
    // MARK: - [생체 인증 에러 메시지 리턴 수행 부분]
    @available(iOS 11.0, *) // [특정 IOS 버전 이상 사용 가능]
    private func biometricError(from nsError: NSError) -> (Int, String) {
        
        var retry = 0 // 재실행 여부
        var error = "" // 에러 메시지
        
        switch nsError {
            
        // MARK: [생체 인증에 실패하였습니다. 재실행하시겠습니까?]
        case LAError.authenticationFailed:
            retry = 1
            error = "생체 인증에 실패하였습니다. 재실행하시겠습니까?"
        
        // MARK: [생체 인증 동작을 취소하였습니다. 재실행하시겠습니까?]
        case LAError.userCancel:
            retry = 1
            error = "생체 인증 동작을 취소하였습니다. 재실행하시겠습니까?"
        
        // MARK: [생체 인증 기능이 필요합니다. 재실행하시겠습니까?]
        case LAError.userFallback:
            retry = 1
            error = "생체 인증 기능이 필요합니다. 재실행하시겠습니까?"
            
        // MARK: [기기에서 생체 인식을 사용할 수 없습니다. 권한을 거부하신 경우 확인해주세요.]
        case LAError.biometryNotAvailable:
            retry = 0
            error = "기기에서 생체 인식을 사용할 수 없습니다. 권한을 거부하신 경우 확인해주세요."
        
        // MARK: [등록된 생체 인식 ID가 없습니다. 생체 인증 등록 후 사용해 주세요.]
        case LAError.biometryNotEnrolled:
            retry = 0
            error = "등록된 생체 인식 ID가 없습니다. 생체 인증 등록 후 사용해 주세요."
            
        // MARK: [생체 인증 사용 잠금 (LOCK) 상태입니다. 디바이스에 등록된 생체 인증을 확인 후 다시 실행해주세요.]
        case LAError.biometryLockout:
            retry = 0
            error = "생체 인증 사용 잠금 (LOCK) 상태입니다. 디바이스에 등록된 생체 인증을 확인 후 다시 실행해주세요."
            
        // MARK: [생체 인증 기능 동작이 취소되었습니다. 잠시후 다시 실행해주세요.]
        case LAError.appCancel:
            retry = 0
            error = "생체 인증 기능 동작이 취소되었습니다. 잠시후 다시 실행해주세요."
            
        // MARK: [생체 인증에서 일시적인 문제가 발생하였습니다.]
        default:
            retry = 0
            error = "생체 인증에서 일시적인 문제가 발생하였습니다."
        }
        
        /*
        print("")
        print("===============================")
        print("[A_Auth >> biometricError() :: 생체 인증 에러 메시지 반환 실시]")
        print("error :: ", error)
        print("===============================")
        print("")
        // */
        
        return (retry, error)
    }
    
    
    
    
    
    // MARK: - [생체 인증 수행 >> 사용 가능 여부 판단]
    @available(iOS 11.0, *) // [특정 IOS 버전 이상 사용 가능]
    func canEvaluate() -> Dictionary<String, Any> {
        print("")
        print("===============================")
        print("[A_Auth >> canEvaluate() :: 생체 인증 [사용] 가능 여부 [판단] 실시]")
        print("===============================")
        print("")
        
        
        // [세마포어 선언 : 프로그램 로직을 동기화 구현]
        let semaphore = DispatchSemaphore(value: 0) // [value 0 값은 대기 상태 선언]
        
        
        var returnDic : Dictionary<String, Any> = [String : Any]() // 리턴 딕셔너리
        var errorData : NSError? = nil // 에러 타입
        var errorMsg = "" // 에러 메시지
        
        
        guard self.context.canEvaluatePolicy(self.policy, error: &self.error) else {
            
            // [생체 인증 타입 매핑]
            let type = self.biometricType(for: self.context.biometryType)
            
            
            // [에러 체크 문제 발생]
            guard let error = self.error
            else {
                print("")
                print("===============================")
                print("[A_Auth >> canEvaluate() :: 생체 인증 [사용] 가능 [결과] 확인]")
                print("결과 :: 에러 체크 실패")
                print("type :: ", type)
                print("error :: ", error?.localizedDescription ?? "")
                print("===============================")
                print("")
                
                // [생체 인증 사용 가능 여부 에러 결과 반환]
                returnDic["result"] = false
                returnDic["msg"] = A_Auth.ERROR_CHECK_FAIL
                
                // [세마포어 신호 알림]
                semaphore.signal()
                
                // [리턴 데이터 반환]
                return returnDic
            }
            
            // [에러 타입 삽입 실시]
            errorData = error as NSError
            
            print("")
            print("===============================")
            print("[A_Auth >> canEvaluate() :: 생체 인증 [사용] 가능 [결과] 확인]")
            print("결과 :: 사용 불가능")
            print("type :: ", type)
            print("retry :: ", self.biometricError(from: errorData as! NSError).0)
            print("msg :: ", self.biometricError(from: errorData as! NSError).1)
            print("===============================")
            print("")
            
            // [에러 메시지 삽입]
            errorMsg = self.biometricError(from: errorData!).1 // [에러 메시지]
            
            
            // [에러 발생 데이터 생성 실시]
            returnDic["result"] = false
            returnDic["msg"] = errorMsg
            
            
            // [세마포어 신호 알림]
            semaphore.signal()
            
            
            // [리턴 데이터 반환]
            return returnDic
        }
        
        print("")
        print("===============================")
        print("[A_Auth >> canEvaluate() :: 생체 인증 [사용] 가능 [결과] 확인]")
        print("결과 :: 사용 가능")
        print("type :: ", biometricType(for: context.biometryType))
        print("msg :: ", A_Auth.CHECK_SUCCESS)
        print("===============================")
        print("")
        
        // MARK: [evaluate 사용 가능]
        returnDic["result"] = true
        returnDic["msg"] = A_Auth.CHECK_SUCCESS
        
        
        // [세마포어 신호 알림]
        semaphore.signal()
        
        
        // [세마포어 확인 대기]
        semaphore.wait()
        
        
        // [리턴 데이터 반환]
        return returnDic
    }
    
    
    
    
    
    // MARK: - [생체 인증 수행 >> 결과 확인 실시]
    @available(iOS 11.0, *) // [특정 IOS 버전 이상 사용 가능]
    func evaluate() {
        print("")
        print("===============================")
        print("[A_Auth >> evaluate() :: 생체 인증 [수행] 실시]")
        print("===============================")
        print("")
        
        self.context.evaluatePolicy(self.policy, localizedReason: self.localizedReason) { (success, error) in
            if success == true {
                print("")
                print("===============================")
                print("[A_Auth >> evaluate() :: 생체 인증 [결과] 확인 실시]")
                print("결과 :: 생체 인증 성공")
                print("msg :: ", A_Auth.CHECK_SUCCESS)
                print("===============================")
                print("")
                
                // [노티피케이션 알림 전송 실시]
                NotificationCenter.default.post(
                     name: NSNotification.Name(rawValue: "simpleAuthNotification"), // 알림을 식별하는 태그
                     object: nil, // 발송자가 옵저버에게 보내려고 하는 객체
                     userInfo: ["message" : "\(A_Auth.CHECK_SUCCESS)"] // 객체의 저장소 [AnyHashable: Any] 형태
                )
                
                return
            }
            else {
                print("")
                print("===============================")
                print("[A_Auth >> evaluate() :: 생체 인증 [결과] 확인 실시]")
                print("결과 :: 에러 발생 확인")
                print("error :: ", error?.localizedDescription ?? "")
                print("retry :: ", self.biometricError(from: error as! NSError).0)
                print("msg :: ", self.biometricError(from: error as! NSError).1)
                print("===============================")
                print("")
                
                // MARK: [재실행 수행인 경우]
                if self.biometricError(from: error as! NSError).0 == 1 {
                    
                    // [다시 호출 수행 실시]
                    self.evaluate()
                }
                else {
                    // [노티피케이션 알림 전송 실시]
                    NotificationCenter.default.post(
                         name: NSNotification.Name(rawValue: "simpleAuthNotification"), // 알림을 식별하는 태그
                         object: nil, // 발송자가 옵저버에게 보내려고 하는 객체
                         userInfo: ["message" : self.biometricError(from: error as! NSError).1 ?? ""] // 객체의 저장소 [AnyHashable: Any] 형태
                    )
                }
                return
            }
        }
    }
    
} // [클래스 종료]
 

[결과 출력]

 

 

반응형
Comments