A blog about how Swift works and iOS tricks, by Bruno Rocha.
Articles about advanced Swift development in excruciating detail, for free!
Detecting TouchID fingerprint/FaceID face changes
By default, LAContext's evaluatePolicy method will consider any registered fingerprint/face valid - even if they were added after your app was installed.
If your app contains sensitive data (like a bank or marketplace app would), you probably don't want this "feature". If someone with bad intentions gets a hold of your user's unlocked phone and adds new fingerprints, he could efforteslly pretend to be the original user - and your TouchID/FaceID enabled app would never notice the difference.
To detect hardware biometry changes, we can compare the value of one of LAContext's properties: evaluatedPolicyDomainState.
From Apple's documentation:
This property is set only when evaluatePolicy is called and succesful Touch ID or Face ID authentication was performed, or when canEvaluatePolicy succeeds for a biometric policy. It stays nil for all other cases.
If biometric database was modified (fingers or faces were removed or added), evaluatedPolicyDomainState data will change. Nature of such database changes cannot be determined but comparing data of evaluatedPolicyDomainState after different evaluatePolicy will reveal the fact database was changed between calls.
Please note that the value returned by this property can change exceptionally between major OS versions even if the state of biometry has not changed.
Since this property returns a Data object, you can effortlessly save it to the user's device.
Although Apple warns that this property can be unstable, at least by the time of this post's creation it managed to remain stable between updates.
If you're concerned about stability, you could potentially check for major system updates through UIDevice.current.systemVersion and act accordingly.
But what if the biometry data changes legitimally, like upgrading to an iPhone X? TouchID data will become FaceID data, changing evaluatedPolicyDomainState's value.
You could store all your data on the Keychain to prevent it from being backed up, but it turns out that LAContext contains several interesting properties.
One of them is biometryType, which indicates which biometry system (TouchID/FaceID/none) is enabled at the user's device.
Like evaluatedPolicyDomainState, it starts at nil, requiring a canEvaluatePolicy or evaluatePolicy call to be filled. I personally use this property to change messages and errors, but it can be used to detect system changes as well.
Due to possible stability issues, one could consider this a premature security measure. Because of that, consider if your app really needs this sort of protection - or even should have TouchID/FaceID support in first place.