From 56d2d669724bcf707992f4946ea777cd19407bbf Mon Sep 17 00:00:00 2001 From: Avner Matan <137777701+avner-m@users.noreply.github.com> Date: Thu, 13 Aug 2026 12:02:39 +1200 Subject: [PATCH] chore(react-native-mdocs-holder-tutorial): update for Holder SDK 10.0.0 Moves both the starter and complete projects from the End of Life 8.x line to 10.0.0, and updates the complete project for the breaking changes: - OfferedCredential.doctype is now docType. - getCredentials and deleteCredential return a Result, so unwrap them. The previous deleteCredential handler reported failures as successes because expected errors are returned rather than thrown. - Credential retrieval results are discriminated on isSuccess, so narrow each item instead of reporting the array length as successes. - UserAuthenticationType.BiometricOrPasscode becomes UserPresence. - claims on an offered credential is optional. The lockfiles are deliberately untouched. They must be regenerated once 10.0.0 is published, which is also when this can first be typechecked. --- .../app/claim-credential.tsx | 36 +++++++++++++++---- .../package.json | 2 +- .../providers/HolderProvider.tsx | 34 +++++++++++++++--- .../package.json | 2 +- 4 files changed, 61 insertions(+), 13 deletions(-) diff --git a/react-native-mdocs-holder-tutorial/react-native-mdocs-holder-tutorial-complete/app/claim-credential.tsx b/react-native-mdocs-holder-tutorial/react-native-mdocs-holder-tutorial-complete/app/claim-credential.tsx index 3e8ca59..c82a5d0 100644 --- a/react-native-mdocs-holder-tutorial/react-native-mdocs-holder-tutorial-complete/app/claim-credential.tsx +++ b/react-native-mdocs-holder-tutorial/react-native-mdocs-holder-tutorial-complete/app/claim-credential.tsx @@ -85,10 +85,31 @@ export default function ClaimCredential() { retrieved.error.message || "Failed to retrieve credentials.", ); } else { - Alert.alert( - "Success", - `Retrieved ${retrieved.value.length} credential(s) successfully!`, - ); + // A credential offer can contain more than one credential, and each one is + // retrieved independently. Narrow each item on isSuccess so that a credential + // that failed to retrieve is not reported as a success. + let successCount = 0; + const failureMessages: string[] = []; + + for (const item of retrieved.value) { + if (item.isSuccess) { + successCount++; + } else { + failureMessages.push(`${item.docType}: ${item.error.message}`); + } + } + + if (failureMessages.length === 0) { + Alert.alert( + "Success", + `Retrieved ${successCount} credential(s) successfully!`, + ); + } else { + Alert.alert( + "Partial Success", + `Retrieved ${successCount} credential(s). Failed to retrieve ${failureMessages.length}:\n${failureMessages.join("\n")}`, + ); + } } // Refresh the list of mobile credentials in the holder application await getMobileCredentials(); @@ -155,11 +176,12 @@ export default function ClaimCredential() { {credentialOffer.credentials.map( (cred: OfferedCredential, index: number) => ( - + {cred.name} - Document Type: {cred.doctype} + Document Type: {cred.docType} + {/* An offer only includes claims when it contains claim data */} - Number of Claims: {cred.claims?.length} + Number of Claims: {cred.claims?.length ?? 0} ), diff --git a/react-native-mdocs-holder-tutorial/react-native-mdocs-holder-tutorial-complete/package.json b/react-native-mdocs-holder-tutorial/react-native-mdocs-holder-tutorial-complete/package.json index c45931a..4034e61 100644 --- a/react-native-mdocs-holder-tutorial/react-native-mdocs-holder-tutorial-complete/package.json +++ b/react-native-mdocs-holder-tutorial/react-native-mdocs-holder-tutorial-complete/package.json @@ -13,7 +13,7 @@ "ios": "expo run:ios" }, "dependencies": { - "@mattrglobal/mobile-credential-holder-react-native": "^8.1.1", + "@mattrglobal/mobile-credential-holder-react-native": "^10.0.0", "expo": "^55", "expo-build-properties": "~55.0.13", "expo-constants": "~55.0.15", diff --git a/react-native-mdocs-holder-tutorial/react-native-mdocs-holder-tutorial-complete/providers/HolderProvider.tsx b/react-native-mdocs-holder-tutorial/react-native-mdocs-holder-tutorial-complete/providers/HolderProvider.tsx index 8f2fa0c..e607640 100644 --- a/react-native-mdocs-holder-tutorial/react-native-mdocs-holder-tutorial-complete/providers/HolderProvider.tsx +++ b/react-native-mdocs-holder-tutorial/react-native-mdocs-holder-tutorial-complete/providers/HolderProvider.tsx @@ -56,7 +56,7 @@ export function HolderProvider({ children }: { children: React.ReactNode }) { const result = await initialize({ userAuthenticationConfiguration: { userAuthenticationBehavior: UserAuthenticationBehavior.OnInitialize, - userAuthenticationType: UserAuthenticationType.BiometricOrPasscode, + userAuthenticationType: UserAuthenticationType.UserPresence, }, credentialIssuanceConfiguration: { autoTrustMobileCredentialIaca: true, @@ -87,8 +87,23 @@ export function HolderProvider({ children }: { children: React.ReactNode }) { const getMobileCredentials = useCallback(async () => { if (!isHolderInitialized) return; - const credentials = await getCredentials(); - setMobileCredentials(credentials); + + try { + const result = await getCredentials(); + + if (result.isErr()) { + setError(result.error.message || "Failed to get credentials."); + return; + } + + setMobileCredentials(result.value); + } catch (err) { + setError( + err instanceof Error + ? err.message + : "Unknown error while getting credentials", + ); + } }, [isHolderInitialized]); // When the holder is initialized, get the mobile credentials to display in the app @@ -113,7 +128,18 @@ export function HolderProvider({ children }: { children: React.ReactNode }) { style: "destructive", onPress: async () => { try { - await deleteCredential(credentialId); + const result = await deleteCredential(credentialId); + + // Expected failures are returned as an error result rather + // than thrown, so handle them before reporting success + if (result.isErr()) { + Alert.alert( + "Error", + result.error.message || "Failed to delete credential.", + ); + return; + } + Alert.alert("Success", "Credential deleted successfully."); await getMobileCredentials(); } catch (err) { diff --git a/react-native-mdocs-holder-tutorial/react-native-mdocs-holder-tutorial-starter/package.json b/react-native-mdocs-holder-tutorial/react-native-mdocs-holder-tutorial-starter/package.json index 3b126a5..5958230 100644 --- a/react-native-mdocs-holder-tutorial/react-native-mdocs-holder-tutorial-starter/package.json +++ b/react-native-mdocs-holder-tutorial/react-native-mdocs-holder-tutorial-starter/package.json @@ -13,7 +13,7 @@ "ios": "expo run:ios" }, "dependencies": { - "@mattrglobal/mobile-credential-holder-react-native": "^8.1.1", + "@mattrglobal/mobile-credential-holder-react-native": "^10.0.0", "expo": "^55", "expo-build-properties": "~55.0.13", "expo-constants": "~55.0.15",