概要
ロック画面の解除に普段何気なく使用しているFace IDやTouch IDですが、セキュリティレベルを高めるのはもちろん、ユーザーがIDやパスワードを打つストレスの解消にもなっています。
私を含め、多くの人は一度体験したらもう二度とパスワードを入力して画面ロックを解除する時代に戻れないのでは?と思うほどの印象です。そんな便利なFace IDやTouch IDをアプリ内でも使用してみたいと思い試してみましたのでご紹介したいと思います。
環境
この記事は以下のバージョン環境のもと作成されたものです。
【Xcode】14.2
【iOS】16.3
【macOS】Monterey
LocalAuthentication
Face IDやTouch IDはiPhoneで標準に備わっている機能で、大半の方がiPhoneの初期設定で行うと思います。(デバイスによりますが)
その時に登録した顔や指紋の情報はセキュリティを最大限に高めるために、OSとは分離されたハードウェアベースのセキュリティプロセッサであるSecure Enclaveで管理されます。
Secure Enclaveで管理されている生体情報を使用して認証を行うためにはLocalAuthenticationフレームワークを使用する必要があります。
以下ドキュメントリンクです。
またFace IDやTouch IDを使用してログインするサンプルコードもあります。
Apple Developer Documentation
サンプルコードを試してみた様子
一見生体認証機能の実装って難しそうに見えますが、ドキュメントやサンプルコードを見る限りシンプルで以下メソッド叩けばとりあえず実装できそうな感じです。
Task { do { try await context.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: "Log in to your account") state = .loggedin } catch let error { print(error.localizedDescription) // Fall back to a asking for username and password. // ... } }
試してみる
ここからは試しに実装してみたいと思います。
今回使用するデバイスはiPhone 14 Proとなりますので、Face IDを使用していきたいと思います。
Privacy - Face ID Usage Descriptionを追加
生体認証を追加したい場合はinfo.plistにPrivacy - Face ID Usage Descriptionを追加する必要があります。
ViewやClassの実装
まず画面を作成します。
struct BioAuthView: View { @State var flag = true @StateObject var faceAuth = FaceAuth() var body: some View { ZStack { Color.yellow .ignoresSafeArea() VStack { Spacer() Text(faceAuth.state == .loggedin ? "状態:ログイン" : "状態:ログアウト") .foregroundColor(.black) Spacer() Button { faceAuth.authStateChanger() } label: { Text(faceAuth.state == .loggedin ? "ログアウト" : "認証開始") .foregroundColor(.white) .padding() .padding(.horizontal, 60) .background( RoundedRectangle(cornerRadius: 16) .foregroundColor(.blue) ) } } } } }
samplerでは認証状態に関してEnum使っていたので同じようにします。
enum AuthenticationState { case loggedin case loggedout }
最後にFace IDを使用する為のClassを作成します。
このClassでLocalAuthenticationフレームワークを使用するのでImportしてください。
import LocalAuthentication //必須です final class FaceAuth: ObservableObject { @Published private(set) var state: AuthenticationState = .loggedout var context: LAContext = LAContext() let reason = "Face IDを使用する場合は設定より\nアクセスの許可を変更してください。" @MainActor func authStateChanger() { if state == .loggedin { state = .loggedout } else { context = LAContext() context.localizedCancelTitle = "パスワード入力で認証を行う" var error: NSError? guard context.canEvaluatePolicy(.deviceOwnerAuthentication, error: &error) else { print(error?.localizedDescription ?? "このデバイスでは生体認証を行うことができません") return } Task { do { try await context.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: reason) state = .loggedin } catch { print(error.localizedDescription) } } } } }
以上で完成です!
起動するとFace IDを使用して良いか確認してきます。
許可をすることでFace IDを使用することができます。
解説
LAContextは認証ポリシーやアクセス制御を評価するためのもので、LAContext()とすることで初期化して認証を行なっています。
よってelse直下にあるcontext = LAContext()を行わないと、一度認証した後は認証したままとなりますので以下のような挙動になります。
context.localizedCancelTitleは生体認証に失敗した際に表示されるアラートの下部の文言を定義することができます。
localizedReasonでは生体認証の許可をしていない場合に表示される文言を定義することができます。
エラーハンドリングに関してはcatchの中で以下のように行うことができます。
guard let laError = error as? LAError else { return print("unknown") }; switch laError.code { case .authenticationFailed: print("authenticationFailed") case .userCancel: print("userCancel") case .userFallback: print("userFallback") case .systemCancel: print("systemCancel") case .passcodeNotSet: print("passcodeNotSet") case .touchIDNotAvailable: print("touchIDNotAvailable") case .touchIDNotEnrolled: print("touchIDNotEnrolled") case .touchIDLockout: print("touchIDLockout") case .appCancel: print("appCancel") case .invalidContext: print("invalidContext") case .notInteractive: print("notInteractive") @unknown default: break }
また生体認証方式の判定にはLABiometryType
で取れるようです。
まとめ
生体認証と聞くとなんだか難しそうでしたが、非常に簡単に行う事ができました。こんなに簡単に実装できるなら次に個人開発を行う際はどこかで取り入れたいなと思いました!
以上生体認証を実装する方法でした。