-
[iOS] Face ID, Touch ID 구현하기👻 iOS 2021. 1. 23. 22:24
앱등이로서 앱을 쓰다보면 많이 적용되어 있어서 굉장히 친숙한 Face ID, Touch ID.
그런데 정작 난 개발자로서 그게 어떤 과정으로 동작하는지 모른다.
반성하며... 이제라도 파보고 잊지 않으려고 기록해 두도록 한다.
Note:
- Face ID 물리 장비가 필요함.
- Touch ID 는 시뮬레이터에서도 된다. (Xcode 9 이상)
- Keychain 도 역시 시뮬레이터에서 사용가능.
- 튜토리얼 대부분 Touch ID를 쓰지만 Face ID 도 대부분 적용됨.
- Under the hood, is the Local Authentication framework. (로컬 인증 프레임웍이 필요한듯)새 프로젝트 만들어서 일단 TouchID 부터 해봅니다.
1. LocalAuthentication framework 부터 추가
2. ViewController에 import
import LocalAuthentication
3. Storyboard > ViewController 에 버튼 하나 만들어서 action 을 연결합니다.
연결된 구현은 실제로 LAContext를 생성해 두고 evaluatePolicy 를 호출해서 Touch ID 요청을 보내는 거예요.
policy 로 deviceOwnerAuthenticationWithBiometrics 와 deviceOwnerAuthentication 가 있는데 생체인증만 제공할껀지 대체 생체인증 실패시 passcode까지 제공할껀지의 차이네요.
class ViewController: UIViewController { @IBOutlet weak var loginButton: UIButton! let authContext = LAContext() ... @IBAction func handleLogin(sender: UIButton) { authContext.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: "인증해야지") { (success, error) in print("인증결과", success, error) } } }
4. 시뮬레이터에서 확인
실행한 결과에 아래 오류를 받았어요.
인증결과 false Optional(Error Domain=com.apple.LocalAuthentication Code=-6 "Biometry is not available on this device." UserInfo={NSLocalizedDescription=Biometry is not available on this device.})
생체인증을 할 수 없는 기기.. 시뮬레이터가 iPod touch 네요 ㅠㅠ
다시 iPhone8으로 돌려봅니다.
인증결과 false Optional(Error Domain=com.apple.LocalAuthentication Code=-7 "No identities are enrolled." UserInfo={NSLocalizedDescription=No identities are enrolled.})
시뮬레이터에 터치ID 등록을 어찌하나 싶었는데 오류로 바로 내뱉네요. 시뮬레이터 메뉴를 가만히 살펴보니 'Features > Touch ID' 메뉴에 인증을 등록처리 하고, 매칭여부를 컨트롤 할 수 있게 되어있네요. Enrolled에 체크부터 합니다.
그리고 다시 시뮬레이터로 가서 터치아이드 버튼을 눌러봅니다.
오.. 이렇게 간단히? 5. 인증 or 미인증처리
좀 전에 시뮬레이터 Features > Touch ID 메뉴에서 봤던 Matching / Non-matching 메뉴로 위의 "인증해야지" 로 대기하고 있는 다이얼로그에 응답을 줍니다.
Matching 선택 시 콘솔에 아래와 같이 print 됩니다.
인증결과 true nil
직접 취소를 한다면 아래의 결과를 받았고,
인증결과 false Optional(Error Domain=com.apple.LocalAuthentication Code=-2 "Canceled by user." UserInfo={NSLocalizedDescription=Canceled by user.})
Non-matching 를 3회 선택하니 아래와 같이 retry limit 초과 메시지를 주네요.
인증결과 false Optional(Error Domain=com.apple.LocalAuthentication Code=-1 "Application retry limit exceeded." UserInfo={NSLocalizedDescription=Application retry limit exceeded.})
그런데 Non-matching 을 1회 시도하면 아래와 같이 Enter password 버튼이 노출되는데 인증정책이 deviceOwnerAuthenticationWithBiometrics 라면 버튼을 눌러도 실패로만 응답받게 되네요. password를 쓰고싶으면 정책을 deviceOwnerAuthentication로 설정하면 되겠습니다.
인증결과 false Optional(Error Domain=com.apple.LocalAuthentication Code=-3 "Fallback authentication mechanism selected." UserInfo={NSLocalizedDescription=Fallback authentication mechanism selected.})
6. Face ID 구현
Touch ID는 이렇게 돌아가는구나.. 알아봤으니 Face ID를 살펴봅니다.
Face ID는 시뮬레이터 안 된다고 하니 기기에 연결에서 실행해봐요.
그리고 아까 연결해 두었던 Touch ID 버튼을 누르면...!
2021-01-23 21:45:22.535786+0900 fido[6258:1353020] [access] This app has crashed because it attempted to access privacy-sensitive data without a usage description. The app's Info.plist must contain an NSFaceIDUsageDescription key with a string value explaining to the user how the app uses this data.
아.. info.plist 에 설명을 추가하래요.
일단 대충 문구를 넣어줍니다.
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> .... <key>NSFaceIDUsageDescription</key> <string>얼굴 좀 보자</string> </dict> </plist>
다시 실행해 보면...!
확인을 누르는게 일반적이지만 저는 변태니까 "허용 안 함" 눌러봤습니다.
당연히 오류 응답을 받고 내용엔 거부했다고 나옵니다.
인증결과 false Optional(Error Domain=com.apple.LocalAuthentication Code=-6 "User has denied the use of biometry for this app." UserInfo={NSLocalizedDescription=User has denied the use of biometry for this app.})
화면의 인증 버튼을 다시 눌러도 한 번 거부한 이상 다이얼로그는 나오지 않더군요 ㅠㅠ
설정에 가서 강제로 허용하는 방법이겠죠...? (아오 귀찮...)
괜히 변태짓 해서.. ㅠㅠ 설정에 가서 앱을 찾아 들어가면 Face ID가 비활성화 되어 있을텐데 활성화 상태로 바꾸고 앱으로 다시 돌아옵니다.
다시 인증 버튼을 누르면...!
기대했던 휘리릭 뿅이 나오면서 안.심.
휘리릭 뿅 7. 지원 가능한 디바이스 구분
authContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil)
응답값이 false 라면 iPod 이나 구형 폰으로 봐야합니다.
8. 지원 가능한 생체인증 종류 구분
멤버 변수로 생성한 authContext.biometryType 에 접근하면 아래와 같은 지원 가능한 생체인증 정보를 얻을 수 있어요.
@available(iOS 11.0, *) public enum LABiometryType : Int { /// The device does not support biometry. @available(iOS 11.2, *) case none = 0 /// The device does not support biometry. @available(iOS, introduced: 11.0, deprecated: 11.2, renamed: "LABiometryType.none") public static var LABiometryNone: LABiometryType { get } /// The device supports Touch ID. case touchID = 1 /// The device supports Face ID. case faceID = 2 }
9. 결론
그래서 간략히 Face ID, Touch ID 를 사용하는 방식에 대해서 확인해 봤는데
생각보다는 구현 자체는 간단한것 같아요.
이제 이와 관련해서 키체인을 봐야겠네요.
아래는 ViewController 전체 소스에요.
별거 없어서 이것만 보면 될 것 같네요!
import UIKit import LocalAuthentication class ViewController: UIViewController { @IBOutlet weak var loginButton: UIButton! let authContext = LAContext() override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. loginButton.isHidden = !self.canEvaluatePolicy() let type = self.getBiometryType() if type == .faceId { loginButton.setImage(UIImage(systemName: "faceid"), for: .normal) } else if type == .touchId { loginButton.setImage(UIImage(systemName: "touchid"), for: .normal) } else { loginButton.isHidden = true } } @IBAction func handleLogin(sender: UIButton) { authContext.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: "인증해야지") { (success, error) in print("인증결과", success, error) } } func canEvaluatePolicy() -> Bool { let can = authContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil) return can } enum BiometryType { case faceId case touchId case none } func getBiometryType() -> BiometryType { switch authContext.biometryType { case .faceID: return .faceId case .touchID: return .touchId default: return .none } } }