Skip to content

fix(ios): biometric commands broken on iOS 26+ (applesimutils interface change) #4931

@SoaringEarth

Description

@SoaringEarth

Description

On iOS 26, applesimutils changed its biometric command interface. The --byId <udid> --matchFace/--matchFinger/--unmatchFace/--unmatchFinger flags are no longer supported. The tool now requires --booted instead of --byId <udid>, and uses unified --biometricMatch / --biometricNonmatch flags rather than per-type variants.

This breaks all three biometric methods in AppleSimUtils.js:

  • matchBiometric
  • unmatchBiometric
  • setBiometricEnrollment

Environment

  • macOS 25.4.0 (Darwin)
  • Xcode 26.1.1
  • applesimutils (latest)

Proposed Fix

Branch on iOS version using the already-memoized _getMajorIOSVersion(udid) call:

@@ -311,14 +311,24 @@ class AppleSimUtils {
       return;
     }
 
-    const options = {
-      args: `--byId ${udid} --match${matchType}`,
-      retries: 1,
-      statusLogs: {
-        trying: `Trying to match ${matchType}...`,
-        successful: `Matched ${matchType}!`
-      },
-    };
+    const isIOS26Plus = (await this._getMajorIOSVersion(udid)) >= 26;
+    const options = isIOS26Plus
+      ? {
+          args: `--booted --biometricMatch`,
+          retries: 1,
+          statusLogs: {
+            trying: `Trying to match ${matchType}...`,
+            successful: `Matched ${matchType}!`
+          }
+        }
+      : {
+          args: `--byId ${udid} --match${matchType}`,
+          retries: 1,
+          statusLogs: {
+            trying: `Trying to match ${matchType}...`,
+            successful: `Matched ${matchType}!`
+          }
+        };
     await this._execAppleSimUtils(options);
   }
 
@@ -327,14 +337,24 @@ class AppleSimUtils {
       return;
     }
 
-    const options = {
-      args: `--byId ${udid} --unmatch${matchType}`,
-      retries: 1,
-      statusLogs: {
-        trying: `Trying to unmatch ${matchType}...`,
-        successful: `Unmatched ${matchType}!`
-      },
-    };
+    const isIOS26Plus = (await this._getMajorIOSVersion(udid)) >= 26;
+    const options = isIOS26Plus
+      ? {
+          args: `--booted --biometricNonmatch`,
+          retries: 1,
+          statusLogs: {
+            trying: `Trying to unmatch ${matchType}...`,
+            successful: `Unmatched ${matchType}!`
+          }
+        }
+      : {
+          args: `--byId ${udid} --unmatch${matchType}`,
+          retries: 1,
+          statusLogs: {
+            trying: `Trying to unmatch ${matchType}...`,
+            successful: `Unmatched ${matchType}!`
+          }
+        };
     await this._execAppleSimUtils(options);
   }
 
@@ -344,13 +364,15 @@ class AppleSimUtils {
     }
 
     const toggle = yesOrNo === 'YES';
+    const isIOS26Plus = (await this._getMajorIOSVersion(udid)) >= 26;
+    const byIdOrBooted = isIOS26Plus ? `--booted` : `--byId ${udid}`;
     const options = {
-      args: `--byId ${udid} --biometricEnrollment ${yesOrNo}`,
+      args: `${byIdOrBooted} --biometricEnrollment ${yesOrNo}`,
       retries: 1,
       statusLogs: {
         trying: `Turning ${toggle ? 'on' : 'off'} biometric enrollment...`,
         successful: toggle ? 'Activated!' : 'Deactivated!'
-      },
+      }
     };
     await this._execAppleSimUtils(options);
   }

The _getMajorIOSVersion() result is already memoized per UDID so there is no performance impact.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions