투케이2K

36. (Objective-C/objc) A_Auth - 간편 지문 , 얼굴 인증 수행 클래스 정의 실시 본문

Objective-C

36. (Objective-C/objc) A_Auth - 간편 지문 , 얼굴 인증 수행 클래스 정의 실시

투케이2K 2022. 6. 24. 09:58

[개발 환경 설정]

개발 툴 : XCODE

개발 언어 : OBJECTIVE-C

 

[사전 info plist 설정 필요]

 

[소스 코드]

import UIKit

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



@objc class A_Auth: NSObject {
    
    
    
    // MARK: [클래스 설명]
    /*
    // -----------------------------------------
    1. A_Auth : 생체 인증 수행 액티비티
    // -----------------------------------------
    2. info.plist 권한 등록 필요 :
       - Privacy - Face ID Usage Description
    // -----------------------------------------
    3. import 추가 필요 :
       - import LocalAuthentication
    // -----------------------------------------
    */
    
    
    
    
    
    // MARK: [간편 지문 인증 사용 방법]
    /*
    // -----------------------------------------
    1. 간편 지문 인증 수행 > 자동으로 권한 체크 > 잠금 설정 체크 > 지문 인증 성공 결과 및 에러 메시지 받음
    // -----------------------------------------
    2. Objective-C 소스코드에서 Swift 소스코드 사용 참고 사이트 :
     
      // MARK: [참고 사이트 주소]
      https://blog.naver.com/kkh0977/222785528528
    // -----------------------------------------
    3. Objective-C 소스코드에서 Swift 사용을 위해 import 등록
     
     // MARK: [프로젝트명칭-Swift.h import 명시]
     #import "objectiveProject-Swift.h"
    // -----------------------------------------
    4. 액티비티 메모리 로드 [viewDidLoad] : 노티피케이션 채널 알림 등록
     
     // MARK: [노티피케이션 알림 채널 등록]
     NSNotificationCenter *simpleAuthNotification = [NSNotificationCenter defaultCenter]; // [객체 선언 실시]
     [simpleAuthNotification addObserver:self selector:@selector(simpleAuthNotification:) name:[A_Auth NOTI_CHANNER] object:nil]; // [채널 등록 실시]
    // -----------------------------------------
    5. 액티비티 수행 종료 [viewDidDisappear] : 노티피케이션 채널 알림 해제
     
     // MARK: [노티피케이션 알림 채널 해제]
     NSNotificationCenter *simpleAuthNotification = [NSNotificationCenter defaultCenter]; // [객체 선언 실시]
     [simpleAuthNotification removeObserver:self name:[A_Auth NOTI_CHANNER] object:nil]; // [채널 해제 실시]
    // -----------------------------------------
    6. 실시간 간편 인증 수행 결과를 받을 수 있는 리시버 등록 실시
     
     // MARK: - [실시간 노티피케이션 알림 수신 (받는) 부분]
     -(void)simpleAuthNotification:(NSNotification *)notification {
         printf("\n");
         printf("==================================== \n");
         printf("[ViewController >> simpleAuthNotification() :: 간편 인증 결과 채널 알림 전달 받음] \n");
         printf("==================================== \n");
         printf("\n");
         
         // [딕셔너리로 전달 받은 데이터 파싱]
         NSString *msgData = [[notification userInfo] objectForKey:@"message"];
         
         printf("\n");
         printf("==================================== \n");
         printf("[ViewController >> simpleAuthNotification() :: 간편 인증 결과 확인] \n");
         printf("[msgData : %s] \n", msgData.description.UTF8String);
         printf("==================================== \n");
         printf("\n");
         
         // [간편 인증 결과가 성공 및 실패인 경우 로직 처리 실시]
         if ([[String_Ext shared] equalsWith_oneString:msgData _twoString:[A_Auth CHECK_SUCCESS]] == true){ // MARK: [간편 지문 인증 등록 성공]
             
             // [팝업창 알림 실시]
             [[A_Auth shared] showAlertWithView:self tittle:@"[간편 인증 결과]" content:msgData okBtb:@"확인" noBtn:@"취소"];
         }
         else { // MARK: [에러 발생 메시지 인 경우]
             // [팝업창 알림 실시]
             [[A_Auth shared] showAlertWithView:self tittle:@"[간편 인증 결과]" content:msgData okBtb:@"확인" noBtn:@"취소"];
         }
     }
    // -----------------------------------------
    7. 뷰 화면 표시 [viewDidAppear] : 간편 인증 기능 수행 실시
     
     // MARK: [간편 인증 기능 수행]
     [[A_Auth shared] simpleAuthWithView:self];
    // -----------------------------------------
    */
    
    
    
    
    
    // MARK: [클래스 싱글톤 생성 실시]
    @objc static let shared = A_Auth()
    
    
    
    
    
    // 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" // [에러 체크 실패 멘트 저장]
    @objc static let CHECK_SUCCESS = "SUCCESS" // [생체 인증 사용 여부 및 생체 인증 결과 성공 멘트 저장]
    
    @objc static let NOTI_CHANNER = "simpleAuthNotification" // [노티피케이션 브로드 캐스팅 채널 알림 등록]
    
    
    
    
    
    // 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: A_Auth.NOTI_CHANNER), // 알림을 식별하는 태그
                     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: A_Auth.NOTI_CHANNER), // 알림을 식별하는 태그
                         object: nil, // 발송자가 옵저버에게 보내려고 하는 객체
                         userInfo: ["message" : self.biometricError(from: error as! NSError).1 ?? ""] // 객체의 저장소 [AnyHashable: Any] 형태
                    )
                }
                return
            }
        }
    }
    
    
    
    
    
    // MARK: - [생체 인증 수행 실시]
    @objc func simpleAuth(view: UIViewController){
        print("")
        print("====================================")
        print("[A_Auth >> 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("[A_Auth >> simpleAuth() :: 생체 인증 사용 여부 확인]")
                print("결과 :: Face ID / Touch ID 사용 가능 여부 확인")
                print("canEvaluateDic :: \(canEvaluateDic)")
                print("====================================")
                print("")
                
                if (canEvaluateDic["result"] != nil) == true { // [생체 인증 사용 가능한 경우]
                    
                    // MARK: [생체 인증 수행 실시]
                    a_auth.evaluate()
                }
                else { // [생체 인증 사용 불가능한 경우]
                    
                    // [팝업창 알림 표시 수행 실시]
                    self.showAlert(
                        view: view,
                        tittle: "[생체 사용 가능 여부]",
                        content: "\(canEvaluateDic["msg"]!)",
                        okBtb: "확인",
                        noBtn: "취소"
                    )
                }
                    
            } else {
                print("")
                print("====================================")
                print("[ViewController >> simpleAuth() :: 생체 인증 사용 여부 확인]")
                print("결과 :: IOS 버전 11 미만 >> 생체 인증 사용 제한 디바이스")
                print("====================================")
                print("")
            }
        }
    }

    
    
    
    
    
    // MARK: [alert 팝업창 호출 메소드 정의 실시 : 이벤트 호출 시]
    // 호출 방법 : [[A_Auth shared] showAlertWithView:self tittle:@"[알 림]" content:msgData okBtb:@"확인" noBtn:@"취소"];
    @objc func showAlert(view:UIViewController, 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 팝업창 활성 실시]
            view.present(alert, animated: false, completion: nil)
        }
    }

    
} // [클래스 종료]
 

[결과 출력]


 

반응형
Comments