From 3847367570e0ffb37cdcd902bea3ee2f6c254011 Mon Sep 17 00:00:00 2001 From: Goncalo-FradeIOHK Date: Wed, 4 Oct 2023 21:16:00 +0100 Subject: [PATCH] feat(anoncreds): add support for anoncreds issuance flow Fixes ATL-4294 --- .../Sources/ApolloImpl+KeyRestoration.swift | 25 +- .../Apollo/Sources/ApolloImpl+Public.swift | 10 +- .../Apollo/Sources/Model/Ed25519Key.swift | 2 + .../Apollo/Sources/Model/Secp256k1Key.swift | 9 +- .../Apollo/Sources/Model/X25519Key.swift | 2 + .../CreateLinkSecretOperation.swift | 4 +- .../CreateSec256k1KeyPairOperation.swift | 2 +- .../Apollo/Tests/ECSigningTests.swift | 5 +- .../Operations/CreatePeerDIDOperation.swift | 3 +- .../Castor/Tests/PeerDIDCreationTests.swift | 2 +- AtalaPrismSDK/Domain/Sources/BBs/Apollo.swift | 2 +- AtalaPrismSDK/Domain/Sources/BBs/Pollux.swift | 6 +- .../Sources/Models/Common/Downloader.swift | 5 + .../Models/Credentials/Credential.swift | 2 + .../ExportableImportableKey.swift | 2 +- .../Models/KeyManagement/KeyRestoration.swift | 23 +- .../Models/KeyManagement/StorableKey.swift | 3 + .../Providers/DIDPrivateKeyProvider.swift | 1 + .../Domain/Providers/DIDProvider.swift | 1 - .../Sources/Domain/StorableKeyModel.swift | 1 + ...DPrivateKeyDAO+DIDPrivateKeyProvider.swift | 17 +- ...DDIDPrivateKeyDAO+DIDPrivateKeyStore.swift | 2 + .../DAO/CDDIDPrivateKeyDAO.swift | 1 + .../Keychain/KeychainDAO.swift | 2 +- .../Models/CDKey+CoreDataProperties.swift | 1 + .../Pluto/Sources/PlutoImpl+Public.swift | 2 +- AtalaPrismSDK/Pluto/Sources/PlutoImpl.swift | 4 + .../PrismPluto.xcdatamodel/contents | 3 +- .../Pluto/Tests/CDDIDPairDAOTests.swift | 483 +++++++++--------- .../Pluto/Tests/CDDIDPrivateKeyDAOTests.swift | 287 ++++++----- .../Pluto/Tests/CDMessagesDAOTests.swift | 385 +++++++------- .../Tests/Helper/KeyRestoration+Test.swift | 8 +- .../Pluto/Tests/Helper/KeychianMock.swift | 28 + .../Pluto/Tests/Helper/PrivateKey+Test.swift | 1 + .../AnonCredential+ProvableCredential.swift | 2 - .../Models/AnonCreds/AnonCredential.swift | 47 +- .../AnonCreds/AnonCredentialDefinition.swift | 21 + ...sCredentialStack+StorableCredential.swift} | 5 +- .../AnonCreds/AnoncredsCredentialStack.swift | 50 ++ .../Sources/Models/JWT/JWTCredential.swift | 1 + .../Models/W3C/W3CVerifiableCredential.swift | 2 + .../CreateAnoncredCredentialRequest.swift | 32 +- .../ParseAnoncredsCredentialFromMessage.swift | 17 +- .../JWT/CreateJWTCredentialRequest.swift | 2 +- .../PolluxImpl+CredentialRequest.swift | 23 +- .../Sources/PolluxImpl+ParseCredential.swift | 38 +- .../Pollux/Sources/PolluxImpl+Public.swift | 2 + .../Pollux/Tests/AnoncredsTests.swift | 136 ++--- AtalaPrismSDK/Pollux/Tests/JWTTests.swift | 279 +++++----- .../Pollux/Tests/Mocks/AnoncredsMocks.swift | 8 - .../Pollux/Tests/Mocks/MockDownloader.swift | 9 + .../Pollux/Tests/Mocks/MockIssuer.swift | 104 ++++ .../Pollux/Tests/Mocks/MockProver.swift | 25 + .../Sources/PrismAgent+Credentials.swift | 87 +++- .../PrismAgent+DIDHigherFucntions.swift | 5 +- .../Sources/PrismAgent+Invitations.swift | 4 +- .../PrismAgent/Sources/PrismAgent+Proof.swift | 9 +- .../PrismAgent/Sources/PrismAgent.swift | 8 +- .../PrismAgent/Sources/PrismAgentErrors.swift | 2 +- .../Connection/ConnectionAccept.swift | 2 +- .../Connection/ConnectionRequest.swift | 5 +- .../Connection/DownloadDataWithResolver.swift | 43 ++ .../Invitation/V2/InvitationRunner.swift | 6 +- .../IssueCredential/CredentialPreview.swift | 29 ++ .../IssueCredential/IssueCredential.swift | 180 ++++++- .../IssueCredential/OfferCredential.swift | 173 ++++++- .../IssueCredential/ProposeCredential.swift | 89 +++- .../IssueCredential/RequestCredential.swift | 160 +++++- .../Protocols/Mediation/MediationGrant.swift | 2 +- .../Protocols/Pickup/PickupRunner.swift | 2 +- .../ProofPresentation/Presentation.swift | 2 +- .../ProposePresentation.swift | 2 +- .../RequestPresentation.swift | 2 +- .../Sources/Protocols/ProtocolTypes.swift | 6 + .../Tests/IssueCredentialTests.swift | 15 +- .../Tests/OfferCredentialTests.swift | 24 +- .../Tests/RequestCredentialTests.swift | 67 ++- Core/Sources/Helpers/Data+Hex.swift | 7 + Core/Sources/Helpers/Data+tryString.swift | 11 + Core/Sources/Helpers/JSONEncoder+Helper.swift | 2 +- Core/Sources/Helpers/String+tryData.swift | 11 + Package.swift | 2 +- .../project.pbxproj | 342 +------------ .../FuncionalitiesList.swift | 11 +- .../AddNewContact/AddNewContactBuilder.swift | 22 - .../AddNewContact/AddNewContactState.swift | 15 - .../AddNewContact/AddNewContactView.swift | 111 ---- .../AddNewContactViewModel.swift | 100 ---- .../UI/AlreadyConnectedView.swift | 39 -- .../UI/ConfirmConnectionView.swift | 42 -- .../AddNewContact/UI/InsertCodeView.swift | 57 --- .../CredentialDetailBuilder.swift | 21 - .../CredentialDetailNeoView.swift | 43 -- .../CredentialDetailViewModel.swift | 43 -- .../CredentialsListBuilder.swift | 22 - .../CredentialsListRouter.swift | 23 - .../CredentialsListState.swift | 15 - .../CredentialsList/CredentialsListView.swift | 199 -------- .../CredentialsListViewModel.swift | 47 -- .../Dashboard/DashboardBuilder.swift | 23 - .../Dashboard/DashboardRouter.swift | 91 ---- .../DashboardTabViewController.swift | 174 ------- .../WalletDemo/Dashboard/DashboardView.swift | 82 --- .../Dashboard/DashboardViewModelImpl.swift | 64 --- .../Modules/WalletDemo/Home/HomeBuilder.swift | 16 - .../Modules/WalletDemo/Home/HomeRouter.swift | 24 - .../Modules/WalletDemo/Home/HomeState.swift | 27 - .../Modules/WalletDemo/Home/HomeView.swift | 116 ----- .../WalletDemo/Home/HomeViewModel.swift | 25 - .../WalletDemo/Home/UI/ActivityListView.swift | 104 ---- .../Modules/WalletDemo/Main/MainView.swift | 69 --- .../WalletDemo/Main/MainViewModel.swift | 41 -- .../WalletDemo/Main/MainViewRouter.swift | 20 - .../Profile/ProfileHeaderView.swift | 63 --- .../ProofOfRequestBuilder.swift | 24 - .../ProofOfRequest/ProofOfRequestState.swift | 27 - .../ProofOfRequest/ProofOfRequestView.swift | 99 ---- .../ProofOfRequestViewModel.swift | 116 ----- .../UI/ProofOfRequestCheckView.swift | 88 ---- .../QRCodeScanner/QRCodeScannerBuilder.swift | 19 - .../QRCodeScanner/QRCodeScannerRouter.swift | 13 - .../QRCodeScanner/QRCodeScannerView.swift | 101 ---- .../QRCodeScannerViewModel.swift | 53 -- .../UI/CameraViewController.swift | 146 ------ .../QRCodeScanner/UI/QRScannerView.swift | 53 -- .../WalletDemo/UIHelper/AtalaButton.swift | 61 --- .../UIHelper/Button+Configuration.swift | 67 --- .../WalletDemo/UIHelper/CheckButton.swift | 22 - .../UIHelper/ClearFullCoverModifier.swift | 149 ------ .../UIHelper/DisablePreferenceKey.swift | 35 -- .../UIHelper/DisplayErrorState.swift | 11 - .../WalletDemo/UIHelper/ErrorDialogView.swift | 77 --- .../UIHelper/IsLoadingEnvironmentValue.swift | 12 - .../WalletDemo/UIHelper/LoadingModifier.swift | 40 -- .../NavigationTypeUtilModifiers.swift | 37 -- .../UIHelper/RoundedRectWithBorderText.swift | 39 -- .../UIHelper/SpecificRoundedRect.swift | 15 - .../CredentialDetailView.swift | 65 +++ .../CredentialDetailViewModel.swift | 54 ++ .../CredentialDetailViewState.swift | 8 + .../CredentialListRouter.swift | 6 +- .../CredentialsList/CredentialListView.swift | 18 +- .../CredentialListViewModel.swift | 6 +- .../CredentialListViewState.swift | 4 +- .../WalletDemo2/Main/Main2Router.swift | 5 +- .../MediatorPage/MediatorPageViewModel.swift | 1 - .../MessageDetailViewModel.swift | 26 +- .../MessagesList/MessagesListViewModel.swift | 14 +- 148 files changed, 2147 insertions(+), 4549 deletions(-) create mode 100644 AtalaPrismSDK/Domain/Sources/Models/Common/Downloader.swift create mode 100644 AtalaPrismSDK/Pluto/Tests/Helper/KeychianMock.swift delete mode 100644 AtalaPrismSDK/Pollux/Sources/Models/AnonCreds/AnonCredential+ProvableCredential.swift create mode 100644 AtalaPrismSDK/Pollux/Sources/Models/AnonCreds/AnonCredentialDefinition.swift rename AtalaPrismSDK/Pollux/Sources/Models/AnonCreds/{AnonCredential+StorableCredential.swift => AnoncredsCredentialStack+StorableCredential.swift} (87%) create mode 100644 AtalaPrismSDK/Pollux/Sources/Models/AnonCreds/AnoncredsCredentialStack.swift delete mode 100644 AtalaPrismSDK/Pollux/Tests/Mocks/AnoncredsMocks.swift create mode 100644 AtalaPrismSDK/Pollux/Tests/Mocks/MockDownloader.swift create mode 100644 AtalaPrismSDK/Pollux/Tests/Mocks/MockIssuer.swift create mode 100644 AtalaPrismSDK/Pollux/Tests/Mocks/MockProver.swift create mode 100644 AtalaPrismSDK/PrismAgent/Sources/Protocols/Connection/DownloadDataWithResolver.swift create mode 100644 Core/Sources/Helpers/Data+Hex.swift create mode 100644 Core/Sources/Helpers/Data+tryString.swift create mode 100644 Core/Sources/Helpers/String+tryData.swift delete mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/AddNewContact/AddNewContactBuilder.swift delete mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/AddNewContact/AddNewContactState.swift delete mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/AddNewContact/AddNewContactView.swift delete mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/AddNewContact/AddNewContactViewModel.swift delete mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/AddNewContact/UI/AlreadyConnectedView.swift delete mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/AddNewContact/UI/ConfirmConnectionView.swift delete mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/AddNewContact/UI/InsertCodeView.swift delete mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/CredentialDetail/CredentialDetailBuilder.swift delete mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/CredentialDetail/CredentialDetailNeoView.swift delete mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/CredentialDetail/CredentialDetailViewModel.swift delete mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/CredentialsList/CredentialsListBuilder.swift delete mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/CredentialsList/CredentialsListRouter.swift delete mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/CredentialsList/CredentialsListState.swift delete mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/CredentialsList/CredentialsListView.swift delete mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/CredentialsList/CredentialsListViewModel.swift delete mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Dashboard/DashboardBuilder.swift delete mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Dashboard/DashboardRouter.swift delete mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Dashboard/DashboardTabViewController.swift delete mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Dashboard/DashboardView.swift delete mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Dashboard/DashboardViewModelImpl.swift delete mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Home/HomeBuilder.swift delete mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Home/HomeRouter.swift delete mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Home/HomeState.swift delete mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Home/HomeView.swift delete mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Home/HomeViewModel.swift delete mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Home/UI/ActivityListView.swift delete mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Main/MainView.swift delete mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Main/MainViewModel.swift delete mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Main/MainViewRouter.swift delete mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Profile/ProfileHeaderView.swift delete mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/ProofOfRequest/ProofOfRequestBuilder.swift delete mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/ProofOfRequest/ProofOfRequestState.swift delete mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/ProofOfRequest/ProofOfRequestView.swift delete mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/ProofOfRequest/ProofOfRequestViewModel.swift delete mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/ProofOfRequest/UI/ProofOfRequestCheckView.swift delete mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/QRCodeScanner/QRCodeScannerBuilder.swift delete mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/QRCodeScanner/QRCodeScannerRouter.swift delete mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/QRCodeScanner/QRCodeScannerView.swift delete mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/QRCodeScanner/QRCodeScannerViewModel.swift delete mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/QRCodeScanner/UI/CameraViewController.swift delete mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/QRCodeScanner/UI/QRScannerView.swift delete mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/UIHelper/AtalaButton.swift delete mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/UIHelper/Button+Configuration.swift delete mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/UIHelper/CheckButton.swift delete mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/UIHelper/ClearFullCoverModifier.swift delete mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/UIHelper/DisablePreferenceKey.swift delete mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/UIHelper/DisplayErrorState.swift delete mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/UIHelper/ErrorDialogView.swift delete mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/UIHelper/IsLoadingEnvironmentValue.swift delete mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/UIHelper/LoadingModifier.swift delete mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/UIHelper/NavigationTypeUtilModifiers.swift delete mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/UIHelper/RoundedRectWithBorderText.swift delete mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/UIHelper/SpecificRoundedRect.swift create mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Credentials/CredentialDetail/CredentialDetailView.swift create mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Credentials/CredentialDetail/CredentialDetailViewModel.swift create mode 100644 Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Credentials/CredentialDetail/CredentialDetailViewState.swift diff --git a/AtalaPrismSDK/Apollo/Sources/ApolloImpl+KeyRestoration.swift b/AtalaPrismSDK/Apollo/Sources/ApolloImpl+KeyRestoration.swift index 2850b0e0..55392553 100644 --- a/AtalaPrismSDK/Apollo/Sources/ApolloImpl+KeyRestoration.swift +++ b/AtalaPrismSDK/Apollo/Sources/ApolloImpl+KeyRestoration.swift @@ -10,29 +10,30 @@ extension ApolloImpl: KeyRestoration { identifier.hasSuffix("pub") } - public func restorePrivateKey(identifier: String?, data: Data) throws -> PrivateKey { - guard let identifier else { throw ApolloError.restoratonFailedNoIdentifierOrInvalid } - switch identifier { + public func restorePrivateKey(_ key: StorableKey) throws -> PrivateKey { + switch key.restorationIdentifier { case "secp256k1+priv": - return Secp256k1PrivateKey(lockedPrivateKey: .init(data: data)) + return Secp256k1PrivateKey( + lockedPrivateKey: .init(data: key.storableData), + derivationPath: key.index.map { DerivationPath(index: $0) } ?? DerivationPath(index: 0) + ) case "x25519+priv": - return X25519PrivateKey(appleCurve: try .init(rawRepresentation: data)) + return X25519PrivateKey(appleCurve: try .init(rawRepresentation: key.storableData)) case "ed25519+priv": - return Ed25519PrivateKey(appleCurve: try .init(rawRepresentation: data)) + return Ed25519PrivateKey(appleCurve: try .init(rawRepresentation: key.storableData)) default: throw ApolloError.restoratonFailedNoIdentifierOrInvalid } } - public func restorePublicKey(identifier: String?, data: Data) throws -> PublicKey { - guard let identifier else { throw ApolloError.restoratonFailedNoIdentifierOrInvalid } - switch identifier { + public func restorePublicKey(_ key: StorableKey) throws -> PublicKey { + switch key.restorationIdentifier { case "secp256k1+pub": - return Secp256k1PublicKey(lockedPublicKey: .init(bytes: data)) + return Secp256k1PublicKey(lockedPublicKey: .init(bytes: key.storableData)) case "x25519+pub": - return X25519PublicKey(appleCurve: try .init(rawRepresentation: data)) + return X25519PublicKey(appleCurve: try .init(rawRepresentation: key.storableData)) case "ed25519+pub": - return Ed25519PublicKey(appleCurve: try .init(rawRepresentation: data)) + return Ed25519PublicKey(appleCurve: try .init(rawRepresentation: key.storableData)) default: throw ApolloError.restoratonFailedNoIdentifierOrInvalid } diff --git a/AtalaPrismSDK/Apollo/Sources/ApolloImpl+Public.swift b/AtalaPrismSDK/Apollo/Sources/ApolloImpl+Public.swift index 01663ed9..dea7c4be 100644 --- a/AtalaPrismSDK/Apollo/Sources/ApolloImpl+Public.swift +++ b/AtalaPrismSDK/Apollo/Sources/ApolloImpl+Public.swift @@ -102,9 +102,11 @@ returns random mnemonics nerver returns invalid mnemonics switch curve { case .secp256k1: if - let keyData = parameters[KeyProperties.rawKey.rawValue].flatMap({ Data(base64Encoded: $0) }) + let keyData = parameters[KeyProperties.rawKey.rawValue].flatMap({ Data(base64Encoded: $0) }), + let derivationPathStr = parameters[KeyProperties.derivationPath.rawValue] { - return Secp256k1PrivateKey(lockedPrivateKey: .init(data: keyData)) + let derivationPath = try DerivationPath(string: derivationPathStr) + return Secp256k1PrivateKey(lockedPrivateKey: .init(data: keyData), derivationPath: derivationPath) } else { guard let derivationPathStr = parameters[KeyProperties.derivationPath.rawValue], @@ -143,7 +145,7 @@ returns random mnemonics nerver returns invalid mnemonics } } - public func createNewLinkSecret() -> String { - CreateLinkSecretOperation().create() + public func createNewLinkSecret() throws -> String { + try CreateLinkSecretOperation().create() } } diff --git a/AtalaPrismSDK/Apollo/Sources/Model/Ed25519Key.swift b/AtalaPrismSDK/Apollo/Sources/Model/Ed25519Key.swift index cacb8f39..989c0dc1 100644 --- a/AtalaPrismSDK/Apollo/Sources/Model/Ed25519Key.swift +++ b/AtalaPrismSDK/Apollo/Sources/Model/Ed25519Key.swift @@ -35,6 +35,7 @@ extension Ed25519PrivateKey: SignableKey { extension Ed25519PrivateKey: KeychainStorableKey { var restorationIdentifier: String { "ed25519+priv" } var storableData: Data { raw } + var index: Int? { nil } var type: Domain.KeychainStorableKeyProperties.KeyAlgorithm { .rawKey } var keyClass: Domain.KeychainStorableKeyProperties.KeyType { .privateKey } var accessiblity: Domain.KeychainStorableKeyProperties.Accessability? { .firstUnlock(deviceOnly: true) } @@ -62,6 +63,7 @@ struct Ed25519PublicKey: PublicKey { extension Ed25519PublicKey: KeychainStorableKey { var restorationIdentifier: String { "ed25519+pub" } var storableData: Data { raw } + var index: Int? { nil } var type: Domain.KeychainStorableKeyProperties.KeyAlgorithm { .rawKey } var keyClass: Domain.KeychainStorableKeyProperties.KeyType { .publicKey } var accessiblity: Domain.KeychainStorableKeyProperties.Accessability? { .firstUnlock(deviceOnly: true) } diff --git a/AtalaPrismSDK/Apollo/Sources/Model/Secp256k1Key.swift b/AtalaPrismSDK/Apollo/Sources/Model/Secp256k1Key.swift index 83d89ab7..4b9162d5 100644 --- a/AtalaPrismSDK/Apollo/Sources/Model/Secp256k1Key.swift +++ b/AtalaPrismSDK/Apollo/Sources/Model/Secp256k1Key.swift @@ -9,11 +9,14 @@ struct Secp256k1PrivateKey: PrivateKey { let keySpecifications: [String : String] let size: Int let raw: Data + let derivationPath: DerivationPath - init(lockedPrivateKey: LockPrivateKey) { + init(lockedPrivateKey: LockPrivateKey, derivationPath: DerivationPath) { self.lockedPrivateKey = lockedPrivateKey + self.derivationPath = derivationPath self.keySpecifications = [ - KeyProperties.curve.rawValue : "secp256k1" + KeyProperties.curve.rawValue : "secp256k1", + KeyProperties.derivationPath.rawValue : derivationPath.keyPathString() ] self.raw = lockedPrivateKey.data @@ -40,6 +43,7 @@ extension Secp256k1PrivateKey: SignableKey { extension Secp256k1PrivateKey: KeychainStorableKey { var restorationIdentifier: String { "secp256k1+priv" } var storableData: Data { raw } + var index: Int? { derivationPath.index } var type: Domain.KeychainStorableKeyProperties.KeyAlgorithm { .rawKey } var keyClass: Domain.KeychainStorableKeyProperties.KeyType { .privateKey } var accessiblity: Domain.KeychainStorableKeyProperties.Accessability? { .firstUnlock(deviceOnly: true) } @@ -81,6 +85,7 @@ struct Secp256k1PublicKey: PublicKey { extension Secp256k1PublicKey: KeychainStorableKey { var restorationIdentifier: String { "secp256k1+pub" } var storableData: Data { raw } + var index: Int? { nil } var type: Domain.KeychainStorableKeyProperties.KeyAlgorithm { .rawKey } var keyClass: Domain.KeychainStorableKeyProperties.KeyType { .publicKey } var accessiblity: Domain.KeychainStorableKeyProperties.Accessability? { .firstUnlock(deviceOnly: true) } diff --git a/AtalaPrismSDK/Apollo/Sources/Model/X25519Key.swift b/AtalaPrismSDK/Apollo/Sources/Model/X25519Key.swift index ed36ea4c..5f5e27ab 100644 --- a/AtalaPrismSDK/Apollo/Sources/Model/X25519Key.swift +++ b/AtalaPrismSDK/Apollo/Sources/Model/X25519Key.swift @@ -23,6 +23,7 @@ struct X25519PrivateKey: PrivateKey { extension X25519PrivateKey: KeychainStorableKey { var restorationIdentifier: String { "x25519+priv" } var storableData: Data { raw } + var index: Int? { nil } var type: Domain.KeychainStorableKeyProperties.KeyAlgorithm { .rawKey } var keyClass: Domain.KeychainStorableKeyProperties.KeyType { .privateKey } var accessiblity: Domain.KeychainStorableKeyProperties.Accessability? { .firstUnlock(deviceOnly: true) } @@ -50,6 +51,7 @@ struct X25519PublicKey: PublicKey { extension X25519PublicKey: KeychainStorableKey { var restorationIdentifier: String { "x25519+pub" } var storableData: Data { raw } + var index: Int? { nil } var type: Domain.KeychainStorableKeyProperties.KeyAlgorithm { .rawKey } var keyClass: Domain.KeychainStorableKeyProperties.KeyType { .publicKey } var accessiblity: Domain.KeychainStorableKeyProperties.Accessability? { .firstUnlock(deviceOnly: true) } diff --git a/AtalaPrismSDK/Apollo/Sources/Operations/CreateLinkSecretOperation.swift b/AtalaPrismSDK/Apollo/Sources/Operations/CreateLinkSecretOperation.swift index a2effd12..44c0b8c3 100644 --- a/AtalaPrismSDK/Apollo/Sources/Operations/CreateLinkSecretOperation.swift +++ b/AtalaPrismSDK/Apollo/Sources/Operations/CreateLinkSecretOperation.swift @@ -2,7 +2,7 @@ import AnoncredsSwift import Foundation struct CreateLinkSecretOperation { - func create() -> String { - Prover().createLinkSecret().getBigNumber() + func create() throws -> String { + try Prover().createLinkSecret().getValue() } } diff --git a/AtalaPrismSDK/Apollo/Sources/Operations/CreateSec256k1KeyPairOperation.swift b/AtalaPrismSDK/Apollo/Sources/Operations/CreateSec256k1KeyPairOperation.swift index f9a3ebc1..63122ea3 100644 --- a/AtalaPrismSDK/Apollo/Sources/Operations/CreateSec256k1KeyPairOperation.swift +++ b/AtalaPrismSDK/Apollo/Sources/Operations/CreateSec256k1KeyPairOperation.swift @@ -16,6 +16,6 @@ struct CreateSec256k1KeyPairOperation { func compute() throws -> PrivateKey { let derivedKey = try HDKeychain(seed: seed.value).derivedKey(path: keyPath.keyPathString()) - return Secp256k1PrivateKey(lockedPrivateKey: derivedKey.privateKey()) + return Secp256k1PrivateKey(lockedPrivateKey: derivedKey.privateKey(), derivationPath: keyPath) } } diff --git a/AtalaPrismSDK/Apollo/Tests/ECSigningTests.swift b/AtalaPrismSDK/Apollo/Tests/ECSigningTests.swift index fea3160a..a333d3b7 100644 --- a/AtalaPrismSDK/Apollo/Tests/ECSigningTests.swift +++ b/AtalaPrismSDK/Apollo/Tests/ECSigningTests.swift @@ -6,7 +6,10 @@ import XCTest final class ECSigningTests: XCTestCase { func testSigning() throws { let privKey = Secp256k1PrivateKey( - lockedPrivateKey: .init(data: Data(fromBase64URL: "N_JFgvYaReyRXwassz5FHg33A4I6dczzdXrjdHGksmg")!) + lockedPrivateKey: .init( + data: Data(fromBase64URL: "N_JFgvYaReyRXwassz5FHg33A4I6dczzdXrjdHGksmg")! + ), + derivationPath: .init(index: 0) ) let testMessage = "Test".data(using: .utf8)! diff --git a/AtalaPrismSDK/Castor/Sources/Operations/CreatePeerDIDOperation.swift b/AtalaPrismSDK/Castor/Sources/Operations/CreatePeerDIDOperation.swift index db351ed3..ae50b369 100644 --- a/AtalaPrismSDK/Castor/Sources/Operations/CreatePeerDIDOperation.swift +++ b/AtalaPrismSDK/Castor/Sources/Operations/CreatePeerDIDOperation.swift @@ -150,8 +150,7 @@ struct CreatePeerDIDOperation { accept: endpoint.accept ) }.compactMap { $0 } - let encoder = JSONEncoder() - encoder.outputFormatting = .withoutEscapingSlashes + let encoder = JSONEncoder.didComm() if peerDidServices.count == 1, let peerDidService = peerDidServices.first diff --git a/AtalaPrismSDK/Castor/Tests/PeerDIDCreationTests.swift b/AtalaPrismSDK/Castor/Tests/PeerDIDCreationTests.swift index 9eb88b05..39634fbb 100644 --- a/AtalaPrismSDK/Castor/Tests/PeerDIDCreationTests.swift +++ b/AtalaPrismSDK/Castor/Tests/PeerDIDCreationTests.swift @@ -5,7 +5,7 @@ import XCTest final class PeerDIDCreationTests: XCTestCase { func testPeerDIDCreation() throws { - let validPeerDID = "did:peer:2.Ez6LSoHkfN1Y4nK9RCjx7vopWsLrMGNFNgTNZgoCNQrTzmb1n.Vz6MknRZmapV7uYZQuZez9n9N3tQotjRN18UGS68Vcfo6gR4h.SeyJyIjpbImRpZDpleGFtcGxlOnNvbWVtZWRpYXRvciNzb21la2V5Il0sInMiOiJodHRwczovL2V4YW1wbGUuY29tL2VuZHBvaW50IiwiYSI6W10sInQiOiJkbSJ9" + let validPeerDID = "did:peer:2.Ez6LSoHkfN1Y4nK9RCjx7vopWsLrMGNFNgTNZgoCNQrTzmb1n.Vz6MknRZmapV7uYZQuZez9n9N3tQotjRN18UGS68Vcfo6gR4h.SeyJhIjpbXSwiciI6WyJkaWQ6ZXhhbXBsZTpzb21lbWVkaWF0b3Ijc29tZWtleSJdLCJzIjoiaHR0cHM6Ly9leGFtcGxlLmNvbS9lbmRwb2ludCIsInQiOiJkbSJ9" let apollo = ApolloImpl() let castor = CastorImpl(apollo: apollo) let keyAgreementPrivateKey = try apollo.createPrivateKey(parameters: [ diff --git a/AtalaPrismSDK/Domain/Sources/BBs/Apollo.swift b/AtalaPrismSDK/Domain/Sources/BBs/Apollo.swift index 55dfe385..e8c98805 100644 --- a/AtalaPrismSDK/Domain/Sources/BBs/Apollo.swift +++ b/AtalaPrismSDK/Domain/Sources/BBs/Apollo.swift @@ -55,5 +55,5 @@ public protocol Apollo { /// - Returns: The decompressed public key func uncompressedPublicKey(compressedData: Data) -> PublicKey - func createNewLinkSecret() -> String + func createNewLinkSecret() throws -> String } diff --git a/AtalaPrismSDK/Domain/Sources/BBs/Pollux.swift b/AtalaPrismSDK/Domain/Sources/BBs/Pollux.swift index 63573849..bd030cf6 100644 --- a/AtalaPrismSDK/Domain/Sources/BBs/Pollux.swift +++ b/AtalaPrismSDK/Domain/Sources/BBs/Pollux.swift @@ -4,9 +4,9 @@ import Foundation /// Options that can be passed into various operations. public enum CredentialOperationsOptions { case schema(id: String, json: String) // The JSON schema. - case schemasStream(stream: AnyPublisher<[(id: String, json: String)], Error>) // Stream of schemas, only the first batch is considered + case schemaDownloader(downloader: Downloader) // Stream of schemas, only the first batch is considered case credentialDefinition(id: String, json: String) // The JSON Credential Definition - case credentialDefinitionsStream(stream: AnyPublisher<[(id: String, json: String)], Error>) // Stream of credential definitions, only the first batch is considered + case credentialDefinitionDownloader(downloader: Downloader) // Download of credential definitions, only the first batch is considered case linkSecret(id: String, secret: String) // A secret link. case subjectDID(DID) // The decentralized identifier of the subject. case entropy(String) // Entropy for any randomization operation. @@ -21,7 +21,7 @@ public protocol Pollux { /// - Parameter data: The encoded item to parse. /// - Throws: An error if the item cannot be parsed or decoded. /// - Returns: An object representing the parsed item. - func parseCredential(issuedCredential: Message) throws -> Credential + func parseCredential(issuedCredential: Message, options: [CredentialOperationsOptions]) async throws -> Credential /// Restores a previously stored item using the provided restoration identifier and data. /// - Parameters: diff --git a/AtalaPrismSDK/Domain/Sources/Models/Common/Downloader.swift b/AtalaPrismSDK/Domain/Sources/Models/Common/Downloader.swift new file mode 100644 index 00000000..2f7a7283 --- /dev/null +++ b/AtalaPrismSDK/Domain/Sources/Models/Common/Downloader.swift @@ -0,0 +1,5 @@ +import Foundation + +public protocol Downloader { + func downloadFromEndpoint(urlOrDID: String) async throws -> Data +} diff --git a/AtalaPrismSDK/Domain/Sources/Models/Credentials/Credential.swift b/AtalaPrismSDK/Domain/Sources/Models/Credentials/Credential.swift index fd02e036..8706d848 100644 --- a/AtalaPrismSDK/Domain/Sources/Models/Credentials/Credential.swift +++ b/AtalaPrismSDK/Domain/Sources/Models/Credentials/Credential.swift @@ -70,6 +70,8 @@ public protocol Credential { var claims: [Claim] { get } /// Additional properties associated with the credential. var properties: [String: Any] { get } + /// The type of the credential Ex: JWT, Anoncred, W3C + var credentialType: String { get } } public extension Credential { diff --git a/AtalaPrismSDK/Domain/Sources/Models/KeyManagement/ExportableImportableKey.swift b/AtalaPrismSDK/Domain/Sources/Models/KeyManagement/ExportableImportableKey.swift index 5b13d94c..5cbf505c 100644 --- a/AtalaPrismSDK/Domain/Sources/Models/KeyManagement/ExportableImportableKey.swift +++ b/AtalaPrismSDK/Domain/Sources/Models/KeyManagement/ExportableImportableKey.swift @@ -165,7 +165,7 @@ extension PEMKey { /// Returns a PEM-encoded string representation of this `PEMKey`. /// - Returns: A string representing this key in PEM format. public func pemEncoded() -> String { - let base64Data = keyData.base64EncodedString(options: [.lineLength64Characters]) + let base64Data = keyData.base64EncodedString() let beginMarker = "-----BEGIN \(keyType)-----" let endMarker = "-----END \(keyType)-----" diff --git a/AtalaPrismSDK/Domain/Sources/Models/KeyManagement/KeyRestoration.swift b/AtalaPrismSDK/Domain/Sources/Models/KeyManagement/KeyRestoration.swift index ca727e8e..4de010bd 100644 --- a/AtalaPrismSDK/Domain/Sources/Models/KeyManagement/KeyRestoration.swift +++ b/AtalaPrismSDK/Domain/Sources/Models/KeyManagement/KeyRestoration.swift @@ -24,7 +24,7 @@ public protocol KeyRestoration { /// - data: The raw data representing the key. /// - Throws: If the restoration process fails, this method throws an error. /// - Returns: The restored `PrivateKey` instance. - func restorePrivateKey(identifier: String?, data: Data) async throws -> PrivateKey + func restorePrivateKey(_ key: StorableKey) async throws -> PrivateKey /// Restores a public key from the given data. /// - Parameters: @@ -32,24 +32,5 @@ public protocol KeyRestoration { /// - data: The raw data representing the key. /// - Throws: If the restoration process fails, this method throws an error. /// - Returns: The restored `PublicKey` instance. - func restorePublicKey(identifier: String?, data: Data) async throws -> PublicKey -} - -/// Extension of the KeyRestoration protocol to provide additional restoration methods. -public extension KeyRestoration { - /// Restores a private key from the given data, without requiring an identifier. - /// - Parameter data: The raw data representing the key. - /// - Throws: If the restoration process fails, this method throws an error. - /// - Returns: The restored PrivateKey instance. - func restorePrivateKey(data: Data) async throws -> PrivateKey { - try await restorePrivateKey(identifier: nil, data: data) - } - - /// Restores a public key from the given data, without requiring an identifier. - /// - Parameter data: The raw data representing the key. - /// - Throws: If the restoration process fails, this method throws an error. - /// - Returns: The restored `PublicKey` instance. - func restorePublicKey(data: Data) async throws -> PublicKey { - try await restorePublicKey(identifier: nil, data: data) - } + func restorePublicKey(_ key: StorableKey) async throws -> PublicKey } diff --git a/AtalaPrismSDK/Domain/Sources/Models/KeyManagement/StorableKey.swift b/AtalaPrismSDK/Domain/Sources/Models/KeyManagement/StorableKey.swift index ebae71f3..fb8e9ba6 100644 --- a/AtalaPrismSDK/Domain/Sources/Models/KeyManagement/StorableKey.swift +++ b/AtalaPrismSDK/Domain/Sources/Models/KeyManagement/StorableKey.swift @@ -7,6 +7,9 @@ public protocol StorableKey { /// The raw data representation of the key, suitable for storage. var storableData: Data { get } + + /// Indexation of the key is useful to keep track of a derivation index + var index: Int? { get } } /// Extension of the `Key` protocol to provide additional functionality related to storage. diff --git a/AtalaPrismSDK/Pluto/Sources/Domain/Providers/DIDPrivateKeyProvider.swift b/AtalaPrismSDK/Pluto/Sources/Domain/Providers/DIDPrivateKeyProvider.swift index 0cc15a02..1d8d25e3 100644 --- a/AtalaPrismSDK/Pluto/Sources/Domain/Providers/DIDPrivateKeyProvider.swift +++ b/AtalaPrismSDK/Pluto/Sources/Domain/Providers/DIDPrivateKeyProvider.swift @@ -7,4 +7,5 @@ protocol DIDPrivateKeyProvider { func getDIDInfo(did: DID) -> AnyPublisher<(did: DID, privateKeys: [StorableKey], alias: String?)?, Error> func getDIDInfo(alias: String) -> AnyPublisher<[(did: DID, privateKeys: [StorableKey], alias: String?)], Error> func getPrivateKeys(did: DID) -> AnyPublisher<[StorableKey]?, Error> + func getLastKeyIndex() -> AnyPublisher } diff --git a/AtalaPrismSDK/Pluto/Sources/Domain/Providers/DIDProvider.swift b/AtalaPrismSDK/Pluto/Sources/Domain/Providers/DIDProvider.swift index 889e4462..dd2cd1c3 100644 --- a/AtalaPrismSDK/Pluto/Sources/Domain/Providers/DIDProvider.swift +++ b/AtalaPrismSDK/Pluto/Sources/Domain/Providers/DIDProvider.swift @@ -12,5 +12,4 @@ protocol DIDProvider { func getDIDInfo( keyPairIndex: Int ) -> AnyPublisher<(did: DID, keyPairIndex: Int, alias: String?)?, Error> - func getLastKeyPairIndex() -> AnyPublisher } diff --git a/AtalaPrismSDK/Pluto/Sources/Domain/StorableKeyModel.swift b/AtalaPrismSDK/Pluto/Sources/Domain/StorableKeyModel.swift index d45bc77f..4a0b44c6 100644 --- a/AtalaPrismSDK/Pluto/Sources/Domain/StorableKeyModel.swift +++ b/AtalaPrismSDK/Pluto/Sources/Domain/StorableKeyModel.swift @@ -4,4 +4,5 @@ import Foundation struct StorableKeyModel: StorableKey { let restorationIdentifier: String let storableData: Data + let index: Int? } diff --git a/AtalaPrismSDK/Pluto/Sources/PersistentStorage/DAO/CDDIDPrivateKeyDAO+DIDPrivateKeyProvider.swift b/AtalaPrismSDK/Pluto/Sources/PersistentStorage/DAO/CDDIDPrivateKeyDAO+DIDPrivateKeyProvider.swift index a2b2ea1c..b14c9028 100644 --- a/AtalaPrismSDK/Pluto/Sources/PersistentStorage/DAO/CDDIDPrivateKeyDAO+DIDPrivateKeyProvider.swift +++ b/AtalaPrismSDK/Pluto/Sources/PersistentStorage/DAO/CDDIDPrivateKeyDAO+DIDPrivateKeyProvider.swift @@ -56,6 +56,17 @@ extension CDDIDPrivateKeyDAO: DIDPrivateKeyProvider { } .eraseToAnyPublisher() } + + func getLastKeyIndex() -> AnyPublisher { + keyDao.fetchController( + sorting: NSSortDescriptor(key: "index", ascending: true), + context: readContext + ) + .map { + $0.first.map { $0.index?.intValue ?? 0 } ?? 0 + } + .eraseToAnyPublisher() + } } extension CDKey { @@ -79,12 +90,14 @@ extension CDKey { return StorableKeyModel( restorationIdentifier: keychainKey.restorationIdentifier, - storableData: keyData + storableData: keyData, + index: keychainKey.index?.intValue ) case let databaseKey as CDDatabaseKey: return StorableKeyModel( restorationIdentifier: databaseKey.restorationIdentifier, - storableData: databaseKey.storableData + storableData: databaseKey.storableData, + index: databaseKey.index?.intValue ) default: throw UnknownError.somethingWentWrongError( diff --git a/AtalaPrismSDK/Pluto/Sources/PersistentStorage/DAO/CDDIDPrivateKeyDAO+DIDPrivateKeyStore.swift b/AtalaPrismSDK/Pluto/Sources/PersistentStorage/DAO/CDDIDPrivateKeyDAO+DIDPrivateKeyStore.swift index 3016ec28..72790ab9 100644 --- a/AtalaPrismSDK/Pluto/Sources/PersistentStorage/DAO/CDDIDPrivateKeyDAO+DIDPrivateKeyStore.swift +++ b/AtalaPrismSDK/Pluto/Sources/PersistentStorage/DAO/CDDIDPrivateKeyDAO+DIDPrivateKeyStore.swift @@ -91,6 +91,7 @@ private extension CDDatabaseKey { ) { self.identifier = identifier self.storableData = key.storableData + self.index = key.index.map { NSNumber(integerLiteral: $0) } self.restorationIdentifier = key.restorationIdentifier } } @@ -104,6 +105,7 @@ private extension CDKeychainKey { ) { self.identifier = identifier self.restorationIdentifier = key.restorationIdentifier + self.index = key.index.map { NSNumber(integerLiteral: $0) } self.type = key.keyClass.rawValue self.algorithm = key.type.rawValue self.service = service diff --git a/AtalaPrismSDK/Pluto/Sources/PersistentStorage/DAO/CDDIDPrivateKeyDAO.swift b/AtalaPrismSDK/Pluto/Sources/PersistentStorage/DAO/CDDIDPrivateKeyDAO.swift index 1c31ebed..c909bebf 100644 --- a/AtalaPrismSDK/Pluto/Sources/PersistentStorage/DAO/CDDIDPrivateKeyDAO.swift +++ b/AtalaPrismSDK/Pluto/Sources/PersistentStorage/DAO/CDDIDPrivateKeyDAO.swift @@ -6,6 +6,7 @@ struct CDDIDPrivateKeyDAO: CoreDataDAO { typealias CoreDataObject = CDDIDPrivateKey let keychain: KeychainStore & KeychainProvider let keychainService: String + let keyDao: CDKeyDAO let readContext: NSManagedObjectContext let writeContext: NSManagedObjectContext let identifierKey: String? = "did" diff --git a/AtalaPrismSDK/Pluto/Sources/PersistentStorage/Keychain/KeychainDAO.swift b/AtalaPrismSDK/Pluto/Sources/PersistentStorage/Keychain/KeychainDAO.swift index e5606ede..7d3e9284 100644 --- a/AtalaPrismSDK/Pluto/Sources/PersistentStorage/Keychain/KeychainDAO.swift +++ b/AtalaPrismSDK/Pluto/Sources/PersistentStorage/Keychain/KeychainDAO.swift @@ -90,7 +90,7 @@ extension KeychainDAO: KeychainStore { try key.getSecKeyAddItemDictionary(service: service, account: account, accessGroup: accessGroup), nil ) - guard status == errSecSuccess else { + guard status == errSecSuccess || status == errSecDuplicateItem else { throw PlutoError.errorSavingKeyOnKeychainWithStatus(status) } } diff --git a/AtalaPrismSDK/Pluto/Sources/PersistentStorage/Models/CDKey+CoreDataProperties.swift b/AtalaPrismSDK/Pluto/Sources/PersistentStorage/Models/CDKey+CoreDataProperties.swift index 5ee59010..acdd203d 100644 --- a/AtalaPrismSDK/Pluto/Sources/PersistentStorage/Models/CDKey+CoreDataProperties.swift +++ b/AtalaPrismSDK/Pluto/Sources/PersistentStorage/Models/CDKey+CoreDataProperties.swift @@ -8,6 +8,7 @@ extension CDKey { @NSManaged var identifier: String @NSManaged var restorationIdentifier: String + @NSManaged var index: NSNumber? @NSManaged var did: CDDIDPrivateKey? } diff --git a/AtalaPrismSDK/Pluto/Sources/PlutoImpl+Public.swift b/AtalaPrismSDK/Pluto/Sources/PlutoImpl+Public.swift index 433baeaf..e9c946b4 100644 --- a/AtalaPrismSDK/Pluto/Sources/PlutoImpl+Public.swift +++ b/AtalaPrismSDK/Pluto/Sources/PlutoImpl+Public.swift @@ -75,7 +75,7 @@ extension PlutoImpl: Pluto { } public func getPrismLastKeyPairIndex() -> AnyPublisher { - registeredDIDDao.getLastKeyPairIndex() + privateKeyDIDDao.getLastKeyIndex() } public func getAllPeerDIDs() -> AnyPublisher<[(did: DID, privateKeys: [StorableKey], alias: String?)], Error> { diff --git a/AtalaPrismSDK/Pluto/Sources/PlutoImpl.swift b/AtalaPrismSDK/Pluto/Sources/PlutoImpl.swift index 904bca5e..24bcd3e3 100644 --- a/AtalaPrismSDK/Pluto/Sources/PlutoImpl.swift +++ b/AtalaPrismSDK/Pluto/Sources/PlutoImpl.swift @@ -56,6 +56,10 @@ public struct PlutoImpl { let privateKeyDao = CDDIDPrivateKeyDAO( keychain: setup.keychain, keychainService: setup.keychainService, + keyDao: CDKeyDAO( + readContext: manager.mainContext, + writeContext: manager.editContext + ), readContext: manager.mainContext, writeContext: manager.editContext ) diff --git a/AtalaPrismSDK/Pluto/Sources/Resources/PrismPluto.xcdatamodeld/PrismPluto.xcdatamodel/contents b/AtalaPrismSDK/Pluto/Sources/Resources/PrismPluto.xcdatamodeld/PrismPluto.xcdatamodel/contents index 6e84348d..12424d2c 100644 --- a/AtalaPrismSDK/Pluto/Sources/Resources/PrismPluto.xcdatamodeld/PrismPluto.xcdatamodel/contents +++ b/AtalaPrismSDK/Pluto/Sources/Resources/PrismPluto.xcdatamodeld/PrismPluto.xcdatamodel/contents @@ -1,5 +1,5 @@ - + @@ -38,6 +38,7 @@ + diff --git a/AtalaPrismSDK/Pluto/Tests/CDDIDPairDAOTests.swift b/AtalaPrismSDK/Pluto/Tests/CDDIDPairDAOTests.swift index 77094d16..db8ae139 100644 --- a/AtalaPrismSDK/Pluto/Tests/CDDIDPairDAOTests.swift +++ b/AtalaPrismSDK/Pluto/Tests/CDDIDPairDAOTests.swift @@ -2,242 +2,247 @@ import Domain @testable import Pluto import XCTest -//final class CDDIDPairDAOTests: XCTestCase { -// private var coreDataManager: CoreDataManager! -// private var privateKeyDao: CDDIDPrivateKeyDAO! -// private var keyRestoration: KeyRestoration! -// -// override func setUpWithError() throws { -// try super.setUpWithError() -// coreDataManager = CoreDataManager(setup: .init( -// modelPath: .storeName("PrismPluto"), -// storeType: .memory -// )) -// keyRestoration = MockKeyRestoration() -// privateKeyDao = CDDIDPrivateKeyDAO( -// keyRestoration: keyRestoration, -// readContext: coreDataManager.mainContext, -// writeContext: coreDataManager.editContext -// ) -// -// } -// -// func testStoreSingleDIDPair() throws { -// let dao = CDDIDPairDAO( -// readContext: coreDataManager.mainContext, -// writeContext: coreDataManager.editContext, -// privateKeyDIDDAO: privateKeyDao -// ) -// -// let testHolderDID = DID(index: 0) -// let testPrivateKey = MockPrivateKey(curve: .x25519) -// let testOtherDID = DID(index: 1) -// let testName = "test" -// let expectation = expectation(description: "Awaiting publisher") -// let cancellable = privateKeyDao -// .addDID(did: testHolderDID, privateKeys: [testPrivateKey], alias: nil) -// .flatMap { -// dao.addDIDPair( -// pair: .init( -// holder: testHolderDID, -// other: testOtherDID, -// name: testName -// ) -// ) -// } -// .flatMap { -// dao.getPair(holderDID: testHolderDID).first() -// }.sink { _ in } receiveValue: { -// XCTAssertEqual(testHolderDID, $0?.holder) -// XCTAssertEqual(testOtherDID, $0?.other) -// XCTAssertEqual(testName, $0?.name) -// expectation.fulfill() -// } -// -// waitForExpectations(timeout: 5) -// } -// -// func testWhenHolderNotPersistedThenThrowErrorOnAddingPair() throws { -// let dao = CDDIDPairDAO( -// readContext: coreDataManager.mainContext, -// writeContext: coreDataManager.editContext, -// privateKeyDIDDAO: privateKeyDao -// ) -// -// let testHolderDID = DID(index: 0) -// let testOtherDID = DID(index: 1) -// let testName = "test" -// let expectation = expectation(description: "Awaiting publisher") -// let cancellable = dao.addDIDPair( -// pair: .init( -// holder: testHolderDID, -// other: testOtherDID, -// name: testName -// ) -// ) -// .flatMap { -// dao.getPair(holderDID: testHolderDID).first() -// }.sink { -// switch $0 { -// case .failure(let error): -// XCTAssertEqual(error as? PlutoError, .missingDataPersistence( -// type: "Holder DID", -// affecting: "DID Pair") -// ) -// default: -// XCTFail("Error not thrown") -// } -// expectation.fulfill() -// } receiveValue: { _ in } -// -// waitForExpectations(timeout: 5) -// } -// -// func testStoreNoDuplicatedOtherDIDPair() throws { -// let dao = CDDIDPairDAO( -// readContext: coreDataManager.mainContext, -// writeContext: coreDataManager.editContext, -// privateKeyDIDDAO: privateKeyDao -// ) -// -// let testHolderDID1 = DID(index: 0) -// let testHolderDID2 = DID(index: 1) -// let testOtherDID1 = DID(index: 2) -// let testOtherDID2 = DID(index: 2) -// let testPrivateKey = MockPrivateKey(curve: .x25519) -// let testName = "test" -// let expectation = expectation(description: "Awaiting publisher") -// let cancellable = privateKeyDao -// .addDID(did: testHolderDID1, privateKeys: [testPrivateKey], alias: nil) -// .flatMap { -// self.privateKeyDao -// .addDID(did: testHolderDID2, privateKeys: [testPrivateKey], alias: nil) -// } -// .flatMap { -// dao.addDIDPair(pair: .init( -// holder: testHolderDID1, -// other: testOtherDID1, -// name: testName -// ) -// ) -// } -// .flatMap { -// dao.addDIDPair(pair: .init( -// holder: testHolderDID2, -// other: testOtherDID2, -// name: testName -// )) -// } -// .flatMap { -// dao.getAll().first() -// }.sink { -// switch $0 { -// case .failure(let error): -// XCTFail(error.localizedDescription) -// default: -// break -// } -// expectation.fulfill() -// } receiveValue: { -// XCTAssertEqual($0.count, 1) -// } -// -// waitForExpectations(timeout: 5) -// } -// -// func testWhenStoreHolderDIDAlreadyPairedThenThrowError() throws { -// let dao = CDDIDPairDAO( -// readContext: coreDataManager.mainContext, -// writeContext: coreDataManager.editContext, -// privateKeyDIDDAO: privateKeyDao -// ) -// -// let testHolderDID = DID(index: 0) -// let testOtherDID1 = DID(index: 1) -// let testOtherDID2 = DID(index: 2) -// let testPrivateKey = MockPrivateKey(curve: .x25519) -// let testName = "test" -// let expectation = expectation(description: "Awaiting publisher") -// let cancellable = privateKeyDao -// .addDID(did: testHolderDID, privateKeys: [testPrivateKey], alias: nil) -// .flatMap { -// dao.addDIDPair(pair: .init( -// holder: testHolderDID, -// other: testOtherDID1, -// name: testName -// )) -// } -// .flatMap { -// dao.addDIDPair(pair: .init( -// holder: testHolderDID, -// other: testOtherDID2, -// name: testName -// )) -// } -// .flatMap { -// dao.getAll().first() -// }.sink { -// switch $0 { -// case .failure(let error): -// XCTAssertEqual(error as? PlutoError, .duplication(type: "Holder DID/DID Pair")) -// default: -// XCTFail("Error not thrown") -// } -// expectation.fulfill() -// } receiveValue: { _ in } -// -// waitForExpectations(timeout: 999) -// } -// -// func testGetHolderDIDPair() throws { -// let dao = CDDIDPairDAO( -// readContext: coreDataManager.mainContext, -// writeContext: coreDataManager.editContext, -// privateKeyDIDDAO: privateKeyDao -// ) -// -// let testHolderDID1 = DID(index: 0) -// let testHolderDID2 = DID(index: 1) -// let testOtherDID1 = DID(index: 2) -// let testOtherDID2 = DID(index: 3) -// let testPrivateKey = MockPrivateKey(curve: .x25519) -// let testName = "test" -// let expectation = expectation(description: "Awaiting publisher") -// let cancellable = privateKeyDao -// .addDID(did: testHolderDID1, privateKeys: [testPrivateKey], alias: nil) -// .flatMap { -// self.privateKeyDao -// .addDID(did: testHolderDID2, privateKeys: [testPrivateKey], alias: nil) -// } -// .flatMap { -// dao.addDIDPair(pair: .init( -// holder: testHolderDID1, -// other: testOtherDID1, -// name: testName -// )) -// } -// .flatMap { -// dao.addDIDPair(pair: .init( -// holder: testHolderDID2, -// other: testOtherDID2, -// name: testName -// )) -// } -// .flatMap { -// dao.getPair(holderDID: testHolderDID2).first() -// }.sink { -// switch $0 { -// case .failure(let error): -// XCTFail(error.localizedDescription) -// default: -// break -// } -// expectation.fulfill() -// } receiveValue: { -// XCTAssertEqual($0?.holder, testHolderDID2) -// XCTAssertEqual($0?.other, testOtherDID2) -// } -// -// waitForExpectations(timeout: 5) -// } -//} +final class CDDIDPairDAOTests: XCTestCase { + private var coreDataManager: CoreDataManager! + private var privateKeyDao: CDDIDPrivateKeyDAO! + private var keychainMock: KeychainMock! + + override func setUpWithError() throws { + try super.setUpWithError() + coreDataManager = CoreDataManager(setup: .init( + modelPath: .storeName("PrismPluto"), + storeType: .memory + )) + keychainMock = KeychainMock() + privateKeyDao = CDDIDPrivateKeyDAO( + keychain: keychainMock, + keychainService: "test", + keyDao: CDKeyDAO( + readContext: coreDataManager.mainContext, + writeContext: coreDataManager.editContext + ), + readContext: coreDataManager.mainContext, + writeContext: coreDataManager.editContext + ) + + } + + func testStoreSingleDIDPair() throws { + let dao = CDDIDPairDAO( + readContext: coreDataManager.mainContext, + writeContext: coreDataManager.editContext, + privateKeyDIDDAO: privateKeyDao + ) + + let testHolderDID = DID(index: 0) + let testPrivateKey = MockPrivateKey(curve: .x25519) + let testOtherDID = DID(index: 1) + let testName = "test" + let expectation = expectation(description: "Awaiting publisher") + let cancellable = privateKeyDao + .addDID(did: testHolderDID, privateKeys: [testPrivateKey], alias: nil) + .flatMap { + dao.addDIDPair( + pair: .init( + holder: testHolderDID, + other: testOtherDID, + name: testName + ) + ) + } + .flatMap { + dao.getPair(holderDID: testHolderDID).first() + }.sink { _ in } receiveValue: { + XCTAssertEqual(testHolderDID, $0?.holder) + XCTAssertEqual(testOtherDID, $0?.other) + XCTAssertEqual(testName, $0?.name) + expectation.fulfill() + } + + waitForExpectations(timeout: 5) + } + + func testWhenHolderNotPersistedThenThrowErrorOnAddingPair() throws { + let dao = CDDIDPairDAO( + readContext: coreDataManager.mainContext, + writeContext: coreDataManager.editContext, + privateKeyDIDDAO: privateKeyDao + ) + + let testHolderDID = DID(index: 0) + let testOtherDID = DID(index: 1) + let testName = "test" + let expectation = expectation(description: "Awaiting publisher") + let cancellable = dao.addDIDPair( + pair: .init( + holder: testHolderDID, + other: testOtherDID, + name: testName + ) + ) + .flatMap { + dao.getPair(holderDID: testHolderDID).first() + }.sink { + switch $0 { + case .failure(let error): + XCTAssertEqual(error as? PlutoError, .missingDataPersistence( + type: "Holder DID", + affecting: "DID Pair") + ) + default: + XCTFail("Error not thrown") + } + expectation.fulfill() + } receiveValue: { _ in } + + waitForExpectations(timeout: 5) + } + + func testStoreNoDuplicatedOtherDIDPair() throws { + let dao = CDDIDPairDAO( + readContext: coreDataManager.mainContext, + writeContext: coreDataManager.editContext, + privateKeyDIDDAO: privateKeyDao + ) + + let testHolderDID1 = DID(index: 0) + let testHolderDID2 = DID(index: 1) + let testOtherDID1 = DID(index: 2) + let testOtherDID2 = DID(index: 2) + let testPrivateKey = MockPrivateKey(curve: .x25519) + let testName = "test" + let expectation = expectation(description: "Awaiting publisher") + let cancellable = privateKeyDao + .addDID(did: testHolderDID1, privateKeys: [testPrivateKey], alias: nil) + .flatMap { + self.privateKeyDao + .addDID(did: testHolderDID2, privateKeys: [testPrivateKey], alias: nil) + } + .flatMap { + dao.addDIDPair(pair: .init( + holder: testHolderDID1, + other: testOtherDID1, + name: testName + ) + ) + } + .flatMap { + dao.addDIDPair(pair: .init( + holder: testHolderDID2, + other: testOtherDID2, + name: testName + )) + } + .flatMap { + dao.getAll().first() + }.sink { + switch $0 { + case .failure(let error): + XCTFail(error.localizedDescription) + default: + break + } + expectation.fulfill() + } receiveValue: { + XCTAssertEqual($0.count, 1) + } + + waitForExpectations(timeout: 5) + } + + func testWhenStoreHolderDIDAlreadyPairedThenThrowError() throws { + let dao = CDDIDPairDAO( + readContext: coreDataManager.mainContext, + writeContext: coreDataManager.editContext, + privateKeyDIDDAO: privateKeyDao + ) + + let testHolderDID = DID(index: 0) + let testOtherDID1 = DID(index: 1) + let testOtherDID2 = DID(index: 2) + let testPrivateKey = MockPrivateKey(curve: .x25519) + let testName = "test" + let expectation = expectation(description: "Awaiting publisher") + let cancellable = privateKeyDao + .addDID(did: testHolderDID, privateKeys: [testPrivateKey], alias: nil) + .flatMap { + dao.addDIDPair(pair: .init( + holder: testHolderDID, + other: testOtherDID1, + name: testName + )) + } + .flatMap { + dao.addDIDPair(pair: .init( + holder: testHolderDID, + other: testOtherDID2, + name: testName + )) + } + .flatMap { + dao.getAll().first() + }.sink { + switch $0 { + case .failure(let error): + XCTAssertEqual(error as? PlutoError, .duplication(type: "Holder DID/DID Pair")) + default: + XCTFail("Error not thrown") + } + expectation.fulfill() + } receiveValue: { _ in } + + waitForExpectations(timeout: 999) + } + + func testGetHolderDIDPair() throws { + let dao = CDDIDPairDAO( + readContext: coreDataManager.mainContext, + writeContext: coreDataManager.editContext, + privateKeyDIDDAO: privateKeyDao + ) + + let testHolderDID1 = DID(index: 0) + let testHolderDID2 = DID(index: 1) + let testOtherDID1 = DID(index: 2) + let testOtherDID2 = DID(index: 3) + let testPrivateKey = MockPrivateKey(curve: .x25519) + let testName = "test" + let expectation = expectation(description: "Awaiting publisher") + let cancellable = privateKeyDao + .addDID(did: testHolderDID1, privateKeys: [testPrivateKey], alias: nil) + .flatMap { + self.privateKeyDao + .addDID(did: testHolderDID2, privateKeys: [testPrivateKey], alias: nil) + } + .flatMap { + dao.addDIDPair(pair: .init( + holder: testHolderDID1, + other: testOtherDID1, + name: testName + )) + } + .flatMap { + dao.addDIDPair(pair: .init( + holder: testHolderDID2, + other: testOtherDID2, + name: testName + )) + } + .flatMap { + dao.getPair(holderDID: testHolderDID2).first() + }.sink { + switch $0 { + case .failure(let error): + XCTFail(error.localizedDescription) + default: + break + } + expectation.fulfill() + } receiveValue: { + XCTAssertEqual($0?.holder, testHolderDID2) + XCTAssertEqual($0?.other, testOtherDID2) + } + + waitForExpectations(timeout: 5) + } +} diff --git a/AtalaPrismSDK/Pluto/Tests/CDDIDPrivateKeyDAOTests.swift b/AtalaPrismSDK/Pluto/Tests/CDDIDPrivateKeyDAOTests.swift index d2b67b12..3e9bd731 100644 --- a/AtalaPrismSDK/Pluto/Tests/CDDIDPrivateKeyDAOTests.swift +++ b/AtalaPrismSDK/Pluto/Tests/CDDIDPrivateKeyDAOTests.swift @@ -4,7 +4,7 @@ import XCTest final class CDDIDPrivateKeyDAOTestsTests: XCTestCase { private var coreDataManager: CoreDataManager! - private var keyRestoration: MockKeyRestoration! + private var keychainMock: KeychainMock! override func setUpWithError() throws { try super.setUpWithError() @@ -13,138 +13,159 @@ final class CDDIDPrivateKeyDAOTestsTests: XCTestCase { storeType: .memory )) - keyRestoration = MockKeyRestoration() + keychainMock = KeychainMock() } -// func testStoreSingleDID() throws { -// let dao = CDDIDPrivateKeyDAO( -// readContext: coreDataManager.mainContext, -// writeContext: coreDataManager.editContext -// ) -// -// let testDID = DID(method: "test", methodId: "test") -// let testPrivateKey = MockPrivateKey(curve: .x25519) -// let expectation = expectation(description: "Awaiting publisher") -// let cancellable = dao.addDID( -// did: testDID, -// privateKeys: [testPrivateKey], -// alias: nil -// ).flatMap { -// dao.getDIDInfo(did: testDID) -// }.first().sink { _ in } receiveValue: { -// XCTAssertEqual(testDID, $0?.did) -// XCTAssertEqual([testPrivateKey], $0?.privateKeys.map { $0 as? MockPrivateKey }) -// expectation.fulfill() -// } -// -// waitForExpectations(timeout: 5) -// } -// -// func testStoreNoDuplicatedDID() throws { -// let dao = CDDIDPrivateKeyDAO( -// keyRestoration: keyRestoration, -// readContext: coreDataManager.mainContext, -// writeContext: coreDataManager.editContext -// ) -// -// let testDID = DID(method: "test", methodId: "test") -// let testPrivateKey = MockPrivateKey(curve: .ed25519) -// let expectation = expectation(description: "Awaiting publisher") -// let cancellable = dao.addDID( -// did: testDID, -// privateKeys: [testPrivateKey], -// alias: nil -// ).flatMap { -// dao.addDID( -// did: testDID, -// privateKeys: [testPrivateKey], -// alias: nil -// ) -// } -// .flatMap { -// dao.getAll() -// } -// .first() -// .sink { _ in } receiveValue: { -// XCTAssertEqual($0.count, 1) -// expectation.fulfill() -// } -// -// waitForExpectations(timeout: 5) -// } -// -// func testGetAllDIDs() throws { -// let dao = CDDIDPrivateKeyDAO( -// keyRestoration: keyRestoration, -// readContext: coreDataManager.mainContext, -// writeContext: coreDataManager.editContext -// ) -// -// let testDID1 = DID(method: "test1", methodId: "test1") -// let testPrivateKey1 = MockPrivateKey(curve: .x25519) -// -// let testDID2 = DID(method: "test2", methodId: "test2") -// let testPrivateKey2 = MockPrivateKey(curve: .ed25519) -// -// let expectation = expectation(description: "Awaiting publisher") -// let cancellable = dao.addDID( -// did: testDID1, -// privateKeys: [testPrivateKey1], -// alias: nil -// ).flatMap { -// dao.addDID( -// did: testDID2, -// privateKeys: [testPrivateKey2], -// alias: nil -// ) -// } -// .flatMap { -// dao.getAll() -// } -// .first() -// .sink { _ in } receiveValue: { -// XCTAssertEqual($0.count, 2) -// expectation.fulfill() -// } -// -// waitForExpectations(timeout: 5) -// } -// -// func testGetDIDInfoByDID() throws { -// let dao = CDDIDPrivateKeyDAO( -// keyRestoration: keyRestoration, -// readContext: coreDataManager.mainContext, -// writeContext: coreDataManager.editContext -// ) -// -// let testDID1 = DID(method: "test1", methodId: "test1") -// let testPrivateKey1 = MockPrivateKey(curve: .x25519) -// -// let testDID2 = DID(method: "test2", methodId: "test2") -// let testPrivateKey2 = MockPrivateKey(curve: .ed25519) -// -// let expectation = expectation(description: "Awaiting publisher") -// let cancellable = dao.addDID( -// did: testDID1, -// privateKeys: [testPrivateKey1], -// alias: nil -// ).flatMap { -// dao.addDID( -// did: testDID2, -// privateKeys: [testPrivateKey2], -// alias: nil -// ) -// } -// .flatMap { -// dao.getDIDInfo(did: testDID2) -// } -// .first() -// .sink { _ in } receiveValue: { -// XCTAssertEqual(testDID2, $0?.did) -// XCTAssertEqual([testPrivateKey2], $0?.privateKeys.map { $0 as? MockPrivateKey }) -// expectation.fulfill() -// } -// -// waitForExpectations(timeout: 5) -// } + func testStoreSingleDID() throws { + let dao = CDDIDPrivateKeyDAO( + keychain: keychainMock, + keychainService: "test", + keyDao: CDKeyDAO( + readContext: coreDataManager.mainContext, + writeContext: coreDataManager.editContext + ), + readContext: coreDataManager.mainContext, + writeContext: coreDataManager.editContext + ) + + let testDID = DID(method: "test", methodId: "test") + let testPrivateKey = MockPrivateKey(curve: .x25519) + let expectation = expectation(description: "Awaiting publisher") + let cancellable = dao.addDID( + did: testDID, + privateKeys: [testPrivateKey], + alias: nil + ).flatMap { + dao.getDIDInfo(did: testDID) + }.first().sink { _ in } receiveValue: { + XCTAssertEqual(testDID, $0?.did) + XCTAssertEqual([testPrivateKey.storableData], $0?.privateKeys.map { $0.storableData }) + expectation.fulfill() + } + + waitForExpectations(timeout: 5) + } + + func testStoreNoDuplicatedDID() throws { + let dao = CDDIDPrivateKeyDAO( + keychain: keychainMock, + keychainService: "test", + keyDao: CDKeyDAO( + readContext: coreDataManager.mainContext, + writeContext: coreDataManager.editContext + ), + readContext: coreDataManager.mainContext, + writeContext: coreDataManager.editContext + ) + + let testDID = DID(method: "test", methodId: "test") + let testPrivateKey = MockPrivateKey(curve: .ed25519) + let expectation = expectation(description: "Awaiting publisher") + let cancellable = dao.addDID( + did: testDID, + privateKeys: [testPrivateKey], + alias: nil + ).flatMap { + dao.addDID( + did: testDID, + privateKeys: [testPrivateKey], + alias: nil + ) + } + .flatMap { + dao.getAll() + } + .first() + .sink { _ in } receiveValue: { + XCTAssertEqual($0.count, 1) + expectation.fulfill() + } + + waitForExpectations(timeout: 5) + } + + func testGetAllDIDs() throws { + let dao = CDDIDPrivateKeyDAO( + keychain: keychainMock, + keychainService: "test", + keyDao: CDKeyDAO( + readContext: coreDataManager.mainContext, + writeContext: coreDataManager.editContext + ), + readContext: coreDataManager.mainContext, + writeContext: coreDataManager.editContext + ) + + let testDID1 = DID(method: "test1", methodId: "test1") + let testPrivateKey1 = MockPrivateKey(curve: .x25519) + + let testDID2 = DID(method: "test2", methodId: "test2") + let testPrivateKey2 = MockPrivateKey(curve: .ed25519) + + let expectation = expectation(description: "Awaiting publisher") + let cancellable = dao.addDID( + did: testDID1, + privateKeys: [testPrivateKey1], + alias: nil + ).flatMap { + dao.addDID( + did: testDID2, + privateKeys: [testPrivateKey2], + alias: nil + ) + } + .flatMap { + dao.getAll() + } + .first() + .sink { _ in } receiveValue: { + XCTAssertEqual($0.count, 2) + expectation.fulfill() + } + + waitForExpectations(timeout: 5) + } + + func testGetDIDInfoByDID() throws { + let dao = CDDIDPrivateKeyDAO( + keychain: keychainMock, + keychainService: "test", + keyDao: CDKeyDAO( + readContext: coreDataManager.mainContext, + writeContext: coreDataManager.editContext + ), + readContext: coreDataManager.mainContext, + writeContext: coreDataManager.editContext + ) + + let testDID1 = DID(method: "test1", methodId: "test1") + let testPrivateKey1 = MockPrivateKey(str: "Key1", curve: .x25519) + + let testDID2 = DID(method: "test2", methodId: "test2") + let testPrivateKey2 = MockPrivateKey(str: "Key2", curve: .ed25519) + + let expectation = expectation(description: "Awaiting publisher") + let cancellable = dao.addDID( + did: testDID1, + privateKeys: [testPrivateKey1], + alias: nil + ).flatMap { + dao.addDID( + did: testDID2, + privateKeys: [testPrivateKey2], + alias: nil + ) + } + .flatMap { + dao.getDIDInfo(did: testDID2) + } + .first() + .sink { _ in } receiveValue: { + XCTAssertEqual(testDID2, $0?.did) + XCTAssertEqual([testPrivateKey2.storableData], $0?.privateKeys.map { $0.storableData }) + expectation.fulfill() + } + + waitForExpectations(timeout: 5) + } } diff --git a/AtalaPrismSDK/Pluto/Tests/CDMessagesDAOTests.swift b/AtalaPrismSDK/Pluto/Tests/CDMessagesDAOTests.swift index deba1cdf..4776d293 100644 --- a/AtalaPrismSDK/Pluto/Tests/CDMessagesDAOTests.swift +++ b/AtalaPrismSDK/Pluto/Tests/CDMessagesDAOTests.swift @@ -2,193 +2,198 @@ import Domain @testable import Pluto import XCTest -//final class CDMessagesDAOTests: XCTestCase { -// private var coreDataManager: CoreDataManager! -// private var privateDAO: CDDIDPrivateKeyDAO! -// private var pairDAO: CDDIDPairDAO! -// private var keyRestoration: KeyRestoration! -// -// override func setUpWithError() throws { -// try super.setUpWithError() -// keyRestoration = MockKeyRestoration() -// coreDataManager = CoreDataManager(setup: .init( -// modelPath: .storeName("PrismPluto"), -// storeType: .memory -// )) -// privateDAO = CDDIDPrivateKeyDAO( -// keyRestoration: keyRestoration, -// readContext: coreDataManager.mainContext, -// writeContext: coreDataManager.editContext -// ) -// pairDAO = CDDIDPairDAO( -// readContext: coreDataManager.mainContext, -// writeContext: coreDataManager.editContext, -// privateKeyDIDDAO: privateDAO -// ) -// } -// -// func testStoreMessage() throws { -// let dao = CDMessageDAO( -// readContext: coreDataManager.mainContext, -// writeContext: coreDataManager.editContext, -// pairDAO: pairDAO -// ) -// let testHolderDID = DID(index: 0) -// let testPrivateKey = MockPrivateKey(curve: .x25519) -// let testOtherDID = DID(index: 1) -// let testName = "test" -// let testMessage = Message( -// piuri: "test", -// from: testHolderDID, -// to: testOtherDID, -// body: Data() -// ) -// let expectation = expectation(description: "Awaiting publisher") -// let cancellable = privateDAO -// .addDID(did: testHolderDID, privateKeys: [testPrivateKey], alias: nil) -// .flatMap { -// self.pairDAO.addDIDPair(pair: .init( -// holder: testHolderDID, -// other: testOtherDID, -// name: testName -// )) -// } -// .flatMap { -// dao.addMessage(msg: testMessage, direction: .received) -// } -// .flatMap { -// dao.getMessage(id: testMessage.id).first() -// }.sink { -// switch $0 { -// case .failure(let error): -// print(error.localizedDescription) -// XCTFail(error.localizedDescription) -// default: -// break -// } -// expectation.fulfill() -// } receiveValue: { -// XCTAssertEqual(testMessage, $0) -// } -// -// waitForExpectations(timeout: 5) -// } -// -// func testStoreNoDuplicatedMessage() throws { -// let dao = CDMessageDAO( -// readContext: coreDataManager.mainContext, -// writeContext: coreDataManager.editContext, -// pairDAO: pairDAO -// ) -// let testHolderDID = DID(index: 0) -// let testPrivateKey = MockPrivateKey(curve: .x25519) -// let testOtherDID = DID(index: 1) -// let testName = "test" -// let testMessage = Message( -// piuri: "test", -// from: testHolderDID, -// to: testOtherDID, -// body: Data() -// ) -// let expectation = expectation(description: "Awaiting publisher") -// let cancellable = privateDAO -// .addDID(did: testHolderDID, privateKeys: [testPrivateKey], alias: nil) -// .flatMap { -// self.pairDAO.addDIDPair(pair: .init( -// holder: testHolderDID, -// other: testOtherDID, -// name: testName -// )) -// } -// .flatMap { -// dao.addMessage(msg: testMessage, direction: .received) -// } -// .flatMap { -// dao.addMessage(msg: testMessage, direction: .received) -// } -// .flatMap { -// dao.getAll().first() -// }.sink { -// switch $0 { -// case .failure(let error): -// XCTFail(error.localizedDescription) -// default: -// break -// } -// } receiveValue: { -// XCTAssertEqual($0.count, 1) -// expectation.fulfill() -// } -// -// waitForExpectations(timeout: 5) -// } -// -// func testGetMessageForDIDPairComponent() throws { -// let dao = CDMessageDAO( -// readContext: coreDataManager.mainContext, -// writeContext: coreDataManager.editContext, -// pairDAO: pairDAO -// ) -// let testHolderDID = DID(index: 0) -// let testPrivateKey = MockPrivateKey(curve: .ed25519) -// let testOtherDID = DID(index: 1) -// let testHolderDID2 = DID(index: 2) -// let testPrivateKey2 = MockPrivateKey(curve: .x25519) -// let testOtherDID2 = DID(index: 3) -// let testName = "test" -// let testMessage1 = Message( -// piuri: "test", -// from: testHolderDID, -// to: testOtherDID, -// body: Data() -// ) -// let testMessage2 = Message( -// piuri: "test2", -// from: testHolderDID2, -// to: testOtherDID2, -// body: Data() -// ) -// let expectation = expectation(description: "Awaiting publisher") -// let cancellable = privateDAO -// .addDID(did: testHolderDID, privateKeys: [testPrivateKey], alias: nil) -// .flatMap { -// self.privateDAO -// .addDID(did: testHolderDID2, privateKeys: [testPrivateKey2], alias: nil) -// } -// .flatMap { -// self.pairDAO.addDIDPair(pair: .init( -// holder: testHolderDID, -// other: testOtherDID, -// name: testName -// )) -// } -// .flatMap { -// self.pairDAO.addDIDPair(pair: .init( -// holder: testHolderDID2, -// other: testOtherDID2, -// name: testName -// )) -// } -// .flatMap { -// dao.addMessage(msg: testMessage1, direction: .received) -// } -// .flatMap { -// dao.addMessage(msg: testMessage2, direction: .received) -// } -// .flatMap { -// dao.getAllFor(did: testHolderDID2).first() -// }.sink { -// switch $0 { -// case .failure(let error): -// XCTFail(error.localizedDescription) -// default: -// break -// } -// expectation.fulfill() -// } receiveValue: { -// XCTAssertEqual(testMessage2, $0.first) -// } -// -// waitForExpectations(timeout: 5) -// } -//} +final class CDMessagesDAOTests: XCTestCase { + private var coreDataManager: CoreDataManager! + private var privateDAO: CDDIDPrivateKeyDAO! + private var pairDAO: CDDIDPairDAO! + private var keychainMock: KeychainMock! + + override func setUpWithError() throws { + try super.setUpWithError() + keychainMock = KeychainMock() + coreDataManager = CoreDataManager(setup: .init( + modelPath: .storeName("PrismPluto"), + storeType: .memory + )) + privateDAO = CDDIDPrivateKeyDAO( + keychain: keychainMock, + keychainService: "test", + keyDao: CDKeyDAO( + readContext: coreDataManager.mainContext, + writeContext: coreDataManager.editContext + ), + readContext: coreDataManager.mainContext, + writeContext: coreDataManager.editContext + ) + pairDAO = CDDIDPairDAO( + readContext: coreDataManager.mainContext, + writeContext: coreDataManager.editContext, + privateKeyDIDDAO: privateDAO + ) + } + + func testStoreMessage() throws { + let dao = CDMessageDAO( + readContext: coreDataManager.mainContext, + writeContext: coreDataManager.editContext, + pairDAO: pairDAO + ) + let testHolderDID = DID(index: 0) + let testPrivateKey = MockPrivateKey(curve: .x25519) + let testOtherDID = DID(index: 1) + let testName = "test" + let testMessage = Message( + piuri: "test", + from: testHolderDID, + to: testOtherDID, + body: Data() + ) + let expectation = expectation(description: "Awaiting publisher") + let cancellable = privateDAO + .addDID(did: testHolderDID, privateKeys: [testPrivateKey], alias: nil) + .flatMap { + self.pairDAO.addDIDPair(pair: .init( + holder: testHolderDID, + other: testOtherDID, + name: testName + )) + } + .flatMap { + dao.addMessage(msg: testMessage, direction: .received) + } + .flatMap { + dao.getMessage(id: testMessage.id).first() + }.sink { + switch $0 { + case .failure(let error): + print(error.localizedDescription) + XCTFail(error.localizedDescription) + default: + break + } + expectation.fulfill() + } receiveValue: { + XCTAssertEqual(testMessage, $0) + } + + waitForExpectations(timeout: 5) + } + + func testStoreNoDuplicatedMessage() throws { + let dao = CDMessageDAO( + readContext: coreDataManager.mainContext, + writeContext: coreDataManager.editContext, + pairDAO: pairDAO + ) + let testHolderDID = DID(index: 0) + let testPrivateKey = MockPrivateKey(curve: .x25519) + let testOtherDID = DID(index: 1) + let testName = "test" + let testMessage = Message( + piuri: "test", + from: testHolderDID, + to: testOtherDID, + body: Data() + ) + let expectation = expectation(description: "Awaiting publisher") + let cancellable = privateDAO + .addDID(did: testHolderDID, privateKeys: [testPrivateKey], alias: nil) + .flatMap { + self.pairDAO.addDIDPair(pair: .init( + holder: testHolderDID, + other: testOtherDID, + name: testName + )) + } + .flatMap { + dao.addMessage(msg: testMessage, direction: .received) + } + .flatMap { + dao.addMessage(msg: testMessage, direction: .received) + } + .flatMap { + dao.getAll().first() + }.sink { + switch $0 { + case .failure(let error): + XCTFail(error.localizedDescription) + default: + break + } + } receiveValue: { + XCTAssertEqual($0.count, 1) + expectation.fulfill() + } + + waitForExpectations(timeout: 5) + } + + func testGetMessageForDIDPairComponent() throws { + let dao = CDMessageDAO( + readContext: coreDataManager.mainContext, + writeContext: coreDataManager.editContext, + pairDAO: pairDAO + ) + let testHolderDID = DID(index: 0) + let testPrivateKey = MockPrivateKey(curve: .ed25519) + let testOtherDID = DID(index: 1) + let testHolderDID2 = DID(index: 2) + let testPrivateKey2 = MockPrivateKey(curve: .x25519) + let testOtherDID2 = DID(index: 3) + let testName = "test" + let testMessage1 = Message( + piuri: "test", + from: testHolderDID, + to: testOtherDID, + body: Data() + ) + let testMessage2 = Message( + piuri: "test2", + from: testHolderDID2, + to: testOtherDID2, + body: Data() + ) + let expectation = expectation(description: "Awaiting publisher") + let cancellable = privateDAO + .addDID(did: testHolderDID, privateKeys: [testPrivateKey], alias: nil) + .flatMap { + self.privateDAO + .addDID(did: testHolderDID2, privateKeys: [testPrivateKey2], alias: nil) + } + .flatMap { + self.pairDAO.addDIDPair(pair: .init( + holder: testHolderDID, + other: testOtherDID, + name: testName + )) + } + .flatMap { + self.pairDAO.addDIDPair(pair: .init( + holder: testHolderDID2, + other: testOtherDID2, + name: testName + )) + } + .flatMap { + dao.addMessage(msg: testMessage1, direction: .received) + } + .flatMap { + dao.addMessage(msg: testMessage2, direction: .received) + } + .flatMap { + dao.getAllFor(did: testHolderDID2).first() + }.sink { + switch $0 { + case .failure(let error): + XCTFail(error.localizedDescription) + default: + break + } + expectation.fulfill() + } receiveValue: { + XCTAssertEqual(testMessage2, $0.first) + } + + waitForExpectations(timeout: 5) + } +} diff --git a/AtalaPrismSDK/Pluto/Tests/Helper/KeyRestoration+Test.swift b/AtalaPrismSDK/Pluto/Tests/Helper/KeyRestoration+Test.swift index bd78a48b..891b8a1f 100644 --- a/AtalaPrismSDK/Pluto/Tests/Helper/KeyRestoration+Test.swift +++ b/AtalaPrismSDK/Pluto/Tests/Helper/KeyRestoration+Test.swift @@ -10,11 +10,11 @@ struct MockKeyRestoration: KeyRestoration { identifier == "MockPublic" } - func restorePrivateKey(identifier: String?, data: Data) throws -> PrivateKey { - MockPrivateKey(raw: data) + func restorePrivateKey(_ key: Domain.StorableKey) throws -> PrivateKey { + MockPrivateKey(raw: key.storableData) } - func restorePublicKey(identifier: String?, data: Data) throws -> PublicKey { - MockPublicKey(raw: data) + func restorePublicKey(_ key: Domain.StorableKey) throws -> PublicKey { + MockPublicKey(raw: key.storableData) } } diff --git a/AtalaPrismSDK/Pluto/Tests/Helper/KeychianMock.swift b/AtalaPrismSDK/Pluto/Tests/Helper/KeychianMock.swift new file mode 100644 index 00000000..c4653f6c --- /dev/null +++ b/AtalaPrismSDK/Pluto/Tests/Helper/KeychianMock.swift @@ -0,0 +1,28 @@ +import Domain +import Foundation +@testable import Pluto + +class KeychainMock: KeychainStore, KeychainProvider { + var keys: [String: KeychainStorableKey] = [:] + + func getKey( + service: String, + account: String, + tag: String?, + algorithm: KeychainStorableKeyProperties.KeyAlgorithm, + type: KeychainStorableKeyProperties.KeyType + ) throws -> Data { + guard let key = keys[service+account] else { + throw PlutoError.errorRetrivingKeyFromKeychainKeyNotFound(service: service, account: account, applicationLabel: tag) + } + return key.storableData + } + + func addKey( + _ key: KeychainStorableKey, + service: String, + account: String + ) throws { + keys[service+account] = key + } +} diff --git a/AtalaPrismSDK/Pluto/Tests/Helper/PrivateKey+Test.swift b/AtalaPrismSDK/Pluto/Tests/Helper/PrivateKey+Test.swift index 2da5821a..e70971df 100644 --- a/AtalaPrismSDK/Pluto/Tests/Helper/PrivateKey+Test.swift +++ b/AtalaPrismSDK/Pluto/Tests/Helper/PrivateKey+Test.swift @@ -6,6 +6,7 @@ struct MockPrivateKey: PrivateKey, StorableKey, Equatable { let keySpecifications: [String : String] let size = 0 let raw: Data + var index: Int? = 0 let restorationIdentifier = "MockPrivate" var storableData: Data { raw } diff --git a/AtalaPrismSDK/Pollux/Sources/Models/AnonCreds/AnonCredential+ProvableCredential.swift b/AtalaPrismSDK/Pollux/Sources/Models/AnonCreds/AnonCredential+ProvableCredential.swift deleted file mode 100644 index e95b43e9..00000000 --- a/AtalaPrismSDK/Pollux/Sources/Models/AnonCreds/AnonCredential+ProvableCredential.swift +++ /dev/null @@ -1,2 +0,0 @@ -import Domain -import Foundation diff --git a/AtalaPrismSDK/Pollux/Sources/Models/AnonCreds/AnonCredential.swift b/AtalaPrismSDK/Pollux/Sources/Models/AnonCreds/AnonCredential.swift index 1260faef..c524275d 100644 --- a/AtalaPrismSDK/Pollux/Sources/Models/AnonCreds/AnonCredential.swift +++ b/AtalaPrismSDK/Pollux/Sources/Models/AnonCreds/AnonCredential.swift @@ -1,4 +1,5 @@ import AnoncredsSwift +import Core import Domain import Foundation @@ -59,9 +60,7 @@ struct AnonCredential { func getAnoncred() throws -> AnoncredsSwift.Credential { let json = try JSONEncoder.didComm().encode(self) - guard let jsonString = String(data: json, encoding: .utf8) else { - throw UnknownError.somethingWentWrongError() - } + let jsonString = try json.toString() return try .init(jsonString: jsonString) } } @@ -114,45 +113,3 @@ extension AnonCredential: Codable { case witness } } - -extension AnonCredential: Domain.Credential { - var id: String { - guard - let jsonData = try? JSONEncoder().encode(self), - let identifier = String(data: jsonData.sha256, encoding: .utf8) - else { - assert(true, "This should never happen") - return "" - } - return identifier - } - - var issuer: String { - "" - } - - var subject: String? { - nil - } - - var claims: [Domain.Claim] { - values.map { - .init(key: $0, value: .string($1.raw)) - } - } - - var properties: [String : Any] { - let properties = [ - "schemaId" : schemaId, - "credentialDefinitionId" : credentialDefinitionId, -// "signatureJson" : signatureJson, -// "signatureCorrectnessProofJson" : signatureCorrectnessProofJson, -// "witnessJson" : witnessJson - ] as [String : Any] - -// revocationRegistryId.map { properties["revocationRegistryId"] = $0 } -// revocationRegistryJson.map { properties["revocationRegistryJson"] = $0 } - - return properties - } -} diff --git a/AtalaPrismSDK/Pollux/Sources/Models/AnonCreds/AnonCredentialDefinition.swift b/AtalaPrismSDK/Pollux/Sources/Models/AnonCreds/AnonCredentialDefinition.swift new file mode 100644 index 00000000..8e89cb18 --- /dev/null +++ b/AtalaPrismSDK/Pollux/Sources/Models/AnonCreds/AnonCredentialDefinition.swift @@ -0,0 +1,21 @@ +import Foundation + +struct AnonCredentialDefinition: Codable { + struct Value: Codable { + let primary: Primary + } + + struct Primary: Codable { + let n: String + let s: String + let r: [String: String] + let rctxt: String + let z: String + } + + let issuerId: String + let schemaId: String + let type: String + let tag: String + let value: Value +} diff --git a/AtalaPrismSDK/Pollux/Sources/Models/AnonCreds/AnonCredential+StorableCredential.swift b/AtalaPrismSDK/Pollux/Sources/Models/AnonCreds/AnoncredsCredentialStack+StorableCredential.swift similarity index 87% rename from AtalaPrismSDK/Pollux/Sources/Models/AnonCreds/AnonCredential+StorableCredential.swift rename to AtalaPrismSDK/Pollux/Sources/Models/AnonCreds/AnoncredsCredentialStack+StorableCredential.swift index a26f6a18..65468f89 100644 --- a/AtalaPrismSDK/Pollux/Sources/Models/AnonCreds/AnonCredential+StorableCredential.swift +++ b/AtalaPrismSDK/Pollux/Sources/Models/AnonCreds/AnoncredsCredentialStack+StorableCredential.swift @@ -1,7 +1,7 @@ import Domain import Foundation -extension AnonCredential: StorableCredential { +extension AnoncredsCredentialStack: StorableCredential { var storingId: String { id } @@ -31,7 +31,7 @@ extension AnonCredential: StorableCredential { } var queryCredentialSchema: String? { - schemaId + credential.schemaId } var queryValidUntil: Date? { @@ -40,7 +40,6 @@ extension AnonCredential: StorableCredential { var queryRevoked: Bool? { nil -// revocationRegistryId != nil } var queryAvailableClaims: [String] { diff --git a/AtalaPrismSDK/Pollux/Sources/Models/AnonCreds/AnoncredsCredentialStack.swift b/AtalaPrismSDK/Pollux/Sources/Models/AnonCreds/AnoncredsCredentialStack.swift new file mode 100644 index 00000000..089cd075 --- /dev/null +++ b/AtalaPrismSDK/Pollux/Sources/Models/AnonCreds/AnoncredsCredentialStack.swift @@ -0,0 +1,50 @@ +import Core +import Domain +import Foundation + +struct AnoncredsCredentialStack: Codable { + let definition: AnonCredentialDefinition + let credential: AnonCredential +} + +extension AnoncredsCredentialStack: Domain.Credential { + var id: String { + guard + let jsonData = try? JSONEncoder.didComm().encode(credential) + else { + assert(false, "This should never happen") + return "" + } + return jsonData.sha256.hex + } + + var issuer: String { + definition.issuerId + } + + var subject: String? { + nil + } + + var claims: [Domain.Claim] { + credential.values.map { + .init(key: $0, value: .string($1.raw)) + } + } + + var properties: [String : Any] { + var properties = [ + "schemaId" : credential.schemaId, + "credentialDefinitionId" : credential.credentialDefinitionId, + ] as [String : Any] + + (try? JSONEncoder.didComm().encode(definition)).map { properties["credentialDefinition"] = $0 } + (try? JSONEncoder.didComm().encode(credential.signature)).map { properties["signature"] = $0 } + (try? JSONEncoder.didComm().encode(credential.signatureCorrectnessProof)).map { properties["signatureCorrectnessProof"] = $0 } + (try? JSONEncoder.didComm().encode(credential.witness)).map { properties["witness"] = $0 } + + return properties + } + + var credentialType: String { "anoncreds" } +} diff --git a/AtalaPrismSDK/Pollux/Sources/Models/JWT/JWTCredential.swift b/AtalaPrismSDK/Pollux/Sources/Models/JWT/JWTCredential.swift index 15953734..f9245afb 100644 --- a/AtalaPrismSDK/Pollux/Sources/Models/JWT/JWTCredential.swift +++ b/AtalaPrismSDK/Pollux/Sources/Models/JWT/JWTCredential.swift @@ -62,5 +62,6 @@ extension JWTCredential: Credential { return properties } + var credentialType: String { "JWT" } } diff --git a/AtalaPrismSDK/Pollux/Sources/Models/W3C/W3CVerifiableCredential.swift b/AtalaPrismSDK/Pollux/Sources/Models/W3C/W3CVerifiableCredential.swift index 5c1aebac..0fbe0cc5 100644 --- a/AtalaPrismSDK/Pollux/Sources/Models/W3C/W3CVerifiableCredential.swift +++ b/AtalaPrismSDK/Pollux/Sources/Models/W3C/W3CVerifiableCredential.swift @@ -162,4 +162,6 @@ extension W3CVerifiableCredential: Credential { return properties } + + var credentialType: String { "W3C" } } diff --git a/AtalaPrismSDK/Pollux/Sources/Operation/Anoncreds/CreateAnoncredCredentialRequest.swift b/AtalaPrismSDK/Pollux/Sources/Operation/Anoncreds/CreateAnoncredCredentialRequest.swift index 32b4a070..ce423ca8 100644 --- a/AtalaPrismSDK/Pollux/Sources/Operation/Anoncreds/CreateAnoncredCredentialRequest.swift +++ b/AtalaPrismSDK/Pollux/Sources/Operation/Anoncreds/CreateAnoncredCredentialRequest.swift @@ -1,5 +1,6 @@ import AnoncredsSwift import Combine +import Core import Domain import Foundation @@ -16,30 +17,25 @@ struct CreateAnoncredCredentialRequest { linkSecret: String, linkSecretId: String, offerData: Data, - credentialDefinitions: AnyPublisher<[(id: String, json: String)], Error> + credentialDefinitionDownloader: Downloader ) async throws -> String { - let linkSecret = try LinkSecret.newFromJson(jsonString: linkSecret) + let linkSecretObj = try LinkSecret.newFromValue(valueString: linkSecret) let offer = try CredentialOffer(jsonString: String(data: offerData, encoding: .utf8)!) let credDefId = offer.getCredDefId() - let definition = try await credentialDefinitions - .tryMap { - try $0 - .first { $0.id == credDefId.value } - .map { try CredentialDefinition(jsonString: $0.json) } - } - .first() - .await() - - guard let definition else { throw UnknownError.somethingWentWrongError() } - - return try Prover().createCredentialRequest( - entropy: nil, - proverDid: did, - credDef: definition, - linkSecret: linkSecret, + let credentialDefinitionData = try await credentialDefinitionDownloader.downloadFromEndpoint(urlOrDID: credDefId) + let credentialDefinitionJson = try credentialDefinitionData.toString() + + let credentialDefinition = try CredentialDefinition(jsonString: credentialDefinitionJson) + + let def = try Prover().createCredentialRequest( + entropy: did, + proverDid: nil, + credDef: credentialDefinition, + linkSecret: linkSecretObj, linkSecretId: linkSecretId, credentialOffer: offer ).request.getJson() + return def } } diff --git a/AtalaPrismSDK/Pollux/Sources/Operation/Anoncreds/ParseAnoncredsCredentialFromMessage.swift b/AtalaPrismSDK/Pollux/Sources/Operation/Anoncreds/ParseAnoncredsCredentialFromMessage.swift index 49ea7b2f..d27c94f1 100644 --- a/AtalaPrismSDK/Pollux/Sources/Operation/Anoncreds/ParseAnoncredsCredentialFromMessage.swift +++ b/AtalaPrismSDK/Pollux/Sources/Operation/Anoncreds/ParseAnoncredsCredentialFromMessage.swift @@ -1,8 +1,21 @@ import AnoncredsSwift +import Domain import Foundation struct ParseAnoncredsCredentialFromMessage { - static func parse(issuerCredentialData: Data) throws -> AnonCredential { - try JSONDecoder().decode(AnonCredential.self, from: issuerCredentialData) + static func parse( + issuerCredentialData: Data, + linkSecret: String, + credentialDefinitionDownloader: Downloader + ) async throws -> AnoncredsCredentialStack { + let domainCred = try JSONDecoder().decode(AnonCredential.self, from: issuerCredentialData) + + let credentialDefinitionData = try await credentialDefinitionDownloader + .downloadFromEndpoint(urlOrDID: domainCred.credentialDefinitionId) + + return AnoncredsCredentialStack( + definition: try JSONDecoder.didComm().decode(AnonCredentialDefinition.self, from: credentialDefinitionData), + credential: domainCred + ) } } diff --git a/AtalaPrismSDK/Pollux/Sources/Operation/JWT/CreateJWTCredentialRequest.swift b/AtalaPrismSDK/Pollux/Sources/Operation/JWT/CreateJWTCredentialRequest.swift index 31af73dc..9aa4d57f 100644 --- a/AtalaPrismSDK/Pollux/Sources/Operation/JWT/CreateJWTCredentialRequest.swift +++ b/AtalaPrismSDK/Pollux/Sources/Operation/JWT/CreateJWTCredentialRequest.swift @@ -33,7 +33,7 @@ struct CreateJWTCredentialRequest { } } -private struct ClaimsRequestSignatureJWT: Claims { +struct ClaimsRequestSignatureJWT: Claims { struct VerifiablePresentation: Codable { enum CodingKeys: String, CodingKey { case context = "@context" diff --git a/AtalaPrismSDK/Pollux/Sources/PolluxImpl+CredentialRequest.swift b/AtalaPrismSDK/Pollux/Sources/PolluxImpl+CredentialRequest.swift index a1bb938c..54e23118 100644 --- a/AtalaPrismSDK/Pollux/Sources/PolluxImpl+CredentialRequest.swift +++ b/AtalaPrismSDK/Pollux/Sources/PolluxImpl+CredentialRequest.swift @@ -17,18 +17,23 @@ extension PolluxImpl { throw PolluxError.offerDoesntProvideEnoughInformation } - switch offerAttachment.mediaType { - case "jwt", "", .none: + switch offerAttachment.format { + case "jwt", "prism/jwt", .none: switch offerAttachment.data { case let json as AttachmentJsonData: return try processJWTCredentialRequest(offerData: json.data, options: options) default: throw PolluxError.offerDoesntProvideEnoughInformation } - case "anoncreds": + case "anoncreds/credential-offer@v1.0": switch offerAttachment.data { - case let json as AttachmentJsonData: - return try await processAnoncredsCredentialRequest(offerData: json.data, options: options) + case let attachmentData as AttachmentJsonData: + return try await processAnoncredsCredentialRequest(offerData: attachmentData.data, options: options) + case let attachmentData as AttachmentBase64: + guard let data = Data(fromBase64URL: attachmentData.base64) else { + throw PolluxError.offerDoesntProvideEnoughInformation + } + return try await processAnoncredsCredentialRequest(offerData: data, options: options) default: throw PolluxError.offerDoesntProvideEnoughInformation } @@ -88,11 +93,11 @@ extension PolluxImpl { } guard - let credentialDefinitonsStreamOption = options.first(where: { - if case .credentialDefinitionsStream = $0 { return true } + let credDefinitionDownloaderOption = options.first(where: { + if case .credentialDefinitionDownloader = $0 { return true } return false }), - case let CredentialOperationsOptions.credentialDefinitionsStream(stream) = credentialDefinitonsStreamOption + case let CredentialOperationsOptions.credentialDefinitionDownloader(downloader) = credDefinitionDownloaderOption else { throw PolluxError.invalidPrismDID } @@ -102,7 +107,7 @@ extension PolluxImpl { linkSecret: linkSecret, linkSecretId: linkSecretId, offerData: offerData, - credentialDefinitions: stream + credentialDefinitionDownloader: downloader ) } } diff --git a/AtalaPrismSDK/Pollux/Sources/PolluxImpl+ParseCredential.swift b/AtalaPrismSDK/Pollux/Sources/PolluxImpl+ParseCredential.swift index cff68826..57d2bcf2 100644 --- a/AtalaPrismSDK/Pollux/Sources/PolluxImpl+ParseCredential.swift +++ b/AtalaPrismSDK/Pollux/Sources/PolluxImpl+ParseCredential.swift @@ -2,12 +2,12 @@ import Domain import Foundation extension PolluxImpl { - public func parseCredential(issuedCredential: Message) throws -> Credential { + public func parseCredential(issuedCredential: Message, options: [CredentialOperationsOptions]) async throws -> Credential { guard let issuedAttachment = issuedCredential.attachments.first else { throw PolluxError.unsupportedIssuedMessage } - switch issuedAttachment.mediaType { + switch issuedAttachment.format { case "jwt", "", "prism/jwt", .none: switch issuedAttachment.data { case let json as AttachmentJsonData: @@ -17,12 +17,40 @@ extension PolluxImpl { default: throw PolluxError.unsupportedIssuedMessage } - case "anoncreds", "prism/anoncreds": + case "anoncreds", "prism/anoncreds", "anoncreds/credential@v1.0": + guard + let linkSecretOption = options.first(where: { + if case .linkSecret = $0 { return true } + return false + }), + case let CredentialOperationsOptions.linkSecret(_, secret: linkSecret) = linkSecretOption + else { + throw PolluxError.invalidPrismDID + } + + guard + let credDefinitionDownloaderOption = options.first(where: { + if case .credentialDefinitionDownloader = $0 { return true } + return false + }), + case let CredentialOperationsOptions.credentialDefinitionDownloader(downloader) = credDefinitionDownloaderOption + else { + throw PolluxError.invalidPrismDID + } + switch issuedAttachment.data { case let json as AttachmentJsonData: - return try ParseAnoncredsCredentialFromMessage.parse(issuerCredentialData: json.data) + return try await ParseAnoncredsCredentialFromMessage.parse( + issuerCredentialData: json.data, + linkSecret: linkSecret, + credentialDefinitionDownloader: downloader + ) case let base64 as AttachmentBase64: - return try ParseAnoncredsCredentialFromMessage.parse(issuerCredentialData: try base64.decoded()) + return try await ParseAnoncredsCredentialFromMessage.parse( + issuerCredentialData: try base64.decoded(), + linkSecret: linkSecret, + credentialDefinitionDownloader: downloader + ) default: throw PolluxError.unsupportedIssuedMessage } diff --git a/AtalaPrismSDK/Pollux/Sources/PolluxImpl+Public.swift b/AtalaPrismSDK/Pollux/Sources/PolluxImpl+Public.swift index ff496d4c..9dea2cc5 100644 --- a/AtalaPrismSDK/Pollux/Sources/PolluxImpl+Public.swift +++ b/AtalaPrismSDK/Pollux/Sources/PolluxImpl+Public.swift @@ -9,6 +9,8 @@ extension PolluxImpl: Pollux { return try JSONDecoder().decode(JWTCredential.self, from: credentialData) case "w3c+credential": return try JSONDecoder().decode(W3CVerifiableCredential.self, from: credentialData) + case "anon+credential": + return try JSONDecoder().decode(AnoncredsCredentialStack.self, from: credentialData) default: throw PolluxError.invalidCredentialError } diff --git a/AtalaPrismSDK/Pollux/Tests/AnoncredsTests.swift b/AtalaPrismSDK/Pollux/Tests/AnoncredsTests.swift index 82524723..64fa9062 100644 --- a/AtalaPrismSDK/Pollux/Tests/AnoncredsTests.swift +++ b/AtalaPrismSDK/Pollux/Tests/AnoncredsTests.swift @@ -1,104 +1,50 @@ +import AnoncredsSwift @testable import Pollux import XCTest final class AnoncredsTests: XCTestCase { + let issuer = MockIssuer() + var linkSecret: LinkSecret! - let anoncredJson = """ -{ -"schema_id": "test schema id", -"cred_def_id": "test cred definition id", -"rev_reg_id": null, -"values": { - "first_name": { - "raw": "Alice", - "encoded": "113...335" - }, - "last_name": { - "raw": "Garcia", - "encoded": "532...452" - }, - "birthdate_dateint": { - "raw": "19981119", - "encoded": "19981119" + override func setUp() async throws { + linkSecret = try LinkSecret.newFromValue(valueString: "28380340054639370074509985417762391330214600660319893567746760706478614060614") } -}, -"signature": { - "p_credential": { - "m_2": "992...312", - "a": "548...252", - "e": "259...199", - "v": "977...597" - }, - "r_credential": null -}, -"signature_correctness_proof": { - "se": "898...935", - "c": "935...598" -}, -"rev_reg": null, -"witness": null -} -""".data(using: .utf8)! - - let anoncredWithRevocationJson = """ -{ -"schema_id": "test schema id", -"cred_def_id": "test cred definition id", -"rev_reg_id": "revocation registry id", -"values": { - "first_name": { - "raw": "Alice", - "encoded": "113...335" - }, - "last_name": { - "raw": "Garcia", - "encoded": "532...452" - }, - "birthdate_dateint": { - "raw": "19981119", - "encoded": "19981119" - } -}, -"signature": { - "p_credential": { - "m_2": "992...312", - "a": "548...252", - "e": "259...199", - "v": "977...597" - }, - "r_credential": { - "sigma": "1 14C...8A8", - "c": "12A...BB6", - "vr_prime_prime": "0F3...FC4", - "witness_signature": { - "sigma_i": "1 1D72...000", - "u_i": "1 0B3...000", - "g_i": "1 10D...8A8" - }, - "g_i": "1 10D7...8A8", - "i": 1, - "m2": "FDC...283" - } -}, -"signature_correctness_proof": { - "se": "898...935", - "c": "935...598" -}, -"rev_reg": { - "accum": "21 118...1FB" -}, -"witness": { - "omega": "21 124...AC8" -} -} -""".data(using: .utf8)! - - func testDecodeAnoncred() throws { - let decoder = JSONDecoder() - XCTAssertNoThrow(try decoder.decode(AnonCredential.self, from: anoncredJson)) + + func testCreateMessageRequest() async throws { + let offer = try issuer.createOfferMessage() + let linkSecretValue = try linkSecret.getValue() + let credDef = issuer.credDef + let defDownloader = MockDownloader(returnData: try credDef.getJson().data(using: .utf8)!) + + // No error means it passed + _ = try await PolluxImpl().processCredentialRequest( + offerMessage: offer, + options: [ + .linkSecret(id: "test", secret: linkSecretValue), + .credentialDefinitionDownloader(downloader: defDownloader), + .subjectDID(.init(method: "test", methodId: "adasdadde")) + ] + ) } - - func testDecodeAnoncredWithRevocation() throws { - XCTAssertNoThrow(try JSONDecoder().decode(AnonCredential.self, from: anoncredWithRevocationJson)) + + func testParseIssueCredential() async throws { + let offer = try issuer.createOffer() + let linkSecretValue = try linkSecret.getValue() + + let credDef = issuer.credDef + let defDownloader = MockDownloader(returnData: try credDef.getJson().data(using: .utf8)!) + let prover = try MockProver(linkSecret: linkSecret, credDef: credDef) + let request = try prover.createRequest(offer: offer) + let issuedMessage = try issuer.issueCredential(offer: offer, request: request.0) + let credential = try await PolluxImpl().parseCredential( + issuedCredential: issuedMessage, + options: [ + .linkSecret(id: "test", secret: linkSecretValue), + .credentialDefinitionDownloader(downloader: defDownloader), + ] + ) + + XCTAssertEqual(credential.claims.first?.key, "test") + XCTAssertEqual(credential.claims.first?.getValueAsString(), "test") } } diff --git a/AtalaPrismSDK/Pollux/Tests/JWTTests.swift b/AtalaPrismSDK/Pollux/Tests/JWTTests.swift index 9a96d12b..7596cacc 100644 --- a/AtalaPrismSDK/Pollux/Tests/JWTTests.swift +++ b/AtalaPrismSDK/Pollux/Tests/JWTTests.swift @@ -6,6 +6,7 @@ import PrismAgent @testable import Pollux import XCTest import secp256k1 +import SwiftJWT final class JWTTests: XCTestCase { @@ -21,139 +22,147 @@ final class JWTTests: XCTestCase { XCTAssertEqual(credential.id, validJWTString) } - func testJWTCreateCredentialRequest() async throws { - let offerCredentialMessage = OfferCredential( - id: "test1", - body: .init( - credentialPreview: CredentialPreview(attributes: []), - formats: [] - ), - attachments: [.init( - id: "", - mediaType: nil, - data: AttachmentJsonData(data: "{\"domain\":\"test\", \"challenge\":\"test\"}".data(using: .utf8)!), - filename: nil, - format: nil, - lastmodTime: nil, - byteCount: nil, - description: nil - )], - thid: nil, - from: DID.init(method: "test", methodId: "123"), - to: DID.init(method: "test", methodId: "123") - ) - - let pollux = PolluxImpl() - let privKey = try apollo.createPrivateKey(parameters: [ - KeyProperties.type.rawValue: "EC", - KeyProperties.curve.rawValue: KnownKeyCurves.secp256k1.rawValue, - KeyProperties.rawKey.rawValue: Data(fromBase64URL: "N_JFgvYaReyRXwassz5FHg33A4I6dczzdXrjdHGksmg")!.base64Encoded() - ]) as! PrivateKey & ExportableKey - - let pubKey = privKey.publicKey() - let subjectPrismDID = try castor.createPrismDID(masterPublicKey: pubKey, services: []) - - let requestString = try await pollux.processCredentialRequest( - offerMessage: offerCredentialMessage.makeMessage(), - options: [ - .subjectDID(subjectPrismDID), - .exportableKey(privKey) - ] - ) - - let validJWTString = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ.eyJub25jZSI6InRlc3QiLCJpc3MiOiJkaWQ6cHJpc206OTRiY2RkZGIxN2E2NTY1NTg2NGYxZGEzYjIyNzU2NTI1NmJjZTM2MTg1Y2EwMzE5OWVlZjNiNmNkMTg4MTBlZDpDc2NCQ3NRQkVtUUtEMkYxZEdobGJuUnBZMkYwYVc5dU1CQUVRazhLQ1hObFkzQXlOVFpyTVJJZ1A2WGlXdERvYWo2ZzNsZTEybGpxamwzSl9aWGFfa1Jzd0M5R05VWW1rVFVhSUlHRlZFU1g4U2pLVFQ1eXBTNi1ERGl6VldtTHdUVHFNdlMtZEJQSTFjZkVFbHdLQjIxaGMzUmxjakFRQVVKUENnbHpaV053TWpVMmF6RVNJRC1sNGxyUTZHby1vTjVYdGRwWTZvNWR5ZjJWMnY1RWJNQXZSalZHSnBFMUdpQ0JoVlJFbF9Fb3lrMC1jcVV1dmd3NHMxVnBpOEUwNmpMMHZuUVR5TlhIeEEiLCJ2cCI6eyJAY29udGV4dCI6WyJodHRwczpcL1wvd3d3LnczLm9yZ1wvMjAxOFwvcHJlc2VudGF0aW9uc1wvdjEiXSwidHlwZSI6WyJWZXJpZmlhYmxlUHJlc2VudGF0aW9uIl19LCJhdWQiOiJ0ZXN0In0.gBfP9y5dscgc--DoKWQ8eWGjJ8yRsfwbDH1sHwMIgm5cSUpvGnzpn4wsrLIsCMG6Udp2H2N-jaFnXhjeE28-lA" - - XCTAssertEqual(validJWTString, requestString) - } - - func testJWTCreateCredentialRequestErrorMissingDomain() async throws { - let offerCredentialMessage = OfferCredential( - id: "test1", - body: .init( - credentialPreview: CredentialPreview(attributes: []), - formats: [] - ), - attachments: [.init( - id: "", - mediaType: nil, - data: AttachmentJsonData(data: "{\"challenge\":\"test\"}".data(using: .utf8)!), - filename: nil, - format: nil, - lastmodTime: nil, - byteCount: nil, - description: nil - )], - thid: nil, - from: DID.init(method: "test", methodId: "123"), - to: DID.init(method: "test", methodId: "123") - ) - - let pollux = PolluxImpl() - let privKey = try apollo.createPrivateKey(parameters: [ - KeyProperties.type.rawValue: "EC", - KeyProperties.curve.rawValue: KnownKeyCurves.secp256k1.rawValue, - KeyProperties.rawKey.rawValue: Data(fromBase64URL: "N_JFgvYaReyRXwassz5FHg33A4I6dczzdXrjdHGksmg")!.base64Encoded() - ]) as! PrivateKey & ExportableKey - - let pubKey = privKey.publicKey() - let subjectPrismDID = try castor.createPrismDID(masterPublicKey: pubKey, services: []) - do { - try await pollux.processCredentialRequest( - offerMessage: offerCredentialMessage.makeMessage(), - options: [ - .subjectDID(subjectPrismDID), - .exportableKey(privKey) - ] - ) - XCTFail("Should throw an error") - } catch { - - } - } - - func testJWTPresentationSignature() async throws { - let requestPresentation = RequestPresentation( - body: .init(proofTypes: []), - attachments: [.init( - id: "", - mediaType: nil, - data: AttachmentJsonData(data: "{\"domain\":\"test\", \"challenge\":\"test\"}".data(using: .utf8)!), - filename: nil, - format: nil, - lastmodTime: nil, - byteCount: nil, - description: nil - )], - thid: nil, - from: DID.init(method: "test", methodId: "123"), - to: DID.init(method: "test", methodId: "123") - ) - - let cred = createJWTCredential() - let privKey = try apollo.createPrivateKey(parameters: [ - KeyProperties.type.rawValue: "EC", - KeyProperties.curve.rawValue: KnownKeyCurves.secp256k1.rawValue, - KeyProperties.rawKey.rawValue: Data(fromBase64URL: "N_JFgvYaReyRXwassz5FHg33A4I6dczzdXrjdHGksmg")!.base64Encoded() - ]) as! PrivateKey & ExportableKey - - let pubKey = privKey.publicKey() - let subjectPrismDID = try castor.createPrismDID(masterPublicKey: pubKey, services: []) - let resultString = try cred.proof?.presentation( - request: requestPresentation.makeMessage(), - options: [ - .subjectDID(subjectPrismDID), - .exportableKey(privKey) - ] - ) - - let validResultString = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ.eyJub25jZSI6InRlc3QiLCJpc3MiOiJkaWQ6cHJpc206OTRiY2RkZGIxN2E2NTY1NTg2NGYxZGEzYjIyNzU2NTI1NmJjZTM2MTg1Y2EwMzE5OWVlZjNiNmNkMTg4MTBlZDpDc2NCQ3NRQkVtUUtEMkYxZEdobGJuUnBZMkYwYVc5dU1CQUVRazhLQ1hObFkzQXlOVFpyTVJJZ1A2WGlXdERvYWo2ZzNsZTEybGpxamwzSl9aWGFfa1Jzd0M5R05VWW1rVFVhSUlHRlZFU1g4U2pLVFQ1eXBTNi1ERGl6VldtTHdUVHFNdlMtZEJQSTFjZkVFbHdLQjIxaGMzUmxjakFRQVVKUENnbHpaV053TWpVMmF6RVNJRC1sNGxyUTZHby1vTjVYdGRwWTZvNWR5ZjJWMnY1RWJNQXZSalZHSnBFMUdpQ0JoVlJFbF9Fb3lrMC1jcVV1dmd3NHMxVnBpOEUwNmpMMHZuUVR5TlhIeEEiLCJ2cCI6eyJAY29udGV4dCI6WyJodHRwczpcL1wvd3d3LnczLm9yZ1wvMjAxOFwvcHJlc2VudGF0aW9uc1wvdjEiXSwidHlwZSI6WyJWZXJpZmlhYmxlUHJlc2VudGF0aW9uIl0sInZlcmlmaWFibGVDcmVkZW50aWFsIjpbImV5SmhiR2NpT2lKRlV6STFOa3NpZlEuZXlKcGMzTWlPaUprYVdRNmNISnBjMjA2TW1VME1HWmtOamt5WWpnell6RTVaamxoTlRVek5qUmpNbU5oTldKbU5qa3lPR0k0T0RVMU5HRTFZbVl4TVRjMFlUYzRaalk0TkRrNFpEZ3daR1pqTmpwRGNtTkNRM0pSUWtWcWEwdENWM1JzWlZNd2VFVkJTa3RNWjI5S1l6SldhbU5FU1RGT2JYTjRSV2xGUTFwRGJEVjRhVVJFYjNac1ZGbE5OVlZTZVhkSE9EWlBXamMyUldOVFkzTmpTRXBsYUhSbmJXTktUbEZUVDJkdlIxbFlWakJoUXpCNFJVRlNTMHhuYjBwak1sWnFZMFJKTVU1dGMzaEZhVVZEUlVNelRVTlBhazR4YjFsTlpqVTJaVlZCYVRBM05reEdYMmhSWkRSd2JGRmliM0pLY25Ca09IZEhZMU5QZDI5SVlsZEdlbVJIVm5sTlFrRkNVMmswUzBOWVRteFpNMEY1VGxSYWNrMVNTV2hCZVRWcVZrYzRVVFJXT0hSWVYwUm9VV052YjJ4UFRtRklkVFpIYVc1b2NrSjZTRXRmUlhZeVNXOXlOU0lzSW5OMVlpSTZJbVJwWkRwd2NtbHpiVG80T0RZd04yWTRZakUzWldKaFptTmhPRGd3TkRkbVpEUTBZVE15WlRFNE5HSTFNR1l3TTJReU5XWmhaV1ExWkdSaVlXUXlaR1JqTkdZeVpqZzVZV1l6T2tOelkwSkRjMUZDUlcxUlMwUXlSakZrUjJoc1ltNVNjRmt5UmpCaFZ6bDFUVUpCUlZGck9FdERXRTVzV1ROQmVVNVVXbkpOVWtsbmNuRkRNVmhhTjJac09VcExTakJOVDNwVGEyaFNaRmhFU0hwblNWUXpUR0oxTWxOTGRUSnZaV3hLVldGSlQzZ3hTekZ2WTJORFJHMTRTUzA1Wm05alJtODRlbWhwVG01QllYQlBVR0ZYUVhZMFVHZzBhelpqV2tWc2QwdENNakZvWXpOU2JHTnFRVkZCVlVwUVEyZHNlbHBYVG5kTmFsVXlZWHBGVTBsTE5tZDBWakpsTXpWbVUxTnBaRVJFY3pCd1NWVllWbmQ0T0RSRFJUbDVNamQwYTJseWRIRkljRk5XUjJsRWMyUlRkR0ZJU0VGbk5YTlRVSFpZTmtoQ1lWQk5ORmxxV25kSGNWUnFNbXhuVEMxRU5HVktUMjVIVVNJc0ltNWlaaUk2TVRZNE9EQTFPRGN5Tnl3aVpYaHdJam94TmpnNE1EWXlNekkzTENKMll5STZleUpqY21Wa1pXNTBhV0ZzVTJOb1pXMWhJanA3SW1sa0lqb2lhSFIwY0hNNlhDOWNMMnM0Y3kxa1pYWXVZWFJoYkdGd2NtbHpiUzVwYjF3dmNISnBjMjB0WVdkbGJuUmNMM05qYUdWdFlTMXlaV2RwYzNSeWVWd3ZjMk5vWlcxaGMxd3ZNREl3TVRZNU0ySXROR1EyWkMwek5tVmpMV0V6TjJRdE9ERmtPRGhsT0RjeU5UTTVJaXdpZEhsd1pTSTZJa055WldSbGJuUnBZV3hUWTJobGJXRXlNREl5SW4wc0ltTnlaV1JsYm5ScFlXeFRkV0pxWldOMElqcDdJblJsYzNRaU9pSlVaWE4wTVNJc0ltbGtJam9pWkdsa09uQnlhWE50T2pnNE5qQTNaamhpTVRkbFltRm1ZMkU0T0RBME4yWmtORFJoTXpKbE1UZzBZalV3WmpBelpESTFabUZsWkRWa1pHSmhaREprWkdNMFpqSm1PRGxoWmpNNlEzTmpRa056VVVKRmJWRkxSREpHTVdSSGFHeGlibEp3V1RKR01HRlhPWFZOUWtGRlVXczRTME5ZVG14Wk0wRjVUbFJhY2sxU1NXZHljVU14V0ZvM1ptdzVTa3RLTUUxUGVsTnJhRkprV0VSSWVtZEpWRE5NWW5VeVUwdDFNbTlsYkVwVllVbFBlREZMTVc5alkwTkViWGhKTFRsbWIyTkdiemg2YUdsT2JrRmhjRTlRWVZkQmRqUlFhRFJyTm1OYVJXeDNTMEl5TVdoak0xSnNZMnBCVVVGVlNsQkRaMng2V2xkT2QwMXFWVEpoZWtWVFNVczJaM1JXTW1Vek5XWlRVMmxrUkVSek1IQkpWVmhXZDNnNE5FTkZPWGt5TjNScmFYSjBjVWh3VTFaSGFVUnpaRk4wWVVoSVFXYzFjMU5RZGxnMlNFSmhVRTAwV1dwYWQwZHhWR295YkdkTUxVUTBaVXBQYmtkUkluMHNJblI1Y0dVaU9sc2lWbVZ5YVdacFlXSnNaVU55WldSbGJuUnBZV3dpWFN3aVFHTnZiblJsZUhRaU9sc2lhSFIwY0hNNlhDOWNMM2QzZHk1M015NXZjbWRjTHpJd01UaGNMMk55WldSbGJuUnBZV3h6WEM5Mk1TSmRmWDAuSlpCcUFyVkZ2V2dqMlcwYjd2VlBTS1IzbVNIX1gtVk9DLVlRX2p5TFpTT0VZVWtvcnRrUkdpNDF4d0E3U1BGU3FQZFNDSGw0aWFncEJpcjF0WU1CT3ciXX0sImF1ZCI6InRlc3QifQ.vEUJnRmJsp7H7IKgQfDBSn12HPBXXVyEySI8sXHtwPRFTGOCvOpB6PImrn7N4I4ENmV6vLadJf5ZbO2n9hBwDw" - - XCTAssertEqual(validResultString, resultString) - } - - private func createJWTCredential() -> Credential { - let jwtString = "eyJhbGciOiJFUzI1NksifQ.eyJpc3MiOiJkaWQ6cHJpc206MmU0MGZkNjkyYjgzYzE5ZjlhNTUzNjRjMmNhNWJmNjkyOGI4ODU1NGE1YmYxMTc0YTc4ZjY4NDk4ZDgwZGZjNjpDcmNCQ3JRQkVqa0tCV3RsZVMweEVBSktMZ29KYzJWamNESTFObXN4RWlFQ1pDbDV4aUREb3ZsVFlNNVVSeXdHODZPWjc2RWNTY3NjSEplaHRnbWNKTlFTT2dvR1lYVjBhQzB4RUFSS0xnb0pjMlZqY0RJMU5tc3hFaUVDRUMzTUNPak4xb1lNZjU2ZVVBaTA3NkxGX2hRZDRwbFFib3JKcnBkOHdHY1NPd29IYldGemRHVnlNQkFCU2k0S0NYTmxZM0F5TlRack1SSWhBeTVqVkc4UTRWOHRYV0RoUWNvb2xPTmFIdTZHaW5ockJ6SEtfRXYySW9yNSIsInN1YiI6ImRpZDpwcmlzbTo4ODYwN2Y4YjE3ZWJhZmNhODgwNDdmZDQ0YTMyZTE4NGI1MGYwM2QyNWZhZWQ1ZGRiYWQyZGRjNGYyZjg5YWYzOkNzY0JDc1FCRW1RS0QyRjFkR2hsYm5ScFkyRjBhVzl1TUJBRVFrOEtDWE5sWTNBeU5UWnJNUklncnFDMVhaN2ZsOUpLSjBNT3pTa2hSZFhESHpnSVQzTGJ1MlNLdTJvZWxKVWFJT3gxSzFvY2NDRG14SS05Zm9jRm84emhpTm5BYXBPUGFXQXY0UGg0azZjWkVsd0tCMjFoYzNSbGNqQVFBVUpQQ2dselpXTndNalUyYXpFU0lLNmd0VjJlMzVmU1NpZEREczBwSVVYVnd4ODRDRTl5Mjd0a2lydHFIcFNWR2lEc2RTdGFISEFnNXNTUHZYNkhCYVBNNFlqWndHcVRqMmxnTC1ENGVKT25HUSIsIm5iZiI6MTY4ODA1ODcyNywiZXhwIjoxNjg4MDYyMzI3LCJ2YyI6eyJjcmVkZW50aWFsU2NoZW1hIjp7ImlkIjoiaHR0cHM6XC9cL2s4cy1kZXYuYXRhbGFwcmlzbS5pb1wvcHJpc20tYWdlbnRcL3NjaGVtYS1yZWdpc3RyeVwvc2NoZW1hc1wvMDIwMTY5M2ItNGQ2ZC0zNmVjLWEzN2QtODFkODhlODcyNTM5IiwidHlwZSI6IkNyZWRlbnRpYWxTY2hlbWEyMDIyIn0sImNyZWRlbnRpYWxTdWJqZWN0Ijp7InRlc3QiOiJUZXN0MSIsImlkIjoiZGlkOnByaXNtOjg4NjA3ZjhiMTdlYmFmY2E4ODA0N2ZkNDRhMzJlMTg0YjUwZjAzZDI1ZmFlZDVkZGJhZDJkZGM0ZjJmODlhZjM6Q3NjQkNzUUJFbVFLRDJGMWRHaGxiblJwWTJGMGFXOXVNQkFFUWs4S0NYTmxZM0F5TlRack1SSWdycUMxWFo3Zmw5SktKME1PelNraFJkWERIemdJVDNMYnUyU0t1Mm9lbEpVYUlPeDFLMW9jY0NEbXhJLTlmb2NGbzh6aGlObkFhcE9QYVdBdjRQaDRrNmNaRWx3S0IyMWhjM1JsY2pBUUFVSlBDZ2x6WldOd01qVTJhekVTSUs2Z3RWMmUzNWZTU2lkRERzMHBJVVhWd3g4NENFOXkyN3RraXJ0cUhwU1ZHaURzZFN0YUhIQWc1c1NQdlg2SEJhUE00WWpad0dxVGoybGdMLUQ0ZUpPbkdRIn0sInR5cGUiOlsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiXSwiQGNvbnRleHQiOlsiaHR0cHM6XC9cL3d3dy53My5vcmdcLzIwMThcL2NyZWRlbnRpYWxzXC92MSJdfX0.JZBqArVFvWgj2W0b7vVPSKR3mSH_X-VOC-YQ_jyLZSOEYUkortkRGi41xwA7SPFSqPdSCHl4iagpBir1tYMBOw" - - return try! JWTCredential(data: jwtString.data(using: .utf8)!) - } +// func testJWTCreateCredentialRequest() async throws { +// let offerCredentialMessage = OfferCredential3_0( +// id: "test1", +// body: .init( +// goalCode: nil, +// comment: nil, +// replacementId: nil, +// multipleAvailable: nil, +// credentialPreview: .init(schemaId: "", attributes: []) +// ), +// type: "test", +// attachments: [.init( +// id: "", +// mediaType: nil, +// data: AttachmentJsonData(data: "{\"domain\":\"test\", \"challenge\":\"test\"}".data(using: .utf8)!), +// filename: nil, +// format: nil, +// lastmodTime: nil, +// byteCount: nil, +// description: nil +// )], +// thid: nil, +// from: DID.init(method: "test", methodId: "123"), +// to: DID.init(method: "test", methodId: "123") +// ) +// +// let pollux = PolluxImpl() +// let privKey = try apollo.createPrivateKey(parameters: [ +// KeyProperties.type.rawValue: "EC", +// KeyProperties.derivationPath.rawValue: DerivationPath(index: 0).keyPathString(), +// KeyProperties.curve.rawValue: KnownKeyCurves.secp256k1.rawValue, +// KeyProperties.rawKey.rawValue: Data(fromBase64URL: "N_JFgvYaReyRXwassz5FHg33A4I6dczzdXrjdHGksmg")!.base64Encoded() +// ]) as! PrivateKey & ExportableKey +// +// let pubKey = privKey.publicKey() +// let subjectPrismDID = try castor.createPrismDID(masterPublicKey: pubKey, services: []) +// +// let requestString = try await pollux.processCredentialRequest( +// offerMessage: offerCredentialMessage.makeMessage(), +// options: [ +// .subjectDID(subjectPrismDID), +// .exportableKey(privKey) +// ] +// ) +// +// let validJWTString = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ.eyJub25jZSI6InRlc3QiLCJpc3MiOiJkaWQ6cHJpc206OTRiY2RkZGIxN2E2NTY1NTg2NGYxZGEzYjIyNzU2NTI1NmJjZTM2MTg1Y2EwMzE5OWVlZjNiNmNkMTg4MTBlZDpDc2NCQ3NRQkVtUUtEMkYxZEdobGJuUnBZMkYwYVc5dU1CQUVRazhLQ1hObFkzQXlOVFpyTVJJZ1A2WGlXdERvYWo2ZzNsZTEybGpxamwzSl9aWGFfa1Jzd0M5R05VWW1rVFVhSUlHRlZFU1g4U2pLVFQ1eXBTNi1ERGl6VldtTHdUVHFNdlMtZEJQSTFjZkVFbHdLQjIxaGMzUmxjakFRQVVKUENnbHpaV053TWpVMmF6RVNJRC1sNGxyUTZHby1vTjVYdGRwWTZvNWR5ZjJWMnY1RWJNQXZSalZHSnBFMUdpQ0JoVlJFbF9Fb3lrMC1jcVV1dmd3NHMxVnBpOEUwNmpMMHZuUVR5TlhIeEEiLCJ2cCI6eyJAY29udGV4dCI6WyJodHRwczpcL1wvd3d3LnczLm9yZ1wvMjAxOFwvcHJlc2VudGF0aW9uc1wvdjEiXSwidHlwZSI6WyJWZXJpZmlhYmxlUHJlc2VudGF0aW9uIl19LCJhdWQiOiJ0ZXN0In0.gBfP9y5dscgc--DoKWQ8eWGjJ8yRsfwbDH1sHwMIgm5cSUpvGnzpn4wsrLIsCMG6Udp2H2N-jaFnXhjeE28-lA" +// +// let verifier = JWT.verify(requestString, using: .es256k(publicKey: pubKey.exporting!.pem.data(using: .utf8)!)) +// XCTAssertTrue(verifier) +// XCTAssertEqual(validJWTString, requestString) +// } +// +// func testJWTCreateCredentialRequestErrorMissingDomain() async throws { +// let offerCredentialMessage = OfferCredential( +// id: "test1", +// body: .init( +// credentialPreview: CredentialPreview(attributes: []), +// formats: [] +// ), +// type: "test", +// attachments: [.init( +// id: "", +// mediaType: nil, +// data: AttachmentJsonData(data: "{\"challenge\":\"test\"}".data(using: .utf8)!), +// filename: nil, +// format: nil, +// lastmodTime: nil, +// byteCount: nil, +// description: nil +// )], +// thid: nil, +// from: DID.init(method: "test", methodId: "123"), +// to: DID.init(method: "test", methodId: "123") +// ) +// +// let pollux = PolluxImpl() +// let privKey = try apollo.createPrivateKey(parameters: [ +// KeyProperties.type.rawValue: "EC", +// KeyProperties.curve.rawValue: KnownKeyCurves.secp256k1.rawValue, +// KeyProperties.rawKey.rawValue: Data(fromBase64URL: "N_JFgvYaReyRXwassz5FHg33A4I6dczzdXrjdHGksmg")!.base64Encoded() +// ]) as! PrivateKey & ExportableKey +// +// let pubKey = privKey.publicKey() +// let subjectPrismDID = try castor.createPrismDID(masterPublicKey: pubKey, services: []) +// do { +// try await pollux.processCredentialRequest( +// offerMessage: offerCredentialMessage.makeMessage(), +// options: [ +// .subjectDID(subjectPrismDID), +// .exportableKey(privKey) +// ] +// ) +// XCTFail("Should throw an error") +// } catch { +// +// } +// } +// +// func testJWTPresentationSignature() async throws { +// let requestPresentation = RequestPresentation( +// body: .init(proofTypes: []), +// attachments: [.init( +// id: "", +// mediaType: nil, +// data: AttachmentJsonData(data: "{\"domain\":\"test\", \"challenge\":\"test\"}".data(using: .utf8)!), +// filename: nil, +// format: nil, +// lastmodTime: nil, +// byteCount: nil, +// description: nil +// )], +// thid: nil, +// from: DID.init(method: "test", methodId: "123"), +// to: DID.init(method: "test", methodId: "123") +// ) +// +// let cred = createJWTCredential() +// let privKey = try apollo.createPrivateKey(parameters: [ +// KeyProperties.type.rawValue: "EC", +// KeyProperties.curve.rawValue: KnownKeyCurves.secp256k1.rawValue, +// KeyProperties.rawKey.rawValue: Data(fromBase64URL: "N_JFgvYaReyRXwassz5FHg33A4I6dczzdXrjdHGksmg")!.base64Encoded() +// ]) as! PrivateKey & ExportableKey +// +// let pubKey = privKey.publicKey() +// let subjectPrismDID = try castor.createPrismDID(masterPublicKey: pubKey, services: []) +// let resultString = try cred.proof?.presentation( +// request: requestPresentation.makeMessage(), +// options: [ +// .subjectDID(subjectPrismDID), +// .exportableKey(privKey) +// ] +// ) +// +// let validResultString = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksifQ.eyJub25jZSI6InRlc3QiLCJpc3MiOiJkaWQ6cHJpc206OTRiY2RkZGIxN2E2NTY1NTg2NGYxZGEzYjIyNzU2NTI1NmJjZTM2MTg1Y2EwMzE5OWVlZjNiNmNkMTg4MTBlZDpDc2NCQ3NRQkVtUUtEMkYxZEdobGJuUnBZMkYwYVc5dU1CQUVRazhLQ1hObFkzQXlOVFpyTVJJZ1A2WGlXdERvYWo2ZzNsZTEybGpxamwzSl9aWGFfa1Jzd0M5R05VWW1rVFVhSUlHRlZFU1g4U2pLVFQ1eXBTNi1ERGl6VldtTHdUVHFNdlMtZEJQSTFjZkVFbHdLQjIxaGMzUmxjakFRQVVKUENnbHpaV053TWpVMmF6RVNJRC1sNGxyUTZHby1vTjVYdGRwWTZvNWR5ZjJWMnY1RWJNQXZSalZHSnBFMUdpQ0JoVlJFbF9Fb3lrMC1jcVV1dmd3NHMxVnBpOEUwNmpMMHZuUVR5TlhIeEEiLCJ2cCI6eyJAY29udGV4dCI6WyJodHRwczpcL1wvd3d3LnczLm9yZ1wvMjAxOFwvcHJlc2VudGF0aW9uc1wvdjEiXSwidHlwZSI6WyJWZXJpZmlhYmxlUHJlc2VudGF0aW9uIl0sInZlcmlmaWFibGVDcmVkZW50aWFsIjpbImV5SmhiR2NpT2lKRlV6STFOa3NpZlEuZXlKcGMzTWlPaUprYVdRNmNISnBjMjA2TW1VME1HWmtOamt5WWpnell6RTVaamxoTlRVek5qUmpNbU5oTldKbU5qa3lPR0k0T0RVMU5HRTFZbVl4TVRjMFlUYzRaalk0TkRrNFpEZ3daR1pqTmpwRGNtTkNRM0pSUWtWcWEwdENWM1JzWlZNd2VFVkJTa3RNWjI5S1l6SldhbU5FU1RGT2JYTjRSV2xGUTFwRGJEVjRhVVJFYjNac1ZGbE5OVlZTZVhkSE9EWlBXamMyUldOVFkzTmpTRXBsYUhSbmJXTktUbEZUVDJkdlIxbFlWakJoUXpCNFJVRlNTMHhuYjBwak1sWnFZMFJKTVU1dGMzaEZhVVZEUlVNelRVTlBhazR4YjFsTlpqVTJaVlZCYVRBM05reEdYMmhSWkRSd2JGRmliM0pLY25Ca09IZEhZMU5QZDI5SVlsZEdlbVJIVm5sTlFrRkNVMmswUzBOWVRteFpNMEY1VGxSYWNrMVNTV2hCZVRWcVZrYzRVVFJXT0hSWVYwUm9VV052YjJ4UFRtRklkVFpIYVc1b2NrSjZTRXRmUlhZeVNXOXlOU0lzSW5OMVlpSTZJbVJwWkRwd2NtbHpiVG80T0RZd04yWTRZakUzWldKaFptTmhPRGd3TkRkbVpEUTBZVE15WlRFNE5HSTFNR1l3TTJReU5XWmhaV1ExWkdSaVlXUXlaR1JqTkdZeVpqZzVZV1l6T2tOelkwSkRjMUZDUlcxUlMwUXlSakZrUjJoc1ltNVNjRmt5UmpCaFZ6bDFUVUpCUlZGck9FdERXRTVzV1ROQmVVNVVXbkpOVWtsbmNuRkRNVmhhTjJac09VcExTakJOVDNwVGEyaFNaRmhFU0hwblNWUXpUR0oxTWxOTGRUSnZaV3hLVldGSlQzZ3hTekZ2WTJORFJHMTRTUzA1Wm05alJtODRlbWhwVG01QllYQlBVR0ZYUVhZMFVHZzBhelpqV2tWc2QwdENNakZvWXpOU2JHTnFRVkZCVlVwUVEyZHNlbHBYVG5kTmFsVXlZWHBGVTBsTE5tZDBWakpsTXpWbVUxTnBaRVJFY3pCd1NWVllWbmQ0T0RSRFJUbDVNamQwYTJseWRIRkljRk5XUjJsRWMyUlRkR0ZJU0VGbk5YTlRVSFpZTmtoQ1lWQk5ORmxxV25kSGNWUnFNbXhuVEMxRU5HVktUMjVIVVNJc0ltNWlaaUk2TVRZNE9EQTFPRGN5Tnl3aVpYaHdJam94TmpnNE1EWXlNekkzTENKMll5STZleUpqY21Wa1pXNTBhV0ZzVTJOb1pXMWhJanA3SW1sa0lqb2lhSFIwY0hNNlhDOWNMMnM0Y3kxa1pYWXVZWFJoYkdGd2NtbHpiUzVwYjF3dmNISnBjMjB0WVdkbGJuUmNMM05qYUdWdFlTMXlaV2RwYzNSeWVWd3ZjMk5vWlcxaGMxd3ZNREl3TVRZNU0ySXROR1EyWkMwek5tVmpMV0V6TjJRdE9ERmtPRGhsT0RjeU5UTTVJaXdpZEhsd1pTSTZJa055WldSbGJuUnBZV3hUWTJobGJXRXlNREl5SW4wc0ltTnlaV1JsYm5ScFlXeFRkV0pxWldOMElqcDdJblJsYzNRaU9pSlVaWE4wTVNJc0ltbGtJam9pWkdsa09uQnlhWE50T2pnNE5qQTNaamhpTVRkbFltRm1ZMkU0T0RBME4yWmtORFJoTXpKbE1UZzBZalV3WmpBelpESTFabUZsWkRWa1pHSmhaREprWkdNMFpqSm1PRGxoWmpNNlEzTmpRa056VVVKRmJWRkxSREpHTVdSSGFHeGlibEp3V1RKR01HRlhPWFZOUWtGRlVXczRTME5ZVG14Wk0wRjVUbFJhY2sxU1NXZHljVU14V0ZvM1ptdzVTa3RLTUUxUGVsTnJhRkprV0VSSWVtZEpWRE5NWW5VeVUwdDFNbTlsYkVwVllVbFBlREZMTVc5alkwTkViWGhKTFRsbWIyTkdiemg2YUdsT2JrRmhjRTlRWVZkQmRqUlFhRFJyTm1OYVJXeDNTMEl5TVdoak0xSnNZMnBCVVVGVlNsQkRaMng2V2xkT2QwMXFWVEpoZWtWVFNVczJaM1JXTW1Vek5XWlRVMmxrUkVSek1IQkpWVmhXZDNnNE5FTkZPWGt5TjNScmFYSjBjVWh3VTFaSGFVUnpaRk4wWVVoSVFXYzFjMU5RZGxnMlNFSmhVRTAwV1dwYWQwZHhWR295YkdkTUxVUTBaVXBQYmtkUkluMHNJblI1Y0dVaU9sc2lWbVZ5YVdacFlXSnNaVU55WldSbGJuUnBZV3dpWFN3aVFHTnZiblJsZUhRaU9sc2lhSFIwY0hNNlhDOWNMM2QzZHk1M015NXZjbWRjTHpJd01UaGNMMk55WldSbGJuUnBZV3h6WEM5Mk1TSmRmWDAuSlpCcUFyVkZ2V2dqMlcwYjd2VlBTS1IzbVNIX1gtVk9DLVlRX2p5TFpTT0VZVWtvcnRrUkdpNDF4d0E3U1BGU3FQZFNDSGw0aWFncEJpcjF0WU1CT3ciXX0sImF1ZCI6InRlc3QifQ.vEUJnRmJsp7H7IKgQfDBSn12HPBXXVyEySI8sXHtwPRFTGOCvOpB6PImrn7N4I4ENmV6vLadJf5ZbO2n9hBwDw" +// +// XCTAssertEqual(validResultString, resultString) +// } +// +// private func createJWTCredential() -> Credential { +// let jwtString = "eyJhbGciOiJFUzI1NksifQ.eyJpc3MiOiJkaWQ6cHJpc206MmU0MGZkNjkyYjgzYzE5ZjlhNTUzNjRjMmNhNWJmNjkyOGI4ODU1NGE1YmYxMTc0YTc4ZjY4NDk4ZDgwZGZjNjpDcmNCQ3JRQkVqa0tCV3RsZVMweEVBSktMZ29KYzJWamNESTFObXN4RWlFQ1pDbDV4aUREb3ZsVFlNNVVSeXdHODZPWjc2RWNTY3NjSEplaHRnbWNKTlFTT2dvR1lYVjBhQzB4RUFSS0xnb0pjMlZqY0RJMU5tc3hFaUVDRUMzTUNPak4xb1lNZjU2ZVVBaTA3NkxGX2hRZDRwbFFib3JKcnBkOHdHY1NPd29IYldGemRHVnlNQkFCU2k0S0NYTmxZM0F5TlRack1SSWhBeTVqVkc4UTRWOHRYV0RoUWNvb2xPTmFIdTZHaW5ockJ6SEtfRXYySW9yNSIsInN1YiI6ImRpZDpwcmlzbTo4ODYwN2Y4YjE3ZWJhZmNhODgwNDdmZDQ0YTMyZTE4NGI1MGYwM2QyNWZhZWQ1ZGRiYWQyZGRjNGYyZjg5YWYzOkNzY0JDc1FCRW1RS0QyRjFkR2hsYm5ScFkyRjBhVzl1TUJBRVFrOEtDWE5sWTNBeU5UWnJNUklncnFDMVhaN2ZsOUpLSjBNT3pTa2hSZFhESHpnSVQzTGJ1MlNLdTJvZWxKVWFJT3gxSzFvY2NDRG14SS05Zm9jRm84emhpTm5BYXBPUGFXQXY0UGg0azZjWkVsd0tCMjFoYzNSbGNqQVFBVUpQQ2dselpXTndNalUyYXpFU0lLNmd0VjJlMzVmU1NpZEREczBwSVVYVnd4ODRDRTl5Mjd0a2lydHFIcFNWR2lEc2RTdGFISEFnNXNTUHZYNkhCYVBNNFlqWndHcVRqMmxnTC1ENGVKT25HUSIsIm5iZiI6MTY4ODA1ODcyNywiZXhwIjoxNjg4MDYyMzI3LCJ2YyI6eyJjcmVkZW50aWFsU2NoZW1hIjp7ImlkIjoiaHR0cHM6XC9cL2s4cy1kZXYuYXRhbGFwcmlzbS5pb1wvcHJpc20tYWdlbnRcL3NjaGVtYS1yZWdpc3RyeVwvc2NoZW1hc1wvMDIwMTY5M2ItNGQ2ZC0zNmVjLWEzN2QtODFkODhlODcyNTM5IiwidHlwZSI6IkNyZWRlbnRpYWxTY2hlbWEyMDIyIn0sImNyZWRlbnRpYWxTdWJqZWN0Ijp7InRlc3QiOiJUZXN0MSIsImlkIjoiZGlkOnByaXNtOjg4NjA3ZjhiMTdlYmFmY2E4ODA0N2ZkNDRhMzJlMTg0YjUwZjAzZDI1ZmFlZDVkZGJhZDJkZGM0ZjJmODlhZjM6Q3NjQkNzUUJFbVFLRDJGMWRHaGxiblJwWTJGMGFXOXVNQkFFUWs4S0NYTmxZM0F5TlRack1SSWdycUMxWFo3Zmw5SktKME1PelNraFJkWERIemdJVDNMYnUyU0t1Mm9lbEpVYUlPeDFLMW9jY0NEbXhJLTlmb2NGbzh6aGlObkFhcE9QYVdBdjRQaDRrNmNaRWx3S0IyMWhjM1JsY2pBUUFVSlBDZ2x6WldOd01qVTJhekVTSUs2Z3RWMmUzNWZTU2lkRERzMHBJVVhWd3g4NENFOXkyN3RraXJ0cUhwU1ZHaURzZFN0YUhIQWc1c1NQdlg2SEJhUE00WWpad0dxVGoybGdMLUQ0ZUpPbkdRIn0sInR5cGUiOlsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiXSwiQGNvbnRleHQiOlsiaHR0cHM6XC9cL3d3dy53My5vcmdcLzIwMThcL2NyZWRlbnRpYWxzXC92MSJdfX0.JZBqArVFvWgj2W0b7vVPSKR3mSH_X-VOC-YQ_jyLZSOEYUkortkRGi41xwA7SPFSqPdSCHl4iagpBir1tYMBOw" +// +// return try! JWTCredential(data: jwtString.data(using: .utf8)!) +// } } diff --git a/AtalaPrismSDK/Pollux/Tests/Mocks/AnoncredsMocks.swift b/AtalaPrismSDK/Pollux/Tests/Mocks/AnoncredsMocks.swift deleted file mode 100644 index c86748b2..00000000 --- a/AtalaPrismSDK/Pollux/Tests/Mocks/AnoncredsMocks.swift +++ /dev/null @@ -1,8 +0,0 @@ -// -// File.swift -// -// -// Created by Goncalo Frade IOHK on 22/08/2023. -// - -import Foundation diff --git a/AtalaPrismSDK/Pollux/Tests/Mocks/MockDownloader.swift b/AtalaPrismSDK/Pollux/Tests/Mocks/MockDownloader.swift new file mode 100644 index 00000000..7ce7af01 --- /dev/null +++ b/AtalaPrismSDK/Pollux/Tests/Mocks/MockDownloader.swift @@ -0,0 +1,9 @@ +import Domain +import Foundation + +struct MockDownloader: Downloader { + let returnData: Data + func downloadFromEndpoint(urlOrDID: String) async throws -> Data { + return returnData + } +} diff --git a/AtalaPrismSDK/Pollux/Tests/Mocks/MockIssuer.swift b/AtalaPrismSDK/Pollux/Tests/Mocks/MockIssuer.swift new file mode 100644 index 00000000..5dc9112d --- /dev/null +++ b/AtalaPrismSDK/Pollux/Tests/Mocks/MockIssuer.swift @@ -0,0 +1,104 @@ +import AnoncredsSwift +import Domain +import Foundation + +struct MockIssuer { + + let issuer = "did:web:asadadada" + let linkSecret: LinkSecret + let schema: Schema + let credDef: CredentialDefinition + let credDefPriv: CredentialDefinitionPrivate + let credDefCorrProof: CredentialKeyCorrectnessProof + + init() { + self.linkSecret = try! LinkSecret.newFromValue(valueString: "36590588636589688587165354116254517405509622321561684934488049104990967858487") + + self.schema = try! Issuer().createSchema( + schemaName: "Test", + schemaVersion: "1.0.0", + issuerId: issuer, + attrNames: ["Test"] + ) + + let credDef = try! Issuer().createCredentialDefinition( + schemaId: "http://localhost:8000/schemas/test", + schema: schema, + issuerId: issuer, + tag: "test", + signatureType: .cl, + config: .init(supportRevocation: false) + ) + + self.credDef = credDef.credentialDefinition + self.credDefPriv = credDef.credentialDefinitionPrivate + self.credDefCorrProof = credDef.credentialKeyCorrectnessProof + + print("IssuerSecret: \(try! linkSecret.getValue())") + + print("credDef") + print(try! credDef.credentialDefinition.getJson()) + } + + func createOffer() throws -> CredentialOffer { + try Issuer().createCredentialOffer( + schemaId: "http://localhost:8000/schemas/test", + credDefId: "http://localhost:8000/definitions/test", + correctnessProof: credDefCorrProof + ) + } + + func createOfferMessage() throws -> Message { + let offer = try createOffer().getJson() + + return Message( + piuri: "https://didcomm.org/issue-credential/3.0/offer-credential", + body: Data(), + attachments: [ + .init( + id: "test1", + mediaType: nil, + data: AttachmentBase64(base64: offer.data(using: .utf8)!.base64EncodedString()), + filename: nil, + format: "anoncreds/credential-offer@v1.0", + lastmodTime: nil, + byteCount: nil, + description: nil + ) + ] + ) + } + + func issueCredential( + offer: CredentialOffer, + request: CredentialRequest + ) throws -> Message { + let issued = try Issuer().createCredential( + credDef: credDef, + credDefPrivate: credDefPriv, + credOffer: offer, + credRequest: request, + credValues: [.init(raw: "test", encoded: "test")], + revRegId: nil, + revStatusList: nil, + revocationConfig: nil + ).getJson() + + return Message( + piuri: "https://didcomm.org/issue-credential/3.0/issue-credential", + body: Data(), + attachments: [ + .init( + id: "test1", + mediaType: nil, + data: AttachmentBase64(base64: issued.data(using: .utf8)!.base64EncodedString()), + filename: nil, + format: "anoncreds/credential@v1.0", + lastmodTime: nil, + byteCount: nil, + description: nil + ) + ] + ) + } +} diff --git a/AtalaPrismSDK/Pollux/Tests/Mocks/MockProver.swift b/AtalaPrismSDK/Pollux/Tests/Mocks/MockProver.swift new file mode 100644 index 00000000..c8712a78 --- /dev/null +++ b/AtalaPrismSDK/Pollux/Tests/Mocks/MockProver.swift @@ -0,0 +1,25 @@ +import AnoncredsSwift +import Foundation + +struct MockProver { + let did = "did:test:adsadiada" + let linkSecret: LinkSecret + let credDef: CredentialDefinition + + init(linkSecret: LinkSecret, credDef: CredentialDefinition) { + self.linkSecret = linkSecret + self.credDef = credDef + } + + func createRequest(offer: CredentialOffer) throws -> (CredentialRequest, CredentialRequestMetadata) { + let result = try Prover().createCredentialRequest( + entropy: "did", + proverDid: nil, + credDef: credDef, + linkSecret: linkSecret, + linkSecretId: "test", + credentialOffer: offer + ) + return (result.request, result.metadata) + } +} diff --git a/AtalaPrismSDK/PrismAgent/Sources/PrismAgent+Credentials.swift b/AtalaPrismSDK/PrismAgent/Sources/PrismAgent+Credentials.swift index c5a2db3c..6cfcf7bd 100644 --- a/AtalaPrismSDK/PrismAgent/Sources/PrismAgent+Credentials.swift +++ b/AtalaPrismSDK/PrismAgent/Sources/PrismAgent+Credentials.swift @@ -29,8 +29,20 @@ public extension PrismAgent { /// - message: Issue credential Message. /// - Returns: The parsed verifiable credential. /// - Throws: PrismAgentError, if there is a problem parsing the credential. - func processIssuedCredentialMessage(message: IssueCredential) async throws -> Credential { - let credential = try pollux.parseCredential(issuedCredential: message.makeMessage()) + func processIssuedCredentialMessage(message: IssueCredential3_0) async throws -> Credential { + guard + let linkSecret = try await pluto.getLinkSecret().first().await().first + else { throw PrismAgentError.cannotFindDIDKeyPairIndex } + + let downloader = DownloadDataWithResolver(castor: castor) + let credential = try await pollux.parseCredential( + issuedCredential: message.makeMessage(), + options: [ + .linkSecret(id: "", secret: linkSecret), + .credentialDefinitionDownloader(downloader: downloader), + .schemaDownloader(downloader: downloader) + ] + ) guard let storableCredential = credential.storable else { return credential @@ -49,7 +61,7 @@ public extension PrismAgent { /// - did: Received offer credential. /// - Returns: Created request credential /// - Throws: PrismAgentError, if there is a problem creating the request credential. - func prepareRequestCredentialWithIssuer(did: DID, offer: OfferCredential) async throws -> RequestCredential? { + func prepareRequestCredentialWithIssuer(did: DID, offer: OfferCredential3_0) async throws -> RequestCredential3_0? { guard did.method == "prism" else { throw PolluxError.invalidPrismDID } let didInfo = try await pluto .getDIDInfo(did: did) @@ -58,39 +70,70 @@ public extension PrismAgent { guard let storedPrivateKey = didInfo?.privateKeys.first else { throw PrismAgentError.cannotFindDIDKeyPairIndex } - let privateKey = try await apollo.restorePrivateKey( - identifier: storedPrivateKey.restorationIdentifier, - data: storedPrivateKey.storableData - ) + let privateKey = try await apollo.restorePrivateKey(storedPrivateKey) guard let exporting = privateKey.exporting, let linkSecret = try await pluto.getLinkSecret().first().await().first else { throw PrismAgentError.cannotFindDIDKeyPairIndex } + let downloader = DownloadDataWithResolver(castor: castor) let requestString = try await pollux.processCredentialRequest( - offerMessage: try offer.makeMessage(), + offerMessage: offer.makeMessage(), options: [ .exportableKey(exporting), .subjectDID(did), .linkSecret(id: did.string, secret: linkSecret), - .credentialDefinitionsStream(stream: credentialDefinitions), - .schemasStream(stream: schemas) + .credentialDefinitionDownloader(downloader: downloader), + .schemaDownloader(downloader: downloader) ] ) - guard let base64String = requestString.data(using: .utf8)?.base64EncodedString() else { - throw UnknownError.somethingWentWrongError() + guard + let offerFormat = offer.attachments.first?.format, + let base64String = requestString.data(using: .utf8)?.base64EncodedString() + else { + throw CommonError.invalidCoding(message: "Could not encode to base64") } - let requestCredential = RequestCredential( + guard + let offerPiuri = ProtocolTypes(rawValue: offer.type) + else { + throw PrismAgentError.invalidMessageType( + type: offer.type, + shouldBe: [ + ProtocolTypes.didcommOfferCredential3_0.rawValue + ] + ) + } + let format: String + switch offerFormat { + case "prism/jwt": + format = "prism/jwt" + case "anoncreds/credential-offer@v1.0": + format = "anoncreds/credential-request@v1.0" + default: + throw PrismAgentError.invalidMessageType( + type: offerFormat, + shouldBe: [ + "prism/jwt", + "anoncreds/credential-offer@v1.0" + ] + ) + } + + let type = offerPiuri == .didcommOfferCredential ? + ProtocolTypes.didcommRequestCredential : + ProtocolTypes.didcommRequestCredential3_0 + + let requestCredential = RequestCredential3_0( body: .init( goalCode: offer.body.goalCode, - comment: offer.body.comment, - formats: offer.body.formats + comment: offer.body.comment ), + type: type.rawValue, attachments: [.init( - mediaType: "prism/jwt", - data: AttachmentBase64(base64: base64String) + data: AttachmentBase64(base64: base64String), + format: format )], thid: offer.thid, from: offer.to, @@ -99,13 +142,3 @@ public extension PrismAgent { return requestCredential } } - -// TODO: Just while we dont have API for this -extension PrismAgent { - var credentialDefinitions: AnyPublisher<[(id: String, json: String)], Error> { - Just([(id: String, json: String)]()).tryMap { $0 }.eraseToAnyPublisher() - } - var schemas: AnyPublisher<[(id: String, json: String)], Error> { - Just([(id: String, json: String)]()).tryMap { $0 }.eraseToAnyPublisher() - } -} diff --git a/AtalaPrismSDK/PrismAgent/Sources/PrismAgent+DIDHigherFucntions.swift b/AtalaPrismSDK/PrismAgent/Sources/PrismAgent+DIDHigherFucntions.swift index 96f0b69b..49fa0c2d 100644 --- a/AtalaPrismSDK/PrismAgent/Sources/PrismAgent+DIDHigherFucntions.swift +++ b/AtalaPrismSDK/PrismAgent/Sources/PrismAgent+DIDHigherFucntions.swift @@ -40,10 +40,7 @@ Could not find key in storage please use Castor instead and provide the private throw PrismAgentError.cannotFindDIDKeyPairIndex } - let privateKey = try await apollo.restorePrivateKey( - identifier: storedPrivateKey.restorationIdentifier, - data: storedPrivateKey.storableData - ) + let privateKey = try await apollo.restorePrivateKey(storedPrivateKey) logger.debug(message: "Signing message", metadata: [ .maskedMetadataByLevel(key: "messageB64", value: message.base64Encoded(), level: .debug) diff --git a/AtalaPrismSDK/PrismAgent/Sources/PrismAgent+Invitations.swift b/AtalaPrismSDK/PrismAgent/Sources/PrismAgent+Invitations.swift index efc27c04..2bf127a0 100644 --- a/AtalaPrismSDK/PrismAgent/Sources/PrismAgent+Invitations.swift +++ b/AtalaPrismSDK/PrismAgent/Sources/PrismAgent+Invitations.swift @@ -116,7 +116,9 @@ public extension PrismAgent { do { let response = try await URLSession.shared.data(for: request) guard let urlResponse = response.1 as? HTTPURLResponse else { - throw UnknownError.somethingWentWrongError() + throw CommonError.invalidCoding( + message: "This should not happen cannot convert URLResponse to HTTPURLResponse" + ) } guard urlResponse.statusCode == 200 else { throw CommonError.httpError( diff --git a/AtalaPrismSDK/PrismAgent/Sources/PrismAgent+Proof.swift b/AtalaPrismSDK/PrismAgent/Sources/PrismAgent+Proof.swift index d8b786b1..7482a7f5 100644 --- a/AtalaPrismSDK/PrismAgent/Sources/PrismAgent+Proof.swift +++ b/AtalaPrismSDK/PrismAgent/Sources/PrismAgent+Proof.swift @@ -23,7 +23,7 @@ public extension PrismAgent { guard let subjectDIDString = credential.subject else { - throw UnknownError.somethingWentWrongError() + throw PolluxError.invalidPrismDID } let subjectDID = try DID(string: subjectDIDString) @@ -37,10 +37,7 @@ public extension PrismAgent { let storedPrivateKey = didInfo?.privateKeys.first else { throw PrismAgentError.cannotFindDIDKeyPairIndex } - let privateKey = try await apollo.restorePrivateKey( - identifier: storedPrivateKey.restorationIdentifier, - data: storedPrivateKey.storableData - ) + let privateKey = try await apollo.restorePrivateKey(storedPrivateKey) guard let exporting = privateKey.exporting @@ -55,7 +52,7 @@ public extension PrismAgent { ) guard let base64String = presentationString.data(using: .utf8)?.base64EncodedString() else { - throw UnknownError.somethingWentWrongError() + throw CommonError.invalidCoding(message: "Could not encode to base64") } return Presentation( body: .init( diff --git a/AtalaPrismSDK/PrismAgent/Sources/PrismAgent.swift b/AtalaPrismSDK/PrismAgent/Sources/PrismAgent.swift index f5bc05a7..3519bdfb 100644 --- a/AtalaPrismSDK/PrismAgent/Sources/PrismAgent.swift +++ b/AtalaPrismSDK/PrismAgent/Sources/PrismAgent.swift @@ -196,7 +196,8 @@ public class PrismAgent { private func firstLinkSecretSetup() async throws { if try await pluto.getLinkSecret().first().await().first == nil { - try await pluto.storeLinkSecret(secret: apollo.createNewLinkSecret()).first().await() + let secret = try apollo.createNewLinkSecret() + try await pluto.storeLinkSecret(secret: secret).first().await() } } } @@ -220,10 +221,7 @@ private func createSecretsStream( Future { try await array.asyncMap { did, privateKeys, _ in let privateKeys = try await privateKeys.asyncMap { - try await keyRestoration.restorePrivateKey( - identifier: $0.restorationIdentifier, - data: $0.storableData - ) + try await keyRestoration.restorePrivateKey($0) } return try parsePrivateKeys( did: did, diff --git a/AtalaPrismSDK/PrismAgent/Sources/PrismAgentErrors.swift b/AtalaPrismSDK/PrismAgent/Sources/PrismAgentErrors.swift index 6cca33dc..2a890953 100644 --- a/AtalaPrismSDK/PrismAgent/Sources/PrismAgentErrors.swift +++ b/AtalaPrismSDK/PrismAgent/Sources/PrismAgentErrors.swift @@ -19,7 +19,7 @@ public enum PrismAgentError: KnownPrismError { case unknownInvitationTypeError /// An error case representing an invalid message type. - case invalidMessageType(type: String, shouldBe: String) + case invalidMessageType(type: String, shouldBe: [String]) /// An error case representing that no mediator is available. case noMediatorAvailableError diff --git a/AtalaPrismSDK/PrismAgent/Sources/Protocols/Connection/ConnectionAccept.swift b/AtalaPrismSDK/PrismAgent/Sources/Protocols/Connection/ConnectionAccept.swift index 0e618c16..0ac0a121 100644 --- a/AtalaPrismSDK/PrismAgent/Sources/Protocols/Connection/ConnectionAccept.swift +++ b/AtalaPrismSDK/PrismAgent/Sources/Protocols/Connection/ConnectionAccept.swift @@ -77,7 +77,7 @@ public struct ConnectionAccept { else { throw PrismAgentError.invalidMessageType( type: fromMessage.piuri, - shouldBe: ProtocolTypes.didcommconnectionResponse.rawValue + shouldBe: [ProtocolTypes.didcommconnectionResponse.rawValue] ) } diff --git a/AtalaPrismSDK/PrismAgent/Sources/Protocols/Connection/ConnectionRequest.swift b/AtalaPrismSDK/PrismAgent/Sources/Protocols/Connection/ConnectionRequest.swift index b1ed6d9d..39b3b35b 100644 --- a/AtalaPrismSDK/PrismAgent/Sources/Protocols/Connection/ConnectionRequest.swift +++ b/AtalaPrismSDK/PrismAgent/Sources/Protocols/Connection/ConnectionRequest.swift @@ -114,8 +114,9 @@ public struct ConnectionRequest { let to = fromMessage.to else { throw PrismAgentError.invalidMessageType( type: fromMessage.piuri, - shouldBe: ProtocolTypes.didcommconnectionRequest.rawValue) - } + shouldBe: [ProtocolTypes.didcommconnectionRequest.rawValue] + ) } + self.init( from: from, to: to, diff --git a/AtalaPrismSDK/PrismAgent/Sources/Protocols/Connection/DownloadDataWithResolver.swift b/AtalaPrismSDK/PrismAgent/Sources/Protocols/Connection/DownloadDataWithResolver.swift new file mode 100644 index 00000000..392760bf --- /dev/null +++ b/AtalaPrismSDK/PrismAgent/Sources/Protocols/Connection/DownloadDataWithResolver.swift @@ -0,0 +1,43 @@ +import Domain +import Foundation + +public struct DownloadDataWithResolver: Downloader { + let castor: Castor + + init(castor: Castor) { + self.castor = castor + } + + public func downloadFromEndpoint(urlOrDID: String) async throws -> Data { + let url: URL + + if let did = try? castor.parseDID(str: urlOrDID) { + let document = try await castor.resolveDID(did: did) + guard + let urlStr = document.services.first?.serviceEndpoint.first?.uri, + let validUrl = URL(string: urlStr) + else { + throw CommonError.invalidURLError(url: "Could not find any URL on DID") + } + url = validUrl + } else if let validUrl = URL(string: urlOrDID) { + url = validUrl + } else { + throw CommonError.invalidURLError(url: urlOrDID) + } + + let (data, urlResponse) = try await URLSession.shared.data(from: url) + + guard + let code = (urlResponse as? HTTPURLResponse)?.statusCode, + 200...299 ~= code + else { + throw CommonError.httpError( + code: (urlResponse as? HTTPURLResponse)?.statusCode ?? 500, + message: String(data: data, encoding: .utf8) ?? "" + ) + } + + return data + } +} diff --git a/AtalaPrismSDK/PrismAgent/Sources/Protocols/Invitation/V2/InvitationRunner.swift b/AtalaPrismSDK/PrismAgent/Sources/Protocols/Invitation/V2/InvitationRunner.swift index c66f8f80..3bd829a0 100644 --- a/AtalaPrismSDK/PrismAgent/Sources/Protocols/Invitation/V2/InvitationRunner.swift +++ b/AtalaPrismSDK/PrismAgent/Sources/Protocols/Invitation/V2/InvitationRunner.swift @@ -1,3 +1,4 @@ +import Core import Domain import Foundation @@ -11,10 +12,7 @@ class InvitationRunner { } func run() async throws -> Message { - let messageData = try OutOfBandParser().parseMessage(url: url) - guard - let messageString = String(data: messageData, encoding: .utf8) - else { throw UnknownError.somethingWentWrongError() } + let messageString = try OutOfBandParser().parseMessage(url: url).toString() return try await mercury.unpackMessage(msg: messageString) } } diff --git a/AtalaPrismSDK/PrismAgent/Sources/Protocols/IssueCredential/CredentialPreview.swift b/AtalaPrismSDK/PrismAgent/Sources/Protocols/IssueCredential/CredentialPreview.swift index 4ba9e1d7..98c6c246 100644 --- a/AtalaPrismSDK/PrismAgent/Sources/Protocols/IssueCredential/CredentialPreview.swift +++ b/AtalaPrismSDK/PrismAgent/Sources/Protocols/IssueCredential/CredentialPreview.swift @@ -34,3 +34,32 @@ public struct CredentialFormat: Codable, Equatable { public let attachId: String public let format: String } + +// https://github.com/hyperledger/aries-rfcs/tree/main/features/0453-issue-credential-v2#preview-credential +public struct CredentialPreview3_0: Codable, Equatable { + public struct Body: Codable, Equatable { + public let attributes: [Attribute] + } + + public struct Attribute: Codable, Equatable { + public let name: String + public let value: String + public let mediaType: String? + + public init(name: String, value: String, mediaType: String?) { + self.name = name + self.value = value + self.mediaType = mediaType + } + } + + public let type: String + public let schemaId: String? + public let body: Body + + public init(schemaId: String, attributes: [Attribute]) { + self.type = ProtocolTypes.didcommCredentialPreview.rawValue + self.schemaId = schemaId + self.body = .init(attributes: attributes) + } +} diff --git a/AtalaPrismSDK/PrismAgent/Sources/Protocols/IssueCredential/IssueCredential.swift b/AtalaPrismSDK/PrismAgent/Sources/Protocols/IssueCredential/IssueCredential.swift index 6d7ca7b0..768c38d1 100644 --- a/AtalaPrismSDK/PrismAgent/Sources/Protocols/IssueCredential/IssueCredential.swift +++ b/AtalaPrismSDK/PrismAgent/Sources/Protocols/IssueCredential/IssueCredential.swift @@ -28,7 +28,7 @@ public struct IssueCredential { } public let id: String - public let type = ProtocolTypes.didcommIssueCredential.rawValue + public let type: String public let body: Body public let attachments: [AttachmentDescriptor] public let thid: String? @@ -38,6 +38,7 @@ public struct IssueCredential { init( id: String = UUID().uuidString, body: Body, + type: String, attachments: [AttachmentDescriptor], thid: String?, from: DID, @@ -45,6 +46,7 @@ public struct IssueCredential { ) { self.id = id self.body = body + self.type = type self.attachments = attachments self.thid = thid self.from = from @@ -53,18 +55,21 @@ public struct IssueCredential { public init(fromMessage: Message) throws { guard - fromMessage.piuri == ProtocolTypes.didcommIssueCredential.rawValue, + let piuri = ProtocolTypes(rawValue: fromMessage.piuri), + (piuri == ProtocolTypes.didcommIssueCredential || + piuri == ProtocolTypes.didcommIssueCredential3_0), let fromDID = fromMessage.from, let toDID = fromMessage.to else { throw PrismAgentError.invalidMessageType( type: fromMessage.piuri, - shouldBe: ProtocolTypes.didcommIssueCredential.rawValue + shouldBe: [ProtocolTypes.didcommIssueCredential.rawValue] ) } let body = try JSONDecoder.didComm().decode(Body.self, from: fromMessage.body) self.init( id: fromMessage.id, body: body, + type: piuri.rawValue, attachments: fromMessage.attachments, thid: fromMessage.thid, from: fromDID, @@ -87,13 +92,30 @@ public struct IssueCredential { public static func makeIssueFromRequestCredential(msg: Message) throws -> IssueCredential { let request = try RequestCredential(fromMessage: msg) - + + guard + let requestPiuri = ProtocolTypes(rawValue: request.type) + else { + throw PrismAgentError.invalidMessageType( + type: request.type, + shouldBe: [ + ProtocolTypes.didcommRequestCredential.rawValue, + ProtocolTypes.didcommRequestCredential3_0.rawValue + ] + ) + } + + let type = requestPiuri == .didcommRequestCredential ? + ProtocolTypes.didcommIssueCredential : + ProtocolTypes.didcommIssueCredential3_0 + return IssueCredential( body: Body( goalCode: request.body.goalCode, comment: request.body.comment, formats: request.body.formats ), + type: type.rawValue, attachments: request.attachments, thid: msg.id, from: request.to, @@ -114,33 +136,145 @@ public struct IssueCredential { } } } +} - static func build( - fromDID: DID, - toDID: DID, - thid: String?, - credentialPreview: CredentialPreview, - credentials: [String: T] = [:] - ) throws -> IssueCredential { - let aux = try credentials.map { key, value in - let attachment = try AttachmentDescriptor.build(payload: value) - let format = CredentialFormat(attachId: attachment.id, format: key) - return (format, attachment) +extension IssueCredential: Equatable { + public static func == (lhs: IssueCredential, rhs: IssueCredential) -> Bool { + lhs.id == rhs.id && + lhs.type == rhs.type && + lhs.from == rhs.from && + lhs.to == rhs.to && + lhs.body == rhs.body + } +} + +// ALL parameters are DIDCOMMV2 format and naming conventions and follows the protocol +// https://github.com/hyperledger/aries-rfcs/tree/main/features/0453-issue-credential-v2 +public struct IssueCredential3_0 { + public struct Body: Codable, Equatable { + public let goalCode: String? + public let comment: String? + public let replacementId: String? + + init( + goalCode: String? = nil, + comment: String? = nil, + replacementId: String? = nil + ) { + self.goalCode = goalCode + self.comment = comment + self.replacementId = replacementId } - return IssueCredential( - body: Body( - formats: aux.map { $0.0 } - ), - attachments: aux.map { $0.1 }, - thid: thid, + } + + public let id: String + public let type: String + public let body: Body + public let attachments: [AttachmentDescriptor] + public let thid: String? + public let from: DID + public let to: DID + + init( + id: String = UUID().uuidString, + body: Body, + type: String, + attachments: [AttachmentDescriptor], + thid: String?, + from: DID, + to: DID + ) { + self.id = id + self.body = body + self.type = type + self.attachments = attachments + self.thid = thid + self.from = from + self.to = to + } + + public init(fromMessage: Message) throws { + guard + let piuri = ProtocolTypes(rawValue: fromMessage.piuri), + piuri == ProtocolTypes.didcommIssueCredential3_0, + let fromDID = fromMessage.from, + let toDID = fromMessage.to + else { throw PrismAgentError.invalidMessageType( + type: fromMessage.piuri, + shouldBe: [ProtocolTypes.didcommIssueCredential3_0.rawValue] + ) } + + let body = try JSONDecoder.didComm().decode(Body.self, from: fromMessage.body) + self.init( + id: fromMessage.id, + body: body, + type: piuri.rawValue, + attachments: fromMessage.attachments, + thid: fromMessage.thid, from: fromDID, to: toDID ) } + + public func makeMessage() throws -> Message { + .init( + id: id, + piuri: type, + from: from, + to: to, + body: try JSONEncoder.didComm().encode(body), + attachments: attachments, + thid: thid, + direction: .sent + ) + } + + public static func makeIssueFromRequestCredential(msg: Message) throws -> IssueCredential3_0 { + let request = try RequestCredential3_0(fromMessage: msg) + + guard + let requestPiuri = ProtocolTypes(rawValue: request.type) + else { + throw PrismAgentError.invalidMessageType( + type: request.type, + shouldBe: [ + ProtocolTypes.didcommRequestCredential3_0.rawValue + ] + ) + } + + let type = ProtocolTypes.didcommIssueCredential3_0 + + return IssueCredential3_0( + body: Body( + goalCode: request.body.goalCode, + comment: request.body.comment + ), + type: type.rawValue, + attachments: request.attachments, + thid: msg.id, + from: request.to, + to: request.from) + } + + func getCredentialStrings() throws -> [String] { + attachments.compactMap { + switch $0.data { + case let data as AttachmentBase64: + guard + let base64 = Data(base64URLEncoded: data.base64), + let str = String(data: base64, encoding: .utf8) + else { return nil } + return str + default: + return nil + } + } + } } -extension IssueCredential: Equatable { - public static func == (lhs: IssueCredential, rhs: IssueCredential) -> Bool { +extension IssueCredential3_0: Equatable { + public static func == (lhs: IssueCredential3_0, rhs: IssueCredential3_0) -> Bool { lhs.id == rhs.id && lhs.type == rhs.type && lhs.from == rhs.from && diff --git a/AtalaPrismSDK/PrismAgent/Sources/Protocols/IssueCredential/OfferCredential.swift b/AtalaPrismSDK/PrismAgent/Sources/Protocols/IssueCredential/OfferCredential.swift index 3c454baa..a190db66 100644 --- a/AtalaPrismSDK/PrismAgent/Sources/Protocols/IssueCredential/OfferCredential.swift +++ b/AtalaPrismSDK/PrismAgent/Sources/Protocols/IssueCredential/OfferCredential.swift @@ -31,7 +31,7 @@ public struct OfferCredential { } public let id: String - public let type = ProtocolTypes.didcommOfferCredential.rawValue + public let type: String public let body: Body public let attachments: [AttachmentDescriptor] public let thid: String? @@ -41,6 +41,7 @@ public struct OfferCredential { public init( id: String = UUID().uuidString, body: Body, + type: String, attachments: [AttachmentDescriptor], thid: String?, from: DID, @@ -48,6 +49,7 @@ public struct OfferCredential { ) { self.id = id self.body = body + self.type = type self.attachments = attachments self.thid = thid self.from = from @@ -56,18 +58,20 @@ public struct OfferCredential { public init(fromMessage: Message) throws { guard - fromMessage.piuri == ProtocolTypes.didcommOfferCredential.rawValue, + let piuri = ProtocolTypes(rawValue: fromMessage.piuri), + piuri == ProtocolTypes.didcommOfferCredential, let fromDID = fromMessage.from, let toDID = fromMessage.to else { throw PrismAgentError.invalidMessageType( type: fromMessage.piuri, - shouldBe: ProtocolTypes.didcommOfferCredential.rawValue + shouldBe: [ProtocolTypes.didcommOfferCredential.rawValue] ) } - + let body = try JSONDecoder.didComm().decode(Body.self, from: fromMessage.body) self.init( id: fromMessage.id, - body: body, + body: .init(credentialPreview: .init(attributes: []), formats: []), // TODO: [Anoncreds] when they fix on the agent put this back + type: piuri.rawValue, attachments: fromMessage.attachments, thid: fromMessage.thid, from: fromDID, @@ -89,46 +93,163 @@ public struct OfferCredential { } public static func makeOfferFromProposedCredential(proposed: ProposeCredential) throws -> OfferCredential { - OfferCredential( + guard + let proposePiuri = ProtocolTypes(rawValue: proposed.type) + else { + throw PrismAgentError.invalidMessageType( + type: proposed.type, + shouldBe: [ + ProtocolTypes.didcommProposeCredential.rawValue + ] + ) + } + + let type = ProtocolTypes.didcommOfferCredential + + return OfferCredential( body: Body( goalCode: proposed.body.goalCode, comment: proposed.body.comment, credentialPreview: proposed.body.credentialPreview, formats: proposed.body.formats ), + type: type.rawValue, attachments: proposed.attachments, thid: proposed.id, from: proposed.to, to: proposed.from) } +} - static func build( - fromDID: DID, - toDID: DID, - thid: String?, - credentialPreview: CredentialPreview, - credentials: [String: T] = [:] - ) throws -> OfferCredential { - let aux = try credentials.map { key, value in - let attachment = try AttachmentDescriptor.build(payload: value) - let format = CredentialFormat(attachId: attachment.id, format: key) - return (format, attachment) +extension OfferCredential: Equatable { + public static func == (lhs: OfferCredential, rhs: OfferCredential) -> Bool { + lhs.id == rhs.id && + lhs.type == rhs.type && + lhs.from == rhs.from && + lhs.to == rhs.to && + lhs.body == rhs.body + } +} + +// ALL parameters are DIDCOMMV2 format and naming conventions and follows the protocol +// https://github.com/hyperledger/aries-rfcs/tree/main/features/0453-issue-credential-v2 +public struct OfferCredential3_0 { + public struct Body: Codable, Equatable { + public let goalCode: String? + public let comment: String? + public let replacementId: String? + public let multipleAvailable: String? + public let credentialPreview: CredentialPreview3_0 + + public init( + goalCode: String? = nil, + comment: String? = nil, + replacementId: String? = nil, + multipleAvailable: String? = nil, + credentialPreview: CredentialPreview3_0 + ) { + self.goalCode = goalCode + self.comment = comment + self.replacementId = replacementId + self.multipleAvailable = multipleAvailable + self.credentialPreview = credentialPreview } - return OfferCredential( - body: Body( - credentialPreview: credentialPreview, - formats: aux.map { $0.0 } - ), - attachments: aux.map { $0.1 }, - thid: thid, + } + + public let id: String + public let type: String + public let body: Body + public let attachments: [AttachmentDescriptor] + public let thid: String? + public let from: DID + public let to: DID + + public init( + id: String = UUID().uuidString, + body: Body, + type: String, + attachments: [AttachmentDescriptor], + thid: String?, + from: DID, + to: DID + ) { + self.id = id + self.body = body + self.type = type + self.attachments = attachments + self.thid = thid + self.from = from + self.to = to + } + + public init(fromMessage: Message) throws { + guard + let piuri = ProtocolTypes(rawValue: fromMessage.piuri), + piuri == ProtocolTypes.didcommOfferCredential3_0, + let fromDID = fromMessage.from, + let toDID = fromMessage.to + else { throw PrismAgentError.invalidMessageType( + type: fromMessage.piuri, + shouldBe: [ProtocolTypes.didcommOfferCredential.rawValue] + ) } + + let body = try JSONDecoder.didComm().decode(Body.self, from: fromMessage.body) + self.init( + id: fromMessage.id, + body: body, + type: piuri.rawValue, + attachments: fromMessage.attachments, + thid: fromMessage.thid, from: fromDID, to: toDID ) } + + public func makeMessage() throws -> Message { + .init( + id: id, + piuri: type, + from: from, + to: to, + body: try JSONEncoder.didComm().encode(body), + attachments: attachments, + thid: thid, + direction: .sent + ) + } + + public static func makeOfferFromProposedCredential(proposed: ProposeCredential3_0) throws -> OfferCredential3_0 { + guard + let proposePiuri = ProtocolTypes(rawValue: proposed.type) + else { + throw PrismAgentError.invalidMessageType( + type: proposed.type, + shouldBe: [ + ProtocolTypes.didcommProposeCredential3_0.rawValue + ] + ) + } + + let type = proposePiuri == .didcommProposeCredential ? + ProtocolTypes.didcommOfferCredential : + ProtocolTypes.didcommOfferCredential3_0 + + return OfferCredential3_0( + body: Body( + goalCode: proposed.body.goalCode, + comment: proposed.body.comment, + credentialPreview: proposed.body.credentialPreview + ), + type: type.rawValue, + attachments: proposed.attachments, + thid: proposed.id, + from: proposed.to, + to: proposed.from) + } } -extension OfferCredential: Equatable { - public static func == (lhs: OfferCredential, rhs: OfferCredential) -> Bool { +extension OfferCredential3_0: Equatable { + public static func == (lhs: OfferCredential3_0, rhs: OfferCredential3_0) -> Bool { lhs.id == rhs.id && lhs.type == rhs.type && lhs.from == rhs.from && diff --git a/AtalaPrismSDK/PrismAgent/Sources/Protocols/IssueCredential/ProposeCredential.swift b/AtalaPrismSDK/PrismAgent/Sources/Protocols/IssueCredential/ProposeCredential.swift index ec14937a..c66e1e1e 100644 --- a/AtalaPrismSDK/PrismAgent/Sources/Protocols/IssueCredential/ProposeCredential.swift +++ b/AtalaPrismSDK/PrismAgent/Sources/Protocols/IssueCredential/ProposeCredential.swift @@ -55,7 +55,7 @@ public struct ProposeCredential { let toDID = fromMessage.to else { throw PrismAgentError.invalidMessageType( type: fromMessage.piuri, - shouldBe: ProtocolTypes.didcommProposeCredential.rawValue + shouldBe: [ProtocolTypes.didcommProposeCredential.rawValue] ) } let body = try JSONDecoder.didComm().decode(Body.self, from: fromMessage.body) self.init( @@ -115,3 +115,90 @@ extension ProposeCredential: Equatable { lhs.body == rhs.body } } + +// ALL parameterS are DIDCOMMV2 format and naming conventions and follows the protocol +// https://github.com/hyperledger/aries-rfcs/tree/main/features/0453-issue-credential-v2 +public struct ProposeCredential3_0 { + struct Body: Codable, Equatable { + let goalCode: String? + let comment: String? + let credentialPreview: CredentialPreview3_0 + + init( + goalCode: String? = nil, + comment: String? = nil, + credentialPreview: CredentialPreview3_0 + ) { + self.goalCode = goalCode + self.comment = comment + self.credentialPreview = credentialPreview + } + } + + public let id: String + public let type = ProtocolTypes.didcommProposeCredential3_0.rawValue + let body: Body + let attachments: [AttachmentDescriptor] + public let thid: String? + public let from: DID + public let to: DID + + init( + id: String = UUID().uuidString, + body: Body, + attachments: [AttachmentDescriptor], + thid: String?, + from: DID, + to: DID + ) { + self.id = id + self.body = body + self.attachments = attachments + self.thid = thid + self.from = from + self.to = to + } + + public init(fromMessage: Message) throws { + guard + fromMessage.piuri == ProtocolTypes.didcommProposeCredential3_0.rawValue, + let fromDID = fromMessage.from, + let toDID = fromMessage.to + else { throw PrismAgentError.invalidMessageType( + type: fromMessage.piuri, + shouldBe: [ProtocolTypes.didcommProposeCredential3_0.rawValue] + ) } + let body = try JSONDecoder.didComm().decode(Body.self, from: fromMessage.body) + self.init( + id: fromMessage.id, + body: body, + attachments: fromMessage.attachments, + thid: fromMessage.thid, + from: fromDID, + to: toDID + ) + } + + public func makeMessage() throws -> Message { + .init( + id: id, + piuri: type, + from: from, + to: to, + body: try JSONEncoder.didComm().encode(body), + attachments: attachments, + thid: thid, + direction: .sent + ) + } +} + +extension ProposeCredential3_0: Equatable { + public static func == (lhs: ProposeCredential3_0, rhs: ProposeCredential3_0) -> Bool { + lhs.id == rhs.id && + lhs.type == rhs.type && + lhs.from == rhs.from && + lhs.to == rhs.to && + lhs.body == rhs.body + } +} diff --git a/AtalaPrismSDK/PrismAgent/Sources/Protocols/IssueCredential/RequestCredential.swift b/AtalaPrismSDK/PrismAgent/Sources/Protocols/IssueCredential/RequestCredential.swift index e595d874..7f860648 100644 --- a/AtalaPrismSDK/PrismAgent/Sources/Protocols/IssueCredential/RequestCredential.swift +++ b/AtalaPrismSDK/PrismAgent/Sources/Protocols/IssueCredential/RequestCredential.swift @@ -22,7 +22,7 @@ public struct RequestCredential { } public let id: String - public let type = ProtocolTypes.didcommRequestCredential.rawValue + public let type: String public let body: Body public let attachments: [AttachmentDescriptor] public let thid: String? @@ -32,6 +32,7 @@ public struct RequestCredential { init( id: String = UUID().uuidString, body: Body, + type: String, attachments: [AttachmentDescriptor], thid: String?, from: DID, @@ -39,6 +40,7 @@ public struct RequestCredential { ) { self.id = id self.body = body + self.type = type self.attachments = attachments self.thid = thid self.from = from @@ -47,18 +49,21 @@ public struct RequestCredential { public init(fromMessage: Message) throws { guard - fromMessage.piuri == ProtocolTypes.didcommRequestCredential.rawValue, + let piuri = ProtocolTypes(rawValue: fromMessage.piuri), + (piuri == ProtocolTypes.didcommRequestCredential || + piuri == ProtocolTypes.didcommRequestCredential3_0), let fromDID = fromMessage.from, let toDID = fromMessage.to else { throw PrismAgentError.invalidMessageType( type: fromMessage.piuri, - shouldBe: ProtocolTypes.didcommRequestCredential.rawValue + shouldBe: [ProtocolTypes.didcommRequestCredential.rawValue] ) } let body = try JSONDecoder.didComm().decode(Body.self, from: fromMessage.body) self.init( id: fromMessage.id, body: body, + type: piuri.rawValue, attachments: fromMessage.attachments, thid: fromMessage.thid, from: fromDID, @@ -80,45 +85,156 @@ public struct RequestCredential { } public static func makeRequestFromOfferCredential(offer: OfferCredential) throws -> RequestCredential { + guard + let offerPiuri = ProtocolTypes(rawValue: offer.type) + else { + throw PrismAgentError.invalidMessageType( + type: offer.type, + shouldBe: [ + ProtocolTypes.didcommOfferCredential.rawValue, + ProtocolTypes.didcommOfferCredential3_0.rawValue + ] + ) + } + + let type = offerPiuri == .didcommOfferCredential ? + ProtocolTypes.didcommRequestCredential : + ProtocolTypes.didcommRequestCredential3_0 + return RequestCredential( body: .init( goalCode: offer.body.goalCode, comment: offer.body.comment, formats: offer.body.formats ), + type: type.rawValue, attachments: offer.attachments, thid: offer.thid, // TODO: This needs to be changed in the pr from: offer.to, to: offer.from ) } +} - static func build( - fromDID: DID, - toDID: DID, - thid: String?, - credentialPreview: CredentialPreview, - credentials: [String: T] = [:] - ) throws -> RequestCredential { - let aux = try credentials.map { key, value in - let attachment = try AttachmentDescriptor.build(payload: value) - let format = CredentialFormat(attachId: attachment.id, format: key) - return (format, attachment) +extension RequestCredential: Equatable { + public static func == (lhs: RequestCredential, rhs: RequestCredential) -> Bool { + lhs.id == rhs.id && + lhs.type == rhs.type && + lhs.from == rhs.from && + lhs.to == rhs.to && + lhs.body == rhs.body + } +} + +// ALL parameters are DIDCOMMV2 format and naming conventions and follows the protocol +// https://github.com/hyperledger/aries-rfcs/tree/main/features/0453-issue-credential-v2 +public struct RequestCredential3_0 { + public struct Body: Codable, Equatable { + public let goalCode: String? + public let comment: String? + + public init( + goalCode: String? = nil, + comment: String? = nil + ) { + self.goalCode = goalCode + self.comment = comment } - return RequestCredential( - body: Body( - formats: aux.map { $0.0 } - ), - attachments: aux.map { $0.1 }, - thid: thid, + } + + public let id: String + public let type: String + public let body: Body + public let attachments: [AttachmentDescriptor] + public let thid: String? + public let from: DID + public let to: DID + + init( + id: String = UUID().uuidString, + body: Body, + type: String, + attachments: [AttachmentDescriptor], + thid: String?, + from: DID, + to: DID + ) { + self.id = id + self.body = body + self.type = type + self.attachments = attachments + self.thid = thid + self.from = from + self.to = to + } + + public init(fromMessage: Message) throws { + guard + let piuri = ProtocolTypes(rawValue: fromMessage.piuri), + piuri == ProtocolTypes.didcommRequestCredential3_0, + let fromDID = fromMessage.from, + let toDID = fromMessage.to + else { throw PrismAgentError.invalidMessageType( + type: fromMessage.piuri, + shouldBe: [ProtocolTypes.didcommRequestCredential3_0.rawValue] + ) } + + let body = try JSONDecoder.didComm().decode(Body.self, from: fromMessage.body) + self.init( + id: fromMessage.id, + body: body, + type: piuri.rawValue, + attachments: fromMessage.attachments, + thid: fromMessage.thid, from: fromDID, to: toDID ) } + + public func makeMessage() throws -> Message { + .init( + id: id, + piuri: type, + from: from, + to: to, + body: try JSONEncoder.didComm().encode(body), + attachments: attachments, + thid: thid, + direction: .sent + ) + } + + public static func makeRequestFromOfferCredential(offer: OfferCredential3_0) throws -> RequestCredential3_0 { + guard + let offerPiuri = ProtocolTypes(rawValue: offer.type), + offerPiuri == ProtocolTypes.didcommOfferCredential3_0 + else { + throw PrismAgentError.invalidMessageType( + type: offer.type, + shouldBe: [ + ProtocolTypes.didcommOfferCredential3_0.rawValue + ] + ) + } + + let type = ProtocolTypes.didcommRequestCredential3_0 + + return RequestCredential3_0( + body: .init( + goalCode: offer.body.goalCode, + comment: offer.body.comment + ), + type: type.rawValue, + attachments: offer.attachments, + thid: offer.thid, + from: offer.to, + to: offer.from + ) + } } -extension RequestCredential: Equatable { - public static func == (lhs: RequestCredential, rhs: RequestCredential) -> Bool { +extension RequestCredential3_0: Equatable { + public static func == (lhs: RequestCredential3_0, rhs: RequestCredential3_0) -> Bool { lhs.id == rhs.id && lhs.type == rhs.type && lhs.from == rhs.from && diff --git a/AtalaPrismSDK/PrismAgent/Sources/Protocols/Mediation/MediationGrant.swift b/AtalaPrismSDK/PrismAgent/Sources/Protocols/Mediation/MediationGrant.swift index 4e63f5f4..fff3afda 100644 --- a/AtalaPrismSDK/PrismAgent/Sources/Protocols/Mediation/MediationGrant.swift +++ b/AtalaPrismSDK/PrismAgent/Sources/Protocols/Mediation/MediationGrant.swift @@ -23,7 +23,7 @@ struct MediationGrant { fromMessage.piuri == ProtocolTypes.didcommMediationGrant.rawValue else { throw PrismAgentError.invalidMessageType( type: fromMessage.piuri, - shouldBe: ProtocolTypes.didcommMediationGrant.rawValue + shouldBe: [ProtocolTypes.didcommMediationGrant.rawValue] ) } self.init( id: fromMessage.id, diff --git a/AtalaPrismSDK/PrismAgent/Sources/Protocols/Pickup/PickupRunner.swift b/AtalaPrismSDK/PrismAgent/Sources/Protocols/Pickup/PickupRunner.swift index b209c60c..d3f54ac0 100644 --- a/AtalaPrismSDK/PrismAgent/Sources/Protocols/Pickup/PickupRunner.swift +++ b/AtalaPrismSDK/PrismAgent/Sources/Protocols/Pickup/PickupRunner.swift @@ -19,7 +19,7 @@ class PickupRunner { default: throw PrismAgentError.invalidMessageType( type: message.piuri, - shouldBe: ProtocolTypes.pickupStatus.rawValue + " or " + ProtocolTypes.pickupDelivery.rawValue + shouldBe: [ProtocolTypes.pickupStatus.rawValue, ProtocolTypes.pickupDelivery.rawValue] ) } self.mercury = mercury diff --git a/AtalaPrismSDK/PrismAgent/Sources/Protocols/ProofPresentation/Presentation.swift b/AtalaPrismSDK/PrismAgent/Sources/Protocols/ProofPresentation/Presentation.swift index 3963faa4..102300ef 100644 --- a/AtalaPrismSDK/PrismAgent/Sources/Protocols/ProofPresentation/Presentation.swift +++ b/AtalaPrismSDK/PrismAgent/Sources/Protocols/ProofPresentation/Presentation.swift @@ -58,7 +58,7 @@ public struct Presentation { let toDID = fromMessage.to else { throw PrismAgentError.invalidMessageType( type: fromMessage.piuri, - shouldBe: ProtocolTypes.didcommPresentation.rawValue + shouldBe: [ProtocolTypes.didcommPresentation.rawValue] ) } let body = try JSONDecoder.didComm().decode(Body.self, from: fromMessage.body) diff --git a/AtalaPrismSDK/PrismAgent/Sources/Protocols/ProofPresentation/ProposePresentation.swift b/AtalaPrismSDK/PrismAgent/Sources/Protocols/ProofPresentation/ProposePresentation.swift index 5b6377c1..1ea6025c 100644 --- a/AtalaPrismSDK/PrismAgent/Sources/Protocols/ProofPresentation/ProposePresentation.swift +++ b/AtalaPrismSDK/PrismAgent/Sources/Protocols/ProofPresentation/ProposePresentation.swift @@ -49,7 +49,7 @@ public struct ProposePresentation { let toDID = fromMessage.to else { throw PrismAgentError.invalidMessageType( type: fromMessage.piuri, - shouldBe: ProtocolTypes.didcommProposePresentation.rawValue + shouldBe: [ProtocolTypes.didcommProposePresentation.rawValue] ) } let body = try JSONDecoder.didComm().decode(Body.self, from: fromMessage.body) diff --git a/AtalaPrismSDK/PrismAgent/Sources/Protocols/ProofPresentation/RequestPresentation.swift b/AtalaPrismSDK/PrismAgent/Sources/Protocols/ProofPresentation/RequestPresentation.swift index 731980b1..570e5c25 100644 --- a/AtalaPrismSDK/PrismAgent/Sources/Protocols/ProofPresentation/RequestPresentation.swift +++ b/AtalaPrismSDK/PrismAgent/Sources/Protocols/ProofPresentation/RequestPresentation.swift @@ -52,7 +52,7 @@ public struct RequestPresentation { let toDID = fromMessage.to else { throw PrismAgentError.invalidMessageType( type: fromMessage.piuri, - shouldBe: ProtocolTypes.didcommRequestPresentation.rawValue + shouldBe: [ProtocolTypes.didcommRequestPresentation.rawValue] ) } let body = try JSONDecoder.didComm().decode(Body.self, from: fromMessage.body) diff --git a/AtalaPrismSDK/PrismAgent/Sources/Protocols/ProtocolTypes.swift b/AtalaPrismSDK/PrismAgent/Sources/Protocols/ProtocolTypes.swift index 95695bb2..319462ab 100644 --- a/AtalaPrismSDK/PrismAgent/Sources/Protocols/ProtocolTypes.swift +++ b/AtalaPrismSDK/PrismAgent/Sources/Protocols/ProtocolTypes.swift @@ -1,3 +1,4 @@ +import Domain import Foundation public enum ProtocolTypes: String { @@ -10,10 +11,15 @@ public enum ProtocolTypes: String { case didcommRequestPresentation = "https://didcomm.atalaprism.io/present-proof/3.0/request-presentation" case didcommProposePresentation = "https://didcomm.atalaprism.io/present-proof/3.0/propose-presentation" case didcommCredentialPreview = "https://didcomm.org/issue-credential/2.0/credential-preview" + case didcommCredentialPreview3_0 = "https://didcomm.org/issue-credential/3.0/credential-preview" case didcommIssueCredential = "https://didcomm.org/issue-credential/2.0/issue-credential" + case didcommIssueCredential3_0 = "https://didcomm.org/issue-credential/3.0/issue-credential" case didcommOfferCredential = "https://didcomm.org/issue-credential/2.0/offer-credential" + case didcommOfferCredential3_0 = "https://didcomm.org/issue-credential/3.0/offer-credential" case didcommProposeCredential = "https://didcomm.org/issue-credential/2.0/propose-credential" + case didcommProposeCredential3_0 = "https://didcomm.org/issue-credential/3.0/propose-credential" case didcommRequestCredential = "https://didcomm.org/issue-credential/2.0/request-credential" + case didcommRequestCredential3_0 = "https://didcomm.org/issue-credential/3.0/request-credential" case didcommconnectionRequest = "https://atalaprism.io/mercury/connections/1.0/request" case didcommconnectionResponse = "https://atalaprism.io/mercury/connections/1.0/response" case didcomminvitation = "https://didcomm.org/out-of-band/2.0/invitation" diff --git a/AtalaPrismSDK/PrismAgent/Tests/IssueCredentialTests.swift b/AtalaPrismSDK/PrismAgent/Tests/IssueCredentialTests.swift index 7b469553..f62e5348 100644 --- a/AtalaPrismSDK/PrismAgent/Tests/IssueCredentialTests.swift +++ b/AtalaPrismSDK/PrismAgent/Tests/IssueCredentialTests.swift @@ -6,8 +6,9 @@ final class IssueCredentialTests: XCTestCase { func testWhenValidIssueMessageThenInitIssueCredential() throws { let fromDID = DID(index: 0) let toDID = DID(index: 1) - let validIssueCredential = IssueCredential( - body: .init(formats: [.init(attachId: "test1", format: "test")]), + let validIssueCredential = IssueCredential3_0( + body: .init(), + type: ProtocolTypes.didcommIssueCredential3_0.rawValue, attachments: [], thid: "1", from: fromDID, @@ -15,7 +16,7 @@ final class IssueCredentialTests: XCTestCase { ) let issueMessage = try validIssueCredential.makeMessage() - let testIssueCredential = try IssueCredential(fromMessage: issueMessage) + let testIssueCredential = try IssueCredential3_0(fromMessage: issueMessage) XCTAssertEqual(validIssueCredential, testIssueCredential) } @@ -33,8 +34,9 @@ final class IssueCredentialTests: XCTestCase { func testWhenValidRequestMessageThenInitIssueCredential() throws { let fromDID = DID(index: 0) let toDID = DID(index: 1) - let validRequestCredential = RequestCredential( - body: .init(formats: [.init(attachId: "test1", format: "test")]), + let validRequestCredential = RequestCredential3_0( + body: .init(), + type: ProtocolTypes.didcommRequestCredential3_0.rawValue, attachments: [], thid: "1", from: fromDID, @@ -42,13 +44,12 @@ final class IssueCredentialTests: XCTestCase { ) let requestMessage = try validRequestCredential.makeMessage() - let testIssueCredential = try IssueCredential.makeIssueFromRequestCredential(msg: requestMessage) + let testIssueCredential = try IssueCredential3_0.makeIssueFromRequestCredential(msg: requestMessage) XCTAssertEqual(validRequestCredential.from, testIssueCredential.to) XCTAssertEqual(validRequestCredential.to, testIssueCredential.from) XCTAssertEqual(validRequestCredential.attachments, testIssueCredential.attachments) XCTAssertEqual(validRequestCredential.id, testIssueCredential.thid) XCTAssertEqual(validRequestCredential.body.goalCode, validRequestCredential.body.goalCode) XCTAssertEqual(validRequestCredential.body.comment, validRequestCredential.body.comment) - XCTAssertEqual(validRequestCredential.body.formats, validRequestCredential.body.formats) } } diff --git a/AtalaPrismSDK/PrismAgent/Tests/OfferCredentialTests.swift b/AtalaPrismSDK/PrismAgent/Tests/OfferCredentialTests.swift index ae2cd209..05cfa195 100644 --- a/AtalaPrismSDK/PrismAgent/Tests/OfferCredentialTests.swift +++ b/AtalaPrismSDK/PrismAgent/Tests/OfferCredentialTests.swift @@ -6,21 +6,13 @@ final class OfferCredentialTests: XCTestCase { func testWhenValidOfferMessageThenInitOfferCredential() throws { let fromDID = DID(index: 0) let toDID = DID(index: 1) - let validOfferCredential = OfferCredential( - body: .init( - credentialPreview: .init( - attributes: [ - .init( - name: "test1", - value: "test", - mimeType: "test.x") - ]), - formats: [ - .init( - attachId: "test1", - format: "test") - ] - ), + let validOfferCredential = OfferCredential3_0( + id: "test2", + body: .init(credentialPreview: .init( + schemaId: "test1", + attributes: [.init(name: "test1", value: "test", mediaType: "test.x")] + )), + type: ProtocolTypes.didcommOfferCredential3_0.rawValue, attachments: [], thid: "1", from: fromDID, @@ -28,7 +20,7 @@ final class OfferCredentialTests: XCTestCase { ) let offerMessage = try validOfferCredential.makeMessage() - let testOfferCredential = try OfferCredential(fromMessage: offerMessage) + let testOfferCredential = try OfferCredential3_0(fromMessage: offerMessage) XCTAssertEqual(validOfferCredential, testOfferCredential) } diff --git a/AtalaPrismSDK/PrismAgent/Tests/RequestCredentialTests.swift b/AtalaPrismSDK/PrismAgent/Tests/RequestCredentialTests.swift index f938da75..326d31b8 100644 --- a/AtalaPrismSDK/PrismAgent/Tests/RequestCredentialTests.swift +++ b/AtalaPrismSDK/PrismAgent/Tests/RequestCredentialTests.swift @@ -6,24 +6,19 @@ final class RequestCredentialTests: XCTestCase { func testWhenValidRequestMessageThenInitRequestCredential() throws { let fromDID = DID(index: 0) let toDID = DID(index: 1) - let validRequestCredential = RequestCredential( + let validRequestCredential = RequestCredential3_0( body: .init( goalCode: "test1", - comment: "test1", - formats: [ - .init( - attachId: "test1", - format: "test") - ] + comment: "test1" ), - attachments: [ - ], + type: ProtocolTypes.didcommRequestCredential3_0.rawValue, + attachments: [], thid: "1", from: fromDID, to: toDID ) let requestMessage = try validRequestCredential.makeMessage() - let testRequestCredential = try RequestCredential(fromMessage: requestMessage) + let testRequestCredential = try RequestCredential3_0(fromMessage: requestMessage) XCTAssertEqual(validRequestCredential, testRequestCredential) } @@ -38,28 +33,32 @@ final class RequestCredentialTests: XCTestCase { XCTAssertThrowsError(try RequestCredential(fromMessage: invalidRequestCredential)) } -// func testWhenValidOfferMessageThenInitRequestCredential() throws { -// let fromDID = DID(index: 0) -// let toDID = DID(index: 1) -// let validOfferCredential = OfferCredential( -// body: .init( -// credentialPreview: .init(attributes: []), -// formats: [.init(attachId: "test1", format: "test")] -// ), -// attachments: [], -// thid: "1", -// from: fromDID, -// to: toDID -// ) -// let offerMessage = try validOfferCredential.makeMessage() -// -// let testRequestCredential = try RequestCredential.makeRequestFromOfferCredential(message: offerMessage) -// XCTAssertEqual(validOfferCredential.from, testRequestCredential.to) -// XCTAssertEqual(validOfferCredential.to, testRequestCredential.from) -// XCTAssertEqual(validOfferCredential.attachments, testRequestCredential.attachments) -// XCTAssertEqual(validOfferCredential.id, testRequestCredential.thid) -// XCTAssertEqual(validOfferCredential.body.goalCode, testRequestCredential.body.goalCode) -// XCTAssertEqual(validOfferCredential.body.comment, testRequestCredential.body.comment) -// XCTAssertEqual(validOfferCredential.body.formats, testRequestCredential.body.formats) -// } + func testWhenValidOfferMessageThenInitRequestCredential() throws { + let fromDID = DID(index: 0) + let toDID = DID(index: 1) + let validOfferCredential = OfferCredential3_0( + id: "test", + body: .init( + credentialPreview: .init( + schemaId: "test", + attributes: [ + .init(name: "test1", value: "test", mediaType: "test.x") + ] + ) + ), + type: ProtocolTypes.didcommOfferCredential3_0.rawValue, + attachments: [], + thid: "1", + from: fromDID, + to: toDID + ) + + let testRequestCredential = try RequestCredential3_0.makeRequestFromOfferCredential(offer: validOfferCredential) + XCTAssertEqual(validOfferCredential.from, testRequestCredential.to) + XCTAssertEqual(validOfferCredential.to, testRequestCredential.from) + XCTAssertEqual(validOfferCredential.attachments, testRequestCredential.attachments) + XCTAssertEqual(validOfferCredential.thid, testRequestCredential.thid) + XCTAssertEqual(validOfferCredential.body.goalCode, testRequestCredential.body.goalCode) + XCTAssertEqual(validOfferCredential.body.comment, testRequestCredential.body.comment) + } } diff --git a/Core/Sources/Helpers/Data+Hex.swift b/Core/Sources/Helpers/Data+Hex.swift new file mode 100644 index 00000000..56cb0ba3 --- /dev/null +++ b/Core/Sources/Helpers/Data+Hex.swift @@ -0,0 +1,7 @@ +import Foundation + +public extension Data { + var hex: String { + return self.reduce("") { $0 + String(format: "%02x", $1) } + } +} diff --git a/Core/Sources/Helpers/Data+tryString.swift b/Core/Sources/Helpers/Data+tryString.swift new file mode 100644 index 00000000..1fe350dc --- /dev/null +++ b/Core/Sources/Helpers/Data+tryString.swift @@ -0,0 +1,11 @@ +import Domain +import Foundation + +public extension Data { + func toString(using: String.Encoding = .utf8) throws -> String { + guard let str = String(data: self, encoding: using) else { + throw CommonError.invalidCoding(message: "Could not get String from Data value") + } + return str + } +} diff --git a/Core/Sources/Helpers/JSONEncoder+Helper.swift b/Core/Sources/Helpers/JSONEncoder+Helper.swift index 0adda1a0..f18a7236 100644 --- a/Core/Sources/Helpers/JSONEncoder+Helper.swift +++ b/Core/Sources/Helpers/JSONEncoder+Helper.swift @@ -5,7 +5,7 @@ public extension JSONEncoder { let encoder = JSONEncoder() encoder.dataEncodingStrategy = .base64 encoder.keyEncodingStrategy = .convertToSnakeCase - encoder.outputFormatting = .withoutEscapingSlashes + encoder.outputFormatting = [.withoutEscapingSlashes, .sortedKeys] return encoder } } diff --git a/Core/Sources/Helpers/String+tryData.swift b/Core/Sources/Helpers/String+tryData.swift new file mode 100644 index 00000000..dbd246e2 --- /dev/null +++ b/Core/Sources/Helpers/String+tryData.swift @@ -0,0 +1,11 @@ +import Domain +import Foundation + +public extension String { + func tryData(using: String.Encoding) throws -> Data { + guard let data = self.data(using: .utf8) else { + throw CommonError.invalidCoding(message: "Could not encode to Data") + } + return data + } +} diff --git a/Package.swift b/Package.swift index b19ac939..7e286433 100644 --- a/Package.swift +++ b/Package.swift @@ -68,7 +68,7 @@ let package = Package( .package(url: "git@github.com:swift-libp2p/swift-multibase.git", from: "0.0.1"), .package(url: "git@github.com:GigaBitcoin/secp256k1.swift.git", exact: "0.10.0"), .package(url: "git@github.com:goncalo-frade-iohk/Swift-JWT.git", from: "4.1.3"), - .package(url: "git@github.com:input-output-hk/anoncreds-rs.git", branch: "main") + .package(url: "https://github.com/input-output-hk/anoncreds-rs.git", exact: "0.3.3") ], targets: [ .target( diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo.xcodeproj/project.pbxproj b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo.xcodeproj/project.pbxproj index 3eea104f..e74efbc1 100644 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo.xcodeproj/project.pbxproj +++ b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo.xcodeproj/project.pbxproj @@ -11,6 +11,7 @@ EE0E1FB229473B10003CD7D5 /* SteradianBold.otf in Resources */ = {isa = PBXBuildFile; fileRef = EE0E1FAE29473B10003CD7D5 /* SteradianBold.otf */; }; EE0E1FB329473B10003CD7D5 /* SteradianMedium.otf in Resources */ = {isa = PBXBuildFile; fileRef = EE0E1FAF29473B10003CD7D5 /* SteradianMedium.otf */; }; EE0E1FB429473B10003CD7D5 /* SteradianRegular.otf in Resources */ = {isa = PBXBuildFile; fileRef = EE0E1FB029473B10003CD7D5 /* SteradianRegular.otf */; }; + EE2D40972ACC470100CF9446 /* CredentialDetailViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE2D40962ACC470100CF9446 /* CredentialDetailViewModel.swift */; }; EE3813502938D5B100A3A710 /* Apollo in Frameworks */ = {isa = PBXBuildFile; productRef = EE38134F2938D5B100A3A710 /* Apollo */; }; EE3813522938D5B100A3A710 /* Authenticate in Frameworks */ = {isa = PBXBuildFile; productRef = EE3813512938D5B100A3A710 /* Authenticate */; }; EE3813542938D5B100A3A710 /* Builders in Frameworks */ = {isa = PBXBuildFile; productRef = EE3813532938D5B100A3A710 /* Builders */; }; @@ -20,38 +21,17 @@ EE38135C2938D5B100A3A710 /* Pluto in Frameworks */ = {isa = PBXBuildFile; productRef = EE38135B2938D5B100A3A710 /* Pluto */; }; EE38135E2938D5B100A3A710 /* Pollux in Frameworks */ = {isa = PBXBuildFile; productRef = EE38135D2938D5B100A3A710 /* Pollux */; }; EE3813602938D5B100A3A710 /* PrismAgent in Frameworks */ = {isa = PBXBuildFile; productRef = EE38135F2938D5B100A3A710 /* PrismAgent */; }; - EE6C38D229462516006CD2D3 /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE6C38D129462516006CD2D3 /* MainView.swift */; }; - EE6C38D429462522006CD2D3 /* MainViewRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE6C38D329462522006CD2D3 /* MainViewRouter.swift */; }; - EE6C38D7294625B3006CD2D3 /* ProfileHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE6C38D6294625B3006CD2D3 /* ProfileHeaderView.swift */; }; - EE6C38DA29462659006CD2D3 /* SpecificRoundedRect.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE6C38D929462659006CD2D3 /* SpecificRoundedRect.swift */; }; + EE549F472ACC1F5E0038ED1D /* CredentialDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE549F462ACC1F5E0038ED1D /* CredentialDetailView.swift */; }; + EE549F492ACC1F7D0038ED1D /* CredentialDetailViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE549F482ACC1F7D0038ED1D /* CredentialDetailViewState.swift */; }; EE6C38DC294626E1006CD2D3 /* String+extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE6C38DB294626E1006CD2D3 /* String+extensions.swift */; }; EE6C38E3294627B2006CD2D3 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = EE6C38E2294627B2006CD2D3 /* Localizable.strings */; }; EE6C38E529462822006CD2D3 /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = EE6C38E429462822006CD2D3 /* Localizable.stringsdict */; }; - EE6C38E829462A6F006CD2D3 /* DashboardTabViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE6C38E729462A6F006CD2D3 /* DashboardTabViewController.swift */; }; - EE6C38EA29467A36006CD2D3 /* DashboardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE6C38E929467A36006CD2D3 /* DashboardView.swift */; }; - EE6C38EC29467A7A006CD2D3 /* ClearFullCoverModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE6C38EB29467A7A006CD2D3 /* ClearFullCoverModifier.swift */; }; - EE6C38EE29467A9D006CD2D3 /* DisablePreferenceKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE6C38ED29467A9D006CD2D3 /* DisablePreferenceKey.swift */; }; EE6C38F029468196006CD2D3 /* DIContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE6C38EF29468196006CD2D3 /* DIContainer.swift */; }; EE6C39012946827B006CD2D3 /* ComponentContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE6C39002946827B006CD2D3 /* ComponentContainer.swift */; }; EE6C390329468288006CD2D3 /* Builder.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE6C390229468288006CD2D3 /* Builder.swift */; }; EE6C390529468309006CD2D3 /* LazyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE6C390429468309006CD2D3 /* LazyView.swift */; }; EE6C39072946834F006CD2D3 /* RootPresentationMode.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE6C39062946834F006CD2D3 /* RootPresentationMode.swift */; }; - EE6C3909294683C4006CD2D3 /* DashboardRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE6C3908294683C4006CD2D3 /* DashboardRouter.swift */; }; - EE6C390B294683D5006CD2D3 /* DashboardBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE6C390A294683D5006CD2D3 /* DashboardBuilder.swift */; }; - EE6C39152946851D006CD2D3 /* QRScannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE6C390F2946851D006CD2D3 /* QRScannerView.swift */; }; - EE6C39162946851D006CD2D3 /* CameraViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE6C39102946851D006CD2D3 /* CameraViewController.swift */; }; - EE6C39172946851D006CD2D3 /* QRCodeScannerBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE6C39112946851D006CD2D3 /* QRCodeScannerBuilder.swift */; }; - EE6C39182946851D006CD2D3 /* QRCodeScannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE6C39122946851D006CD2D3 /* QRCodeScannerView.swift */; }; - EE6C39192946851D006CD2D3 /* QRCodeScannerRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE6C39132946851D006CD2D3 /* QRCodeScannerRouter.swift */; }; - EE6C391A2946851D006CD2D3 /* QRCodeScannerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE6C39142946851D006CD2D3 /* QRCodeScannerViewModel.swift */; }; - EE6C393129468533006CD2D3 /* ActivityListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE6C392229468533006CD2D3 /* ActivityListView.swift */; }; - EE6C393429468533006CD2D3 /* HomeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE6C392529468533006CD2D3 /* HomeViewModel.swift */; }; - EE6C393529468533006CD2D3 /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE6C392629468533006CD2D3 /* HomeView.swift */; }; - EE6C393729468533006CD2D3 /* HomeRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE6C392829468533006CD2D3 /* HomeRouter.swift */; }; - EE6C393829468533006CD2D3 /* HomeBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE6C392929468533006CD2D3 /* HomeBuilder.swift */; }; - EE6C393929468533006CD2D3 /* HomeState.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE6C392A29468533006CD2D3 /* HomeState.swift */; }; EE6C393C29468614006CD2D3 /* EmptyNavigationLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE6C393B29468614006CD2D3 /* EmptyNavigationLink.swift */; }; - EE6C393E2946901D006CD2D3 /* DashboardViewModelImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE6C393D2946901D006CD2D3 /* DashboardViewModelImpl.swift */; }; EE6C39402946931E006CD2D3 /* Array+Zipped.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE6C393F2946931E006CD2D3 /* Array+Zipped.swift */; }; EE6C7F4B29C2357A00D866AD /* MediatorPageStateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE6C7F4A29C2357A00D866AD /* MediatorPageStateView.swift */; }; EE6C7F4D29C2367400D866AD /* MediatorPageViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE6C7F4C29C2367400D866AD /* MediatorPageViewModel.swift */; }; @@ -64,36 +44,6 @@ EE75148029C378DB00FFFAA4 /* DIDDetailViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE75147F29C378DB00FFFAA4 /* DIDDetailViewState.swift */; }; EE75A2F729479488007D4405 /* FancyToast.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE75A2F629479488007D4405 /* FancyToast.swift */; }; EE75A2F9294794F9007D4405 /* FancyToastModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE75A2F8294794F8007D4405 /* FancyToastModifier.swift */; }; - EE75A2FB29487F36007D4405 /* MainViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE75A2FA29487F36007D4405 /* MainViewModel.swift */; }; - EE75A302294A33D0007D4405 /* CredentialsListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE75A2FD294A33D0007D4405 /* CredentialsListView.swift */; }; - EE75A303294A33D0007D4405 /* CredentialsListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE75A2FE294A33D0007D4405 /* CredentialsListViewModel.swift */; }; - EE75A304294A33D0007D4405 /* CredentialsListBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE75A2FF294A33D0007D4405 /* CredentialsListBuilder.swift */; }; - EE75A305294A33D0007D4405 /* CredentialsListRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE75A300294A33D0007D4405 /* CredentialsListRouter.swift */; }; - EE75A306294A33D0007D4405 /* CredentialsListState.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE75A301294A33D0007D4405 /* CredentialsListState.swift */; }; - EE75A308294A346B007D4405 /* NavigationTypeUtilModifiers.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE75A307294A346B007D4405 /* NavigationTypeUtilModifiers.swift */; }; - EE75A30E294A34E1007D4405 /* CredentialDetailViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE75A30A294A34E1007D4405 /* CredentialDetailViewModel.swift */; }; - EE75A30F294A34E1007D4405 /* CredentialDetailBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE75A30B294A34E1007D4405 /* CredentialDetailBuilder.swift */; }; - EE75A311294A34E1007D4405 /* CredentialDetailNeoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE75A30D294A34E1007D4405 /* CredentialDetailNeoView.swift */; }; - EE75A313294A4A3F007D4405 /* LoadingModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE75A312294A4A3F007D4405 /* LoadingModifier.swift */; }; - EE75A31D294A7CAE007D4405 /* AddNewContactView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE75A315294A7CAE007D4405 /* AddNewContactView.swift */; }; - EE75A31E294A7CAE007D4405 /* AlreadyConnectedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE75A317294A7CAE007D4405 /* AlreadyConnectedView.swift */; }; - EE75A31F294A7CAE007D4405 /* ConfirmConnectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE75A318294A7CAE007D4405 /* ConfirmConnectionView.swift */; }; - EE75A320294A7CAE007D4405 /* InsertCodeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE75A319294A7CAE007D4405 /* InsertCodeView.swift */; }; - EE75A321294A7CAE007D4405 /* AddNewContactBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE75A31A294A7CAE007D4405 /* AddNewContactBuilder.swift */; }; - EE75A322294A7CAE007D4405 /* AddNewContactState.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE75A31B294A7CAE007D4405 /* AddNewContactState.swift */; }; - EE75A323294A7CAE007D4405 /* AddNewContactViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE75A31C294A7CAE007D4405 /* AddNewContactViewModel.swift */; }; - EE75A325294A7D0F007D4405 /* AtalaButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE75A324294A7D0F007D4405 /* AtalaButton.swift */; }; - EE75A327294A7D40007D4405 /* Button+Configuration.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE75A326294A7D40007D4405 /* Button+Configuration.swift */; }; - EE75A329294A7D99007D4405 /* IsLoadingEnvironmentValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE75A328294A7D99007D4405 /* IsLoadingEnvironmentValue.swift */; }; - EE75A32B294A822C007D4405 /* ErrorDialogView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE75A32A294A822C007D4405 /* ErrorDialogView.swift */; }; - EE75A32D294A82A4007D4405 /* RoundedRectWithBorderText.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE75A32C294A82A4007D4405 /* RoundedRectWithBorderText.swift */; }; - EE75A32F294A8D55007D4405 /* DisplayErrorState.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE75A32E294A8D55007D4405 /* DisplayErrorState.swift */; }; - EE75A337294A905F007D4405 /* ProofOfRequestCheckView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE75A332294A905F007D4405 /* ProofOfRequestCheckView.swift */; }; - EE75A338294A905F007D4405 /* ProofOfRequestBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE75A333294A905F007D4405 /* ProofOfRequestBuilder.swift */; }; - EE75A339294A905F007D4405 /* ProofOfRequestState.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE75A334294A905F007D4405 /* ProofOfRequestState.swift */; }; - EE75A33A294A905F007D4405 /* ProofOfRequestViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE75A335294A905F007D4405 /* ProofOfRequestViewModel.swift */; }; - EE75A33B294A905F007D4405 /* ProofOfRequestView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE75A336294A905F007D4405 /* ProofOfRequestView.swift */; }; - EE75A33D294A9172007D4405 /* CheckButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE75A33C294A9172007D4405 /* CheckButton.swift */; }; EEB7D32229420180006E076D /* SetupPrismAgentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEB7D32129420180006E076D /* SetupPrismAgentView.swift */; }; EEB7D3242942018C006E076D /* SetupPrismAgentViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEB7D3232942018C006E076D /* SetupPrismAgentViewModel.swift */; }; EEBC938D29C730FA0015A36E /* CredentialListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EEBC938C29C730FA0015A36E /* CredentialListView.swift */; }; @@ -131,39 +81,19 @@ EE0E1FAE29473B10003CD7D5 /* SteradianBold.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = SteradianBold.otf; sourceTree = ""; }; EE0E1FAF29473B10003CD7D5 /* SteradianMedium.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = SteradianMedium.otf; sourceTree = ""; }; EE0E1FB029473B10003CD7D5 /* SteradianRegular.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = SteradianRegular.otf; sourceTree = ""; }; + EE2D40962ACC470100CF9446 /* CredentialDetailViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CredentialDetailViewModel.swift; sourceTree = ""; }; EE38134E2938D55D00A3A710 /* atala-prism-swift-sdk */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = "atala-prism-swift-sdk"; path = ../..; sourceTree = ""; }; - EE6C38D129462516006CD2D3 /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = ""; }; - EE6C38D329462522006CD2D3 /* MainViewRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainViewRouter.swift; sourceTree = ""; }; - EE6C38D6294625B3006CD2D3 /* ProfileHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ProfileHeaderView.swift; path = ../../Profile/ProfileHeaderView.swift; sourceTree = ""; }; - EE6C38D929462659006CD2D3 /* SpecificRoundedRect.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpecificRoundedRect.swift; sourceTree = ""; }; + EE549F462ACC1F5E0038ED1D /* CredentialDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CredentialDetailView.swift; sourceTree = ""; }; + EE549F482ACC1F7D0038ED1D /* CredentialDetailViewState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CredentialDetailViewState.swift; sourceTree = ""; }; EE6C38DB294626E1006CD2D3 /* String+extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+extensions.swift"; sourceTree = ""; }; EE6C38E2294627B2006CD2D3 /* Localizable.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; path = Localizable.strings; sourceTree = ""; }; EE6C38E429462822006CD2D3 /* Localizable.stringsdict */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.stringsdict; path = Localizable.stringsdict; sourceTree = ""; }; - EE6C38E729462A6F006CD2D3 /* DashboardTabViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DashboardTabViewController.swift; sourceTree = ""; }; - EE6C38E929467A36006CD2D3 /* DashboardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DashboardView.swift; sourceTree = ""; }; - EE6C38EB29467A7A006CD2D3 /* ClearFullCoverModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClearFullCoverModifier.swift; sourceTree = ""; }; - EE6C38ED29467A9D006CD2D3 /* DisablePreferenceKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisablePreferenceKey.swift; sourceTree = ""; }; EE6C38EF29468196006CD2D3 /* DIContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DIContainer.swift; sourceTree = ""; }; EE6C39002946827B006CD2D3 /* ComponentContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComponentContainer.swift; sourceTree = ""; }; EE6C390229468288006CD2D3 /* Builder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Builder.swift; sourceTree = ""; }; EE6C390429468309006CD2D3 /* LazyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LazyView.swift; sourceTree = ""; }; EE6C39062946834F006CD2D3 /* RootPresentationMode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootPresentationMode.swift; sourceTree = ""; }; - EE6C3908294683C4006CD2D3 /* DashboardRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DashboardRouter.swift; sourceTree = ""; }; - EE6C390A294683D5006CD2D3 /* DashboardBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DashboardBuilder.swift; sourceTree = ""; }; - EE6C390F2946851D006CD2D3 /* QRScannerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QRScannerView.swift; sourceTree = ""; }; - EE6C39102946851D006CD2D3 /* CameraViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CameraViewController.swift; sourceTree = ""; }; - EE6C39112946851D006CD2D3 /* QRCodeScannerBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QRCodeScannerBuilder.swift; sourceTree = ""; }; - EE6C39122946851D006CD2D3 /* QRCodeScannerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QRCodeScannerView.swift; sourceTree = ""; }; - EE6C39132946851D006CD2D3 /* QRCodeScannerRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QRCodeScannerRouter.swift; sourceTree = ""; }; - EE6C39142946851D006CD2D3 /* QRCodeScannerViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QRCodeScannerViewModel.swift; sourceTree = ""; }; - EE6C392229468533006CD2D3 /* ActivityListView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActivityListView.swift; sourceTree = ""; }; - EE6C392529468533006CD2D3 /* HomeViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeViewModel.swift; sourceTree = ""; }; - EE6C392629468533006CD2D3 /* HomeView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeView.swift; sourceTree = ""; }; - EE6C392829468533006CD2D3 /* HomeRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeRouter.swift; sourceTree = ""; }; - EE6C392929468533006CD2D3 /* HomeBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeBuilder.swift; sourceTree = ""; }; - EE6C392A29468533006CD2D3 /* HomeState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeState.swift; sourceTree = ""; }; EE6C393B29468614006CD2D3 /* EmptyNavigationLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyNavigationLink.swift; sourceTree = ""; }; - EE6C393D2946901D006CD2D3 /* DashboardViewModelImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DashboardViewModelImpl.swift; sourceTree = ""; }; EE6C393F2946931E006CD2D3 /* Array+Zipped.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+Zipped.swift"; sourceTree = ""; }; EE6C7F4A29C2357A00D866AD /* MediatorPageStateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediatorPageStateView.swift; sourceTree = ""; }; EE6C7F4C29C2367400D866AD /* MediatorPageViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediatorPageViewModel.swift; sourceTree = ""; }; @@ -176,36 +106,6 @@ EE75147F29C378DB00FFFAA4 /* DIDDetailViewState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DIDDetailViewState.swift; sourceTree = ""; }; EE75A2F629479488007D4405 /* FancyToast.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FancyToast.swift; sourceTree = ""; }; EE75A2F8294794F8007D4405 /* FancyToastModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FancyToastModifier.swift; sourceTree = ""; }; - EE75A2FA29487F36007D4405 /* MainViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainViewModel.swift; sourceTree = ""; }; - EE75A2FD294A33D0007D4405 /* CredentialsListView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CredentialsListView.swift; sourceTree = ""; }; - EE75A2FE294A33D0007D4405 /* CredentialsListViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CredentialsListViewModel.swift; sourceTree = ""; }; - EE75A2FF294A33D0007D4405 /* CredentialsListBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CredentialsListBuilder.swift; sourceTree = ""; }; - EE75A300294A33D0007D4405 /* CredentialsListRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CredentialsListRouter.swift; sourceTree = ""; }; - EE75A301294A33D0007D4405 /* CredentialsListState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CredentialsListState.swift; sourceTree = ""; }; - EE75A307294A346B007D4405 /* NavigationTypeUtilModifiers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationTypeUtilModifiers.swift; sourceTree = ""; }; - EE75A30A294A34E1007D4405 /* CredentialDetailViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CredentialDetailViewModel.swift; sourceTree = ""; }; - EE75A30B294A34E1007D4405 /* CredentialDetailBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CredentialDetailBuilder.swift; sourceTree = ""; }; - EE75A30D294A34E1007D4405 /* CredentialDetailNeoView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CredentialDetailNeoView.swift; sourceTree = ""; }; - EE75A312294A4A3F007D4405 /* LoadingModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingModifier.swift; sourceTree = ""; }; - EE75A315294A7CAE007D4405 /* AddNewContactView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddNewContactView.swift; sourceTree = ""; }; - EE75A317294A7CAE007D4405 /* AlreadyConnectedView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlreadyConnectedView.swift; sourceTree = ""; }; - EE75A318294A7CAE007D4405 /* ConfirmConnectionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConfirmConnectionView.swift; sourceTree = ""; }; - EE75A319294A7CAE007D4405 /* InsertCodeView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InsertCodeView.swift; sourceTree = ""; }; - EE75A31A294A7CAE007D4405 /* AddNewContactBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddNewContactBuilder.swift; sourceTree = ""; }; - EE75A31B294A7CAE007D4405 /* AddNewContactState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddNewContactState.swift; sourceTree = ""; }; - EE75A31C294A7CAE007D4405 /* AddNewContactViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddNewContactViewModel.swift; sourceTree = ""; }; - EE75A324294A7D0F007D4405 /* AtalaButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AtalaButton.swift; sourceTree = ""; }; - EE75A326294A7D40007D4405 /* Button+Configuration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Button+Configuration.swift"; sourceTree = ""; }; - EE75A328294A7D99007D4405 /* IsLoadingEnvironmentValue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IsLoadingEnvironmentValue.swift; sourceTree = ""; }; - EE75A32A294A822C007D4405 /* ErrorDialogView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorDialogView.swift; sourceTree = ""; }; - EE75A32C294A82A4007D4405 /* RoundedRectWithBorderText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoundedRectWithBorderText.swift; sourceTree = ""; }; - EE75A32E294A8D55007D4405 /* DisplayErrorState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisplayErrorState.swift; sourceTree = ""; }; - EE75A332294A905F007D4405 /* ProofOfRequestCheckView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProofOfRequestCheckView.swift; sourceTree = ""; }; - EE75A333294A905F007D4405 /* ProofOfRequestBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProofOfRequestBuilder.swift; sourceTree = ""; }; - EE75A334294A905F007D4405 /* ProofOfRequestState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProofOfRequestState.swift; sourceTree = ""; }; - EE75A335294A905F007D4405 /* ProofOfRequestViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProofOfRequestViewModel.swift; sourceTree = ""; }; - EE75A336294A905F007D4405 /* ProofOfRequestView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProofOfRequestView.swift; sourceTree = ""; }; - EE75A33C294A9172007D4405 /* CheckButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckButton.swift; sourceTree = ""; }; EEB7D32129420180006E076D /* SetupPrismAgentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetupPrismAgentView.swift; sourceTree = ""; }; EEB7D3232942018C006E076D /* SetupPrismAgentViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetupPrismAgentViewModel.swift; sourceTree = ""; }; EEBC938C29C730FA0015A36E /* CredentialListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CredentialListView.swift; sourceTree = ""; }; @@ -287,49 +187,14 @@ name = Packages; sourceTree = ""; }; - EE6C38CA29462474006CD2D3 /* WalletDemo */ = { + EE549F452ACC1F400038ED1D /* CredentialDetail */ = { isa = PBXGroup; children = ( - EE75A330294A905F007D4405 /* ProofOfRequest */, - EE75A314294A7CAE007D4405 /* AddNewContact */, - EE75A309294A34E1007D4405 /* CredentialDetail */, - EE75A2FC294A33D0007D4405 /* CredentialsList */, - EE6C391B29468533006CD2D3 /* Home */, - EE6C390D2946851D006CD2D3 /* QRCodeScanner */, - EE6C38E629462A60006CD2D3 /* Dashboard */, - EE6C38D82946264C006CD2D3 /* UIHelper */, - EE6C38D0294624FD006CD2D3 /* Main */, + EE549F462ACC1F5E0038ED1D /* CredentialDetailView.swift */, + EE549F482ACC1F7D0038ED1D /* CredentialDetailViewState.swift */, + EE2D40962ACC470100CF9446 /* CredentialDetailViewModel.swift */, ); - path = WalletDemo; - sourceTree = ""; - }; - EE6C38D0294624FD006CD2D3 /* Main */ = { - isa = PBXGroup; - children = ( - EE6C38D129462516006CD2D3 /* MainView.swift */, - EE6C38D329462522006CD2D3 /* MainViewRouter.swift */, - EE75A2FA29487F36007D4405 /* MainViewModel.swift */, - ); - path = Main; - sourceTree = ""; - }; - EE6C38D82946264C006CD2D3 /* UIHelper */ = { - isa = PBXGroup; - children = ( - EE6C38D929462659006CD2D3 /* SpecificRoundedRect.swift */, - EE6C38EB29467A7A006CD2D3 /* ClearFullCoverModifier.swift */, - EE6C38ED29467A9D006CD2D3 /* DisablePreferenceKey.swift */, - EE75A307294A346B007D4405 /* NavigationTypeUtilModifiers.swift */, - EE75A312294A4A3F007D4405 /* LoadingModifier.swift */, - EE75A324294A7D0F007D4405 /* AtalaButton.swift */, - EE75A326294A7D40007D4405 /* Button+Configuration.swift */, - EE75A328294A7D99007D4405 /* IsLoadingEnvironmentValue.swift */, - EE75A32A294A822C007D4405 /* ErrorDialogView.swift */, - EE75A32C294A82A4007D4405 /* RoundedRectWithBorderText.swift */, - EE75A32E294A8D55007D4405 /* DisplayErrorState.swift */, - EE75A33C294A9172007D4405 /* CheckButton.swift */, - ); - path = UIHelper; + path = CredentialDetail; sourceTree = ""; }; EE6C38DD2946276F006CD2D3 /* Resources */ = { @@ -342,61 +207,6 @@ path = Resources; sourceTree = ""; }; - EE6C38E629462A60006CD2D3 /* Dashboard */ = { - isa = PBXGroup; - children = ( - EE6C38E729462A6F006CD2D3 /* DashboardTabViewController.swift */, - EE6C38E929467A36006CD2D3 /* DashboardView.swift */, - EE6C3908294683C4006CD2D3 /* DashboardRouter.swift */, - EE6C390A294683D5006CD2D3 /* DashboardBuilder.swift */, - EE6C393D2946901D006CD2D3 /* DashboardViewModelImpl.swift */, - ); - path = Dashboard; - sourceTree = ""; - }; - EE6C390D2946851D006CD2D3 /* QRCodeScanner */ = { - isa = PBXGroup; - children = ( - EE6C390E2946851D006CD2D3 /* UI */, - EE6C39112946851D006CD2D3 /* QRCodeScannerBuilder.swift */, - EE6C39122946851D006CD2D3 /* QRCodeScannerView.swift */, - EE6C39132946851D006CD2D3 /* QRCodeScannerRouter.swift */, - EE6C39142946851D006CD2D3 /* QRCodeScannerViewModel.swift */, - ); - path = QRCodeScanner; - sourceTree = ""; - }; - EE6C390E2946851D006CD2D3 /* UI */ = { - isa = PBXGroup; - children = ( - EE6C390F2946851D006CD2D3 /* QRScannerView.swift */, - EE6C39102946851D006CD2D3 /* CameraViewController.swift */, - ); - path = UI; - sourceTree = ""; - }; - EE6C391B29468533006CD2D3 /* Home */ = { - isa = PBXGroup; - children = ( - EE6C391C29468533006CD2D3 /* UI */, - EE6C392529468533006CD2D3 /* HomeViewModel.swift */, - EE6C392629468533006CD2D3 /* HomeView.swift */, - EE6C392829468533006CD2D3 /* HomeRouter.swift */, - EE6C392929468533006CD2D3 /* HomeBuilder.swift */, - EE6C392A29468533006CD2D3 /* HomeState.swift */, - ); - path = Home; - sourceTree = ""; - }; - EE6C391C29468533006CD2D3 /* UI */ = { - isa = PBXGroup; - children = ( - EE6C38D6294625B3006CD2D3 /* ProfileHeaderView.swift */, - EE6C392229468533006CD2D3 /* ActivityListView.swift */, - ); - path = UI; - sourceTree = ""; - }; EE6C7F4729C234C200D866AD /* WalletDemo2 */ = { isa = PBXGroup; children = ( @@ -457,70 +267,6 @@ path = DIDList; sourceTree = ""; }; - EE75A2FC294A33D0007D4405 /* CredentialsList */ = { - isa = PBXGroup; - children = ( - EE75A2FD294A33D0007D4405 /* CredentialsListView.swift */, - EE75A2FE294A33D0007D4405 /* CredentialsListViewModel.swift */, - EE75A2FF294A33D0007D4405 /* CredentialsListBuilder.swift */, - EE75A300294A33D0007D4405 /* CredentialsListRouter.swift */, - EE75A301294A33D0007D4405 /* CredentialsListState.swift */, - ); - path = CredentialsList; - sourceTree = ""; - }; - EE75A309294A34E1007D4405 /* CredentialDetail */ = { - isa = PBXGroup; - children = ( - EE75A30A294A34E1007D4405 /* CredentialDetailViewModel.swift */, - EE75A30B294A34E1007D4405 /* CredentialDetailBuilder.swift */, - EE75A30D294A34E1007D4405 /* CredentialDetailNeoView.swift */, - ); - path = CredentialDetail; - sourceTree = ""; - }; - EE75A314294A7CAE007D4405 /* AddNewContact */ = { - isa = PBXGroup; - children = ( - EE75A315294A7CAE007D4405 /* AddNewContactView.swift */, - EE75A316294A7CAE007D4405 /* UI */, - EE75A31A294A7CAE007D4405 /* AddNewContactBuilder.swift */, - EE75A31B294A7CAE007D4405 /* AddNewContactState.swift */, - EE75A31C294A7CAE007D4405 /* AddNewContactViewModel.swift */, - ); - path = AddNewContact; - sourceTree = ""; - }; - EE75A316294A7CAE007D4405 /* UI */ = { - isa = PBXGroup; - children = ( - EE75A317294A7CAE007D4405 /* AlreadyConnectedView.swift */, - EE75A318294A7CAE007D4405 /* ConfirmConnectionView.swift */, - EE75A319294A7CAE007D4405 /* InsertCodeView.swift */, - ); - path = UI; - sourceTree = ""; - }; - EE75A330294A905F007D4405 /* ProofOfRequest */ = { - isa = PBXGroup; - children = ( - EE75A331294A905F007D4405 /* UI */, - EE75A333294A905F007D4405 /* ProofOfRequestBuilder.swift */, - EE75A334294A905F007D4405 /* ProofOfRequestState.swift */, - EE75A335294A905F007D4405 /* ProofOfRequestViewModel.swift */, - EE75A336294A905F007D4405 /* ProofOfRequestView.swift */, - ); - path = ProofOfRequest; - sourceTree = ""; - }; - EE75A331294A905F007D4405 /* UI */ = { - isa = PBXGroup; - children = ( - EE75A332294A905F007D4405 /* ProofOfRequestCheckView.swift */, - ); - path = UI; - sourceTree = ""; - }; EEB7D32029420155006E076D /* SetupPrismAgent */ = { isa = PBXGroup; children = ( @@ -533,7 +279,7 @@ EEBC938929C730900015A36E /* Credentials */ = { isa = PBXGroup; children = ( - EEBC939329C735640015A36E /* CredentialsDetail */, + EE549F452ACC1F400038ED1D /* CredentialDetail */, EEBC939229C7354E0015A36E /* CredentialsList */, ); path = Credentials; @@ -550,13 +296,6 @@ path = CredentialsList; sourceTree = ""; }; - EEBC939329C735640015A36E /* CredentialsDetail */ = { - isa = PBXGroup; - children = ( - ); - path = CredentialsDetail; - sourceTree = ""; - }; EEE61FAD2937CA280053AE52 = { isa = PBXGroup; children = ( @@ -601,7 +340,6 @@ isa = PBXGroup; children = ( EE6C7F4729C234C200D866AD /* WalletDemo2 */, - EE6C38CA29462474006CD2D3 /* WalletDemo */, EEB7D32029420155006E076D /* SetupPrismAgent */, EEE6202C2937FD4B0053AE52 /* AuthenticateWallet */, EEE620142937F2C80053AE52 /* SigningVerification */, @@ -810,104 +548,54 @@ buildActionMask = 2147483647; files = ( EEECB29029C29F0700BBB4B9 /* MessagesListViewModel.swift in Sources */, - EE75A303294A33D0007D4405 /* CredentialsListViewModel.swift in Sources */, EEBC939729C737DE0015A36E /* CredentialListRouter.swift in Sources */, - EE75A2FB29487F36007D4405 /* MainViewModel.swift in Sources */, - EE75A33D294A9172007D4405 /* CheckButton.swift in Sources */, - EE75A323294A7CAE007D4405 /* AddNewContactViewModel.swift in Sources */, EEB7D3242942018C006E076D /* SetupPrismAgentViewModel.swift in Sources */, EEE620132937F1D40053AE52 /* PrintObjects.swift in Sources */, EEECB29929C309FC00BBB4B9 /* MessageDetailViewModel.swift in Sources */, EEECB28C29C29A5400BBB4B9 /* MessagesListView.swift in Sources */, - EE6C393E2946901D006CD2D3 /* DashboardViewModelImpl.swift in Sources */, EEE620302937FDE50053AE52 /* AuthenticateWalletViewModel.swift in Sources */, EE6C390529468309006CD2D3 /* LazyView.swift in Sources */, - EE6C39172946851D006CD2D3 /* QRCodeScannerBuilder.swift in Sources */, EE75147E29C376E700FFFAA4 /* DIDDetailView.swift in Sources */, - EE6C39152946851D006CD2D3 /* QRScannerView.swift in Sources */, EEE61FE02937CEAA0053AE52 /* SeedViewModel.swift in Sources */, - EE75A33A294A905F007D4405 /* ProofOfRequestViewModel.swift in Sources */, - EE75A302294A33D0007D4405 /* CredentialsListView.swift in Sources */, - EE6C393929468533006CD2D3 /* HomeState.swift in Sources */, EEBC939529C735910015A36E /* CredentialListViewModel.swift in Sources */, EE6C38DC294626E1006CD2D3 /* String+extensions.swift in Sources */, EEE61FE82937D7EE0053AE52 /* DIDFuncionalitiesView.swift in Sources */, EEE61FBC2937CA280053AE52 /* FuncionalitiesList.swift in Sources */, - EE75A327294A7D40007D4405 /* Button+Configuration.swift in Sources */, - EE6C3909294683C4006CD2D3 /* DashboardRouter.swift in Sources */, - EE75A339294A905F007D4405 /* ProofOfRequestState.swift in Sources */, EEE61FDD2937CD7A0053AE52 /* SeedFuncionalitiesView.swift in Sources */, - EE75A325294A7D0F007D4405 /* AtalaButton.swift in Sources */, - EE6C38EE29467A9D006CD2D3 /* DisablePreferenceKey.swift in Sources */, - EE6C38E829462A6F006CD2D3 /* DashboardTabViewController.swift in Sources */, EE75A2F9294794F9007D4405 /* FancyToastModifier.swift in Sources */, + EE549F492ACC1F7D0038ED1D /* CredentialDetailViewState.swift in Sources */, EE6C7F5429C2754500D866AD /* DIDListView.swift in Sources */, EE6C7F4B29C2357A00D866AD /* MediatorPageStateView.swift in Sources */, - EE6C393529468533006CD2D3 /* HomeView.swift in Sources */, - EE75A321294A7CAE007D4405 /* AddNewContactBuilder.swift in Sources */, EEECB29229C2A37700BBB4B9 /* MessagesListRouter.swift in Sources */, - EE75A32B294A822C007D4405 /* ErrorDialogView.swift in Sources */, EE75148029C378DB00FFFAA4 /* DIDDetailViewState.swift in Sources */, - EE75A305294A33D0007D4405 /* CredentialsListRouter.swift in Sources */, - EE75A31E294A7CAE007D4405 /* AlreadyConnectedView.swift in Sources */, - EE75A337294A905F007D4405 /* ProofOfRequestCheckView.swift in Sources */, - EE75A311294A34E1007D4405 /* CredentialDetailNeoView.swift in Sources */, EE6C7F5129C23A3400D866AD /* MediatorPageView.swift in Sources */, - EE75A30E294A34E1007D4405 /* CredentialDetailViewModel.swift in Sources */, - EE6C38EC29467A7A006CD2D3 /* ClearFullCoverModifier.swift in Sources */, - EE6C38D429462522006CD2D3 /* MainViewRouter.swift in Sources */, - EE75A329294A7D99007D4405 /* IsLoadingEnvironmentValue.swift in Sources */, EE75A2F729479488007D4405 /* FancyToast.swift in Sources */, - EE75A322294A7CAE007D4405 /* AddNewContactState.swift in Sources */, - EE6C39162946851D006CD2D3 /* CameraViewController.swift in Sources */, EE75147C29C3766800FFFAA4 /* DIDDetailViewModel.swift in Sources */, EE6C7F5629C2758100D866AD /* DIDListViewState.swift in Sources */, EE6C393C29468614006CD2D3 /* EmptyNavigationLink.swift in Sources */, - EE75A32D294A82A4007D4405 /* RoundedRectWithBorderText.swift in Sources */, EE6C39012946827B006CD2D3 /* ComponentContainer.swift in Sources */, - EE6C393729468533006CD2D3 /* HomeRouter.swift in Sources */, EEE61FE42937D5560053AE52 /* DIDFuncionalitiesViewModel.swift in Sources */, - EE75A30F294A34E1007D4405 /* CredentialDetailBuilder.swift in Sources */, - EE6C38D7294625B3006CD2D3 /* ProfileHeaderView.swift in Sources */, - EE75A31F294A7CAE007D4405 /* ConfirmConnectionView.swift in Sources */, - EE75A31D294A7CAE007D4405 /* AddNewContactView.swift in Sources */, - EE6C38D229462516006CD2D3 /* MainView.swift in Sources */, EEE6202F2937FDE50053AE52 /* AuthenticateWalletView.swift in Sources */, EEE620182937F6870053AE52 /* SigningVerificationView.swift in Sources */, - EE75A313294A4A3F007D4405 /* LoadingModifier.swift in Sources */, - EE6C391A2946851D006CD2D3 /* QRCodeScannerViewModel.swift in Sources */, EEE61FBA2937CA280053AE52 /* AtalaPrismWalletDemoApp.swift in Sources */, - EE6C390B294683D5006CD2D3 /* DashboardBuilder.swift in Sources */, + EE2D40972ACC470100CF9446 /* CredentialDetailViewModel.swift in Sources */, EEECB28129C282D500BBB4B9 /* ConnectionsViewState.swift in Sources */, EE6C39402946931E006CD2D3 /* Array+Zipped.swift in Sources */, - EE6C39192946851D006CD2D3 /* QRCodeScannerRouter.swift in Sources */, EEECB27F29C282A800BBB4B9 /* ConnectionsListView.swift in Sources */, - EE6C393129468533006CD2D3 /* ActivityListView.swift in Sources */, - EE75A308294A346B007D4405 /* NavigationTypeUtilModifiers.swift in Sources */, EEECB29529C2A48B00BBB4B9 /* MessageDetailView.swift in Sources */, EE6C390329468288006CD2D3 /* Builder.swift in Sources */, - EE75A338294A905F007D4405 /* ProofOfRequestBuilder.swift in Sources */, - EE6C39182946851D006CD2D3 /* QRCodeScannerView.swift in Sources */, EEECB29729C2A4AA00BBB4B9 /* MessageDetailViewState.swift in Sources */, - EE6C38EA29467A36006CD2D3 /* DashboardView.swift in Sources */, + EE549F472ACC1F5E0038ED1D /* CredentialDetailView.swift in Sources */, EE6C7F5829C275C400D866AD /* DIDListViewModel.swift in Sources */, - EE75A320294A7CAE007D4405 /* InsertCodeView.swift in Sources */, EE6C7F4D29C2367400D866AD /* MediatorPageViewModel.swift in Sources */, - EE6C38DA29462659006CD2D3 /* SpecificRoundedRect.swift in Sources */, EEBC938D29C730FA0015A36E /* CredentialListView.swift in Sources */, EEECB28329C2831E00BBB4B9 /* ConnectionsListViewModel.swift in Sources */, EEBC938F29C7311C0015A36E /* CredentialListViewState.swift in Sources */, EEECB28829C28CA100BBB4B9 /* Main2Router.swift in Sources */, - EE6C393429468533006CD2D3 /* HomeViewModel.swift in Sources */, - EE75A304294A33D0007D4405 /* CredentialsListBuilder.swift in Sources */, EEB7D32229420180006E076D /* SetupPrismAgentView.swift in Sources */, EEECB28E29C29A7300BBB4B9 /* MessagesListViewState.swift in Sources */, EE6C38F029468196006CD2D3 /* DIContainer.swift in Sources */, - EE75A306294A33D0007D4405 /* CredentialsListState.swift in Sources */, - EE75A33B294A905F007D4405 /* ProofOfRequestView.swift in Sources */, EEECB28629C28A8400BBB4B9 /* Main2View.swift in Sources */, - EE6C393829468533006CD2D3 /* HomeBuilder.swift in Sources */, - EE75A32F294A8D55007D4405 /* DisplayErrorState.swift in Sources */, EEE620162937F3110053AE52 /* SigningVerificationViewModel.swift in Sources */, EE6C39072946834F006CD2D3 /* RootPresentationMode.swift in Sources */, ); diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/FuncionalitiesList.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/FuncionalitiesList.swift index 4a321db5..4f7c26f3 100644 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/FuncionalitiesList.swift +++ b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/FuncionalitiesList.swift @@ -1,7 +1,6 @@ import SwiftUI struct FuncionalitiesList: View { - let mainRouter = MainViewRouterImpl() @State var presentWallet2 = false var body: some View { NavigationStack { @@ -9,21 +8,13 @@ struct FuncionalitiesList: View { NavigationLink("Seed Funcionalities", destination: SeedFuncionalitiesView(model: .init())) NavigationLink("DID Funcionalities", destination: DIDFuncionalitiesView(model: .init())) NavigationLink("Signing/Verification Funcionalities", destination: SigningVerificationView(model: .init())) -// NavigationLink("Authenticate Wallet Side", destination: AuthenticateWalletView(viewModel: AuthenticateWalletViewModelImpl()) -// ) NavigationLink("Setup Prism Agent", destination: SetupPrismAgentView(viewModel: SetupPrismAgentViewModelImpl()) ) Button { self.presentWallet2 = true } label: { - Text("Wallet Demo 2.0") + Text("Wallet Demo") } -// Button { -// self.presentWallet2 = true -// } label: { -// Text("Wallet Demo") -// } -// NavigationLink("Wallet Demo", destination: MainView(viewModel: MainViewModelImpl(router: mainRouter), router: mainRouter)) } .buttonStyle(.plain) .fullScreenCover(isPresented: $presentWallet2) { diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/AddNewContact/AddNewContactBuilder.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/AddNewContact/AddNewContactBuilder.swift deleted file mode 100644 index b99c4dff..00000000 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/AddNewContact/AddNewContactBuilder.swift +++ /dev/null @@ -1,22 +0,0 @@ -import SwiftUI -import PrismAgent - -struct AddNewContactComponent: ComponentContainer { - let container: DIContainer - let token: String? -} - -struct AddNewContactBuilder: Builder { - func build(component: AddNewContactComponent) -> some View { - let viewModel = getViewModel(component: component) { - AddNewContactViewModelImpl( - token: component.token ?? "", - agent: component.container.resolve(type: PrismAgent.self)! - ) - } - return AddNewContactView(viewModel: viewModel) - .onDisappear { - component.container.unregister(type: AddNewContactViewModelImpl.self) - } - } -} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/AddNewContact/AddNewContactState.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/AddNewContact/AddNewContactState.swift deleted file mode 100644 index 2d1bac6c..00000000 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/AddNewContact/AddNewContactState.swift +++ /dev/null @@ -1,15 +0,0 @@ -import Foundation - -struct AddNewContactState { - enum AddContacFlowStep { - case getCode - case getInfo - case alreadyConnected - case confirmConnection - case error(DisplayError) - } - - struct Contact { - let text: String - } -} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/AddNewContact/AddNewContactView.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/AddNewContact/AddNewContactView.swift deleted file mode 100644 index 8279f030..00000000 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/AddNewContact/AddNewContactView.swift +++ /dev/null @@ -1,111 +0,0 @@ -import SwiftUI - -protocol AddNewContactViewModel: ObservableObject { - var flowStep: AddNewContactState.AddContacFlowStep { get } - var contactInfo: AddNewContactState.Contact? { get } - var loading: Bool { get } - var dismiss: Bool { get } - var dismissRoot: Bool { get } - var code: String { get set } - func getTokenInfo() - func addContact() -} - -struct AddNewContactView: View { - @StateObject var viewModel: ViewModel - @Environment(\.presentationMode) var presentationMode - @Environment(\.rootPresentationMode) var modalPresentation - - var body: some View { - VStack { - switch viewModel.flowStep { - case .getCode: - InsertCodeView( - textField: $viewModel.code, - loading: viewModel.loading - ) { - self.viewModel.getTokenInfo() - } cancelAction: { - self.presentationMode.wrappedValue.dismiss() - } - .commitDisablePreference() - .disabled(viewModel.loading) - case .getInfo: - ProgressView() - .progressViewStyle( - CircularProgressViewStyle() - ) - .onAppear(perform: { - viewModel.getTokenInfo() - }) - .frame(maxWidth: .infinity, maxHeight: 55) - .commitDisablePreference() - .disabled(true) - case .alreadyConnected: - if let contact = viewModel.contactInfo { - AlreadyConnectedView( - name: contact.text - ) { - self.presentationMode.wrappedValue.dismiss() - } - .commitDisablePreference() - .disabled(viewModel.loading) - } - - case .confirmConnection: - if let contact = viewModel.contactInfo { - ConfirmConnectionView( - name: contact.text, - loading: viewModel.loading - ) { - self.viewModel.addContact() - } cancelAction: { - self.presentationMode.wrappedValue.dismiss() - } - .commitDisablePreference() - .disabled(viewModel.loading) - } - case let .error(error): - ErrorDialogView( - error: .constant(error) - ) { - self.presentationMode.wrappedValue.dismiss() - } - } - } - .background(Color.white) - .clipShape(RoundedRectangle(cornerRadius: 10)) - .padding() - .animation(.default) - .onChange(of: viewModel.dismiss, perform: { value in - if value { - self.presentationMode.wrappedValue.dismiss() - } - }) - .onChange(of: viewModel.dismissRoot, perform: { value in - self.modalPresentation.wrappedValue = value - }) - } -} - -struct AddNewContactView_Previews: PreviewProvider { - static var previews: some View { - AddNewContactView(viewModel: MockViewModel()) - } -} - -private class MockViewModel: AddNewContactViewModel { - var contactInfo: AddNewContactState.Contact? - var flowStep: AddNewContactState.AddContacFlowStep = .getCode - var loading = false - var dismiss = false - var dismissRoot = false - var code = "" - func getTokenInfo() { - flowStep = .confirmConnection - } - - func addContact() { - dismiss = true - } -} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/AddNewContact/AddNewContactViewModel.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/AddNewContact/AddNewContactViewModel.swift deleted file mode 100644 index a9faf547..00000000 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/AddNewContact/AddNewContactViewModel.swift +++ /dev/null @@ -1,100 +0,0 @@ -import Combine -import Domain -import Foundation -import PrismAgent - -final class AddNewContactViewModelImpl: AddNewContactViewModel { - @Published var flowStep: AddNewContactState.AddContacFlowStep - @Published var code = "" - @Published var dismiss = false - @Published var dismissRoot = false - @Published var loading = false - @Published var contactInfo: AddNewContactState.Contact? - private let agent: PrismAgent - private var parsed: PrismAgent.InvitationType? - private var cancellables = Set() - - init( - token: String = "", - agent: PrismAgent - ) { - code = token - self.agent = agent - flowStep = token.isEmpty ? .getCode : .getInfo - if token.isEmpty { - // Due to not being able to paste on rosetta simulator from M1 - code = "https://domain.com/path?_oob=eyJpZCI6ImUzNzZlZGYyLWVmNmQtNDk4ZS1hMTk3LWMwZTI2MGQxNTA2OCIsInR5cGUiOiJodHRwczovL2RpZGNvbW0ub3JnL291dC1vZi1iYW5kLzIuMC9pbnZpdGF0aW9uIiwiZnJvbSI6ImRpZDpwZWVyOjIuRXo2TFNoQUFxY1ZlZXRQNmlrdHk1bXl5OFFweE5wVlk3NEF0YUZuVmFrOFRwYnRkSy5WejZNa2dlYUVWZ0FVSHoyQWczaUZLRDIxMjZTR0tERnpIS28zSEFxYmM4eExOM1paLlNleUowSWpvaVpHMGlMQ0p6SWpvaWFIUjBjRG92TDJodmMzUXVaRzlqYTJWeUxtbHVkR1Z5Ym1Gc09qZ3dPREF2Wkdsa1kyOXRiU0lzSW5JaU9sdGRMQ0poSWpwYkltUnBaR052YlcwdmRqSWlYWDAiLCJib2R5Ijp7ImdvYWxfY29kZSI6ImNvbm5lY3QiLCJnb2FsIjoiRXN0YWJsaXNoIGEgdHJ1c3QgY29ubmVjdGlvbiBiZXR3ZWVuIHR3byBwZWVycyIsImFjY2VwdCI6W119fQ==" - } - } - - func getTokenInfo() { - guard !loading else { return } - loading = true - Task { - do { - let parsed = try await agent.parseInvitation(str: code) - await MainActor.run { - switch parsed { - case let .onboardingPrism(onboarding): - self.contactInfo = .init(text: onboarding.from) - case let .onboardingDIDComm(invitation): - self.contactInfo = .init(text: invitation.from) - } - self.parsed = parsed - flowStep = .confirmConnection - loading = false - } - } catch { - self.flowStep = .error(DisplayErrorState(error: error)) - } - } - } - - func addContact() { - guard !code.isEmpty, !loading, let parsed else { return } - loading = true - Task { - do { - switch parsed { - case let .onboardingPrism(onboarding): - try await agent.acceptPrismInvitation(invitation: onboarding) - case let .onboardingDIDComm(invitation): - try await agent.acceptDIDCommInvitation(invitation: invitation) - } - await MainActor.run { - loading = false - dismiss = true - dismissRoot = true - } - } catch let error as MercuryError { - switch error { - case let .didcommError(msg, _): - print("Error: \(msg)") - default: - break - } - await MainActor.run { - self.flowStep = .error(DisplayErrorState(error: error)) - } - print("Error: \(error.errorDescription)") - - } catch let error as CommonError { - switch error { - case let .httpError(code, _): - print("HTTP Error: \(code)") - default: - break - } - await MainActor.run { - self.flowStep = .error(DisplayErrorState(error: error)) - } - print("Error: \(error.localizedDescription)") - } catch let error as LocalizedError { - await MainActor.run { - self.flowStep = .error(DisplayErrorState(error: error)) - print(error.errorDescription) - } - } - } - } -} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/AddNewContact/UI/AlreadyConnectedView.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/AddNewContact/UI/AlreadyConnectedView.swift deleted file mode 100644 index 06a12722..00000000 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/AddNewContact/UI/AlreadyConnectedView.swift +++ /dev/null @@ -1,39 +0,0 @@ -import SwiftUI - -struct AlreadyConnectedView: View { - let name: String - let doneAction: () -> Void - - var body: some View { - VStack(spacing: 20) { - HStack { - VStack(alignment: .leading, spacing: 5) { - Text("contacts_add_new_already_connected_caption".localize()) - .font(.footnote) - .foregroundColor(.gray) - Text(name) - .font(.title3) - .fontWeight(.heavy) - .foregroundColor(.black) - } - } - - Divider() - - AtalaButton { - self.doneAction() - } label: { - Text("ok".localize()) - } - } - .padding(24) - } -} - -struct AlreadyConnectedView_Previews: PreviewProvider { - static var previews: some View { - AlreadyConnectedView( - name: "Atala KYC" - ) {} - } -} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/AddNewContact/UI/ConfirmConnectionView.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/AddNewContact/UI/ConfirmConnectionView.swift deleted file mode 100644 index 46a368a7..00000000 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/AddNewContact/UI/ConfirmConnectionView.swift +++ /dev/null @@ -1,42 +0,0 @@ -import SwiftUI - -struct ConfirmConnectionView: View { - let name: String - let loading: Bool - let doneAction: () -> Void - let cancelAction: () -> Void - - var body: some View { - VStack(spacing: 20) { - HStack { - VStack(alignment: .leading, spacing: 5) { - Text("contacts_add_new_confirm_connection_caption".localize()) - .font(.footnote) - .foregroundColor(.gray) - Text(name) - .font(.title3) - .fontWeight(.heavy) - .foregroundColor(.black) - } - Spacer() - } - - Divider() - - HStack { - AtalaButton(configuration: .secondary) { - self.cancelAction() - } label: { - Text("cancel".localize()) - } - - AtalaButton(loading: loading) { - self.doneAction() - } label: { - Text("contacts_add_new_confirm_connection_confirm_bt".localize()) - } - } - } - .padding(24) - } -} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/AddNewContact/UI/InsertCodeView.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/AddNewContact/UI/InsertCodeView.swift deleted file mode 100644 index b7248e16..00000000 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/AddNewContact/UI/InsertCodeView.swift +++ /dev/null @@ -1,57 +0,0 @@ -import SwiftUI - -struct InsertCodeView: View { - @Binding var textField: String - let loading: Bool - let doneAction: () -> Void - let cancelAction: () -> Void - - var body: some View { - VStack(spacing: 35) { - VStack(spacing: 0) { - Image("img_qr_red") - Text("connections_enter_code".localize()) - .font(.title2) - .fontWeight(.heavy) - } - - TextField("", text: $textField) - .padding() - .roundedRectBorderWithText("connections_enter_code_placeholder".localize()) - - HStack { - AtalaButton( - configuration: .secondary, - action: { - self.cancelAction() - }, - label: { - Text("cancel".localize()) - } - ) - - AtalaButton( - loading: loading, - action: { - self.doneAction() - }, - label: { - Text("confirm".localize()) - } - ) - } - } - .padding(24) - } -} - -struct InsertCodeView_Previews: PreviewProvider { - static var previews: some View { - InsertCodeView( - textField: .constant(""), - loading: false, - doneAction: {}, - cancelAction: {} - ) - } -} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/CredentialDetail/CredentialDetailBuilder.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/CredentialDetail/CredentialDetailBuilder.swift deleted file mode 100644 index b9bb0ae3..00000000 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/CredentialDetail/CredentialDetailBuilder.swift +++ /dev/null @@ -1,21 +0,0 @@ -import Foundation -import PrismAgent -import SwiftUI - -struct CredentialDetailComponent { - let credentialId: String - let container: DIContainer -} - -struct CredentialDetailBuilder { - func build(component: CredentialDetailComponent) -> some View { - let viewModel = CredentialDetailViewModelImpl( - credentialId: component.credentialId, - agent: component.container.resolve(type: PrismAgent.self)! - ) - - return CredentialDetailNeoView( - viewModel: viewModel - ) - } -} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/CredentialDetail/CredentialDetailNeoView.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/CredentialDetail/CredentialDetailNeoView.swift deleted file mode 100644 index ace53e9c..00000000 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/CredentialDetail/CredentialDetailNeoView.swift +++ /dev/null @@ -1,43 +0,0 @@ -import SwiftUI - -protocol CredentialDetailViewModel: ObservableObject { - var schema: String { get } - var types: [String] { get } - var issued: String { get } - var error: Error? { get } - var dismiss: Bool { get } -} - -struct CredentialDetailNeoView< - ViewModel: CredentialDetailViewModel ->: View { - @StateObject var viewModel: ViewModel - @Environment(\.presentationMode) var presentationMode - - var body: some View { - ScrollView(.vertical, showsIndicators: true) { - VStack(spacing: 26) { - VStack(alignment: .leading) { - HStack { - Image("ico_time") - .accentColor(.gray) - Text(viewModel.issued) - .font(.body) - .foregroundColor(.gray) - } - .padding(.horizontal) - .frame(maxWidth: .infinity, minHeight: 40, alignment: .leading) - .background(Color.gray.opacity(0.3)) - .clipShape(RoundedRectangle(cornerRadius: 5)) - } - } - .padding() - } - .navigationBarTitleDisplayMode(.inline) - .onChange(of: viewModel.dismiss, perform: { value in - if value { - self.presentationMode.wrappedValue.dismiss() - } - }) - } -} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/CredentialDetail/CredentialDetailViewModel.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/CredentialDetail/CredentialDetailViewModel.swift deleted file mode 100644 index 54bf84f9..00000000 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/CredentialDetail/CredentialDetailViewModel.swift +++ /dev/null @@ -1,43 +0,0 @@ -import Combine -import Foundation -import PrismAgent - -final class CredentialDetailViewModelImpl: CredentialDetailViewModel { - @Published var schema = "" - @Published var types = [String]() - @Published var issued = "" - @Published var dismiss = false - @Published var error: Error? - - private let credentialId: String - private let agent: PrismAgent - private var cancellables = Set() - - init( - credentialId: String, - agent: PrismAgent - ) { - self.credentialId = credentialId - self.agent = agent - - bind() - } - - private func bind() { - agent.verifiableCredentials() - .map { [weak self] in - $0.first { $0.subject == self?.credentialId } - } - .replaceError(with: nil) - .sink { [weak self] in - guard - let credential = $0, - let schema = credential.properties["credentialSchema"] as? String - else { return } - self?.schema = schema - self?.types = [""] - self?.issued = "" - } - .store(in: &cancellables) - } -} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/CredentialsList/CredentialsListBuilder.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/CredentialsList/CredentialsListBuilder.swift deleted file mode 100644 index 242377f5..00000000 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/CredentialsList/CredentialsListBuilder.swift +++ /dev/null @@ -1,22 +0,0 @@ -import Foundation -import PrismAgent -import SwiftUI -import UIKit - -struct CredentialsListComponent { - let container: DIContainer -} - -struct CredentialsListBuilder { - func build(component: CredentialsListComponent) -> UIViewController { - let viewModel = CredentialsListViewModelImpl( - agent: component.container.resolve(type: PrismAgent.self)! - ) - let router = CredentialsListRouterImpl(container: component.container) - let view = CredentialsListView< - CredentialsListViewModelImpl, CredentialsListRouterImpl - >(router: router).environmentObject(viewModel) - - return UIHostingController(rootView: view) - } -} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/CredentialsList/CredentialsListRouter.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/CredentialsList/CredentialsListRouter.swift deleted file mode 100644 index d60c6ebe..00000000 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/CredentialsList/CredentialsListRouter.swift +++ /dev/null @@ -1,23 +0,0 @@ -import Foundation -import SwiftUI - -struct CredentialsListRouterImpl: CredentialsListRouter { - let container: DIContainer - - func routeToCredentialDetail(id: String) -> some View { - LazyView { - CredentialDetailBuilder().build(component: .init( - credentialId: id, - container: container - )) - } - } - - func routeToInsertToken() -> some View { - AddNewContactBuilder() - .build(component: .init( - container: container, - token: nil - )) - } -} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/CredentialsList/CredentialsListState.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/CredentialsList/CredentialsListState.swift deleted file mode 100644 index 5b42d7a6..00000000 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/CredentialsList/CredentialsListState.swift +++ /dev/null @@ -1,15 +0,0 @@ -import Foundation - -struct CredentialsListState { - struct Credential: Identifiable { - enum Icon { - case data(Data) - case name(String) - } - - let id: String - let icon: Icon - let title: String - let subtitle: String - } -} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/CredentialsList/CredentialsListView.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/CredentialsList/CredentialsListView.swift deleted file mode 100644 index e940d0dd..00000000 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/CredentialsList/CredentialsListView.swift +++ /dev/null @@ -1,199 +0,0 @@ -import SwiftUI - -protocol CredentialsListViewModel: ObservableObject { - var credentials: [CredentialsListState.Credential] { get } - var showEmptyList: Bool { get } -} - -protocol CredentialsListRouter { - associatedtype CredentialDetail: View - associatedtype AddContactV: View - - func routeToCredentialDetail(id: String) -> CredentialDetail - func routeToInsertToken() -> AddContactV -} - -struct CredentialsListView< - ViewModel: CredentialsListViewModel, - Router: CredentialsListRouter ->: View { - let router: Router - @State var presentAddNewContact = false - @EnvironmentObject var viewModel: ViewModel - - var body: some View { - NavigationView { - VStack(spacing: 0) { - Divider() - VStack(spacing: 16) { - if viewModel.showEmptyList { - VStack(spacing: 16) { - Spacer() - Image("img_notifications_tray") - Text("credentials_empty_title".localize()) - .font(.system( - size: 20, - weight: .semibold, - design: .default - )) - .fontWeight(.semibold) - .multilineTextAlignment(.center) - Text("credentials_empty_subtitle".localize()) - .font(.system( - size: 16, - weight: .regular, - design: .default - )) - .multilineTextAlignment(.center) - .foregroundColor(Color(.gray)) - Spacer() - } - .padding(.horizontal) - } else { - ScrollView(.vertical, showsIndicators: true) { - LazyVStack(alignment: .leading) { - ForEach(viewModel.credentials) { credential in - NavigationLink( - destination: router.routeToCredentialDetail(id: credential.id), - label: { - card(credential: credential) - } - ) - } - } - .padding() - .frame( - maxWidth: .infinity, - maxHeight: .infinity, - alignment: .topLeading - ) - } - .padding(.bottom) - } - } - .navigationBarItems( - leading: Text("credentials_nav_title".localize()).foregroundColor(.black), - trailing: navigationButtons - ) - .configureNavigationBar { - $0.barTintColor = .white - $0.setBackgroundImage(UIImage(), for: .default) - $0.shadowImage = UIImage() - $0.isTranslucent = true - $0.backgroundColor = .white - } - .navigationBarTitleDisplayMode(.inline) - } - .clearFullScreenCover( - isPresented: $presentAddNewContact - ) { - self.router.routeToInsertToken() - } - .background(Color.white) - } - } - - private var navigationButtons: some View { - HStack { - addTokenButton - } - } - - private var addTokenButton: some View { - Button( - action: { - self.presentAddNewContact = true - }, - label: { - Text("connections_add_new".localize()) - .font(.caption2) - .underline() - .foregroundColor(Color(.gray)) - } - ) - } - - private func card(credential: CredentialsListState.Credential) -> some View { - HStack(spacing: 16) { - credential.icon.image() - .resizable() - .frame(width: 40, height: 40) - .clipShape(RoundedRectangle(cornerRadius: 10)) - VStack(alignment: .leading, spacing: 2) { - Text(credential.title) - .bold() - .foregroundColor(.black) - Text(credential.subtitle) - .font(.caption) - .fontWeight(.light) - .foregroundColor(.black) - .multilineTextAlignment(.leading) - .padding(.leading, 1) - } - - Spacer() - Image(systemName: "chevron.forward") - .foregroundColor(.gray) - } - .frame(minHeight: 68) - .frame(maxWidth: .infinity) - .padding(.horizontal) - .background( - RoundedRectangle(cornerRadius: 10) - .fill(Color.white) - .shadow(color: Color(.gray), radius: 3) - ) - } -} - -private extension CredentialsListState.Credential.Icon { - func image() -> Image { - switch self { -// case .id, .nationalId: -// return Image("icon-credential-id") -// case .universityDegree: -// return Image("icon-credential-university") -// case .proofOfEmployment: -// return Image("icon-credential-employment") -// case .insurance: -// return Image("icon-credential-insurance") -// case .passport: -// return Image("icon_passport") -// case .payId: -// return Image("icon_pay_id") - case let .name(name): - return Image(name) - case let .data(data): - guard let uiImage = UIImage(data: data) else { - return Image("ico_placeholder_credential") - } - return Image(uiImage: uiImage) - } - } -} -// -//struct CredentialsListView_Previews: PreviewProvider { -// static var previews: some View { -// CredentialsListView(router: MockRouter()) -// .environmentObject(MockViewModel()) -// } -//} -// -//private class MockViewModel: CredentialsListViewModel { -// var showEmptyList = false -// var searchString = "" -// var credentials: [CredentialsListState.Credential] = [ -// .init( -// id: "", -// icon: .id, -// title: "Credential", -// subtitle: "Contact" -// ) -// ] -//} -// -//private struct MockRouter: CredentialsListRouter { -// func routeToCredentialDetail(id: String) -> some View { -// Text("") -// } -//} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/CredentialsList/CredentialsListViewModel.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/CredentialsList/CredentialsListViewModel.swift deleted file mode 100644 index 7f08624d..00000000 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/CredentialsList/CredentialsListViewModel.swift +++ /dev/null @@ -1,47 +0,0 @@ -import Combine -import Domain -import Foundation -import PrismAgent - -final class CredentialsListViewModelImpl: CredentialsListViewModel { - @Published var credentials = [CredentialsListState.Credential]() - @Published var showEmptyList = false - - private let agent: PrismAgent - private var cancellables = Set() - - init(agent: PrismAgent) { - self.agent = agent - - bind() - } - - private func bind() { - agent.verifiableCredentials() - .map { - $0.map { mapCredentialType($0) }.sorted { $0.id < $1.id } - } - .replaceError(with: []) - .receive(on: DispatchQueue.main) - .assign(to: &$credentials) - - $credentials - .map { $0.isEmpty } - .assign(to: &$showEmptyList) - - agent.handleReceivedMessagesEvents() - .sink { _ in } receiveValue: { _ in} - .store(in: &cancellables) - } -} - -private func mapCredentialType( - _ credential: Credential -) -> CredentialsListState.Credential { - .init( - id: credential.id, - icon: .name(""), - title: credential.claims.sorted { $0.key < $1.key }.first?.getValueAsString() ?? "", - subtitle: credential.claims.sorted { $0.key < $1.key }.last?.getValueAsString() ?? "" - ) -} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Dashboard/DashboardBuilder.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Dashboard/DashboardBuilder.swift deleted file mode 100644 index 71bebc64..00000000 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Dashboard/DashboardBuilder.swift +++ /dev/null @@ -1,23 +0,0 @@ -import PrismAgent -import SwiftUI -import UIKit - -struct DashboardComponent: ComponentContainer { - let container: DIContainer -} - -struct DashboardBuilder: Builder { - func build(component: DashboardComponent) -> some View { - let viewModel = DashboardViewModelImpl(agent: component.container.resolve(type: PrismAgent.self)!) - - let view = DashboardView( - router: DashboardRouterImpl( - container: component.container, - agent: component.container.resolve(type: PrismAgent.self)! - ), - viewModel: viewModel - ) - - return view - } -} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Dashboard/DashboardRouter.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Dashboard/DashboardRouter.swift deleted file mode 100644 index 9ed02859..00000000 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Dashboard/DashboardRouter.swift +++ /dev/null @@ -1,91 +0,0 @@ -import Combine -import Foundation -import SwiftUI -import UIKit -import PrismAgent - -struct DashboardRouterImpl: DashboardRouter { - let container: DIContainer - let agent: PrismAgent - - func tabViews(qrCodeBinding: Binding) -> [UIViewController] { - [ - makeCredentialsVC(), - makeContactsVC(qrCodeBinding: qrCodeBinding), - makeHomeVC(), - makeServicesVC(), - makeSettingsVC() - ] - } - - func routeToPresentProofOfRequest(request: RequestPresentation) -> some View { - ProofOfRequestBuilder().build(component: .init( - proofOfRequest: request, - container: container - )) - } - - func routeToQRCodeReader() -> some View { - QRCodeScannerBuilder().build(component: .init(container: container)) - } - - private func makeCredentialsVC() -> UIViewController { - let viewController = CredentialsListBuilder().build(component: .init(container: container)) - - viewController.tabBarItem = UITabBarItem( - title: "tab_credentials".localize(), - image: UIImage(named: "tab_credentials"), - tag: 0 - ) - return viewController - } - - private func makeContactsVC(qrCodeBinding: Binding) -> UIViewController { -// let viewController = ContactsListBuilder().buildVC(component: .init( -// container: container, -// contactsRepository: IntegrationStore.shared.contactsRepository, -// appConfiguration: AppConfiguration(), -// presentQRCode: qrCodeBinding -// )) - - let viewController = UIViewController() - - viewController.tabBarItem = UITabBarItem( - title: "tab_contacts".localize(), - image: UIImage(named: "tab_contacts"), - tag: 1 - ) - return viewController - } - - private func makeHomeVC() -> UIViewController { - let viewController = HomeBuilder().buildVC(component: .init( - container: container - )) - - viewController.tabBarItem = UITabBarItem(title: nil, image: nil, tag: 2) - return viewController - } - - private func makeServicesVC() -> UIViewController { - let viewController = UIViewController() - - viewController.tabBarItem = UITabBarItem( - title: "tab_services".localize(), - image: UIImage(named: "tab_services"), - tag: 3 - ) - return viewController - } - - private func makeSettingsVC() -> UIViewController { - let viewController = UIViewController() - - viewController.tabBarItem = UITabBarItem( - title: "tab_settings".localize(), - image: UIImage(named: "tab_settings"), - tag: 4 - ) - return viewController - } -} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Dashboard/DashboardTabViewController.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Dashboard/DashboardTabViewController.swift deleted file mode 100644 index 90b0381d..00000000 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Dashboard/DashboardTabViewController.swift +++ /dev/null @@ -1,174 +0,0 @@ -import Combine -import PrismAgent -import SwiftUI -import UIKit - -protocol DashboardViewModel: ObservableObject { - var toasty: FancyToast? { get set } - var selectedIndex: Int { get set } - var proofOfRequest: RequestPresentation? { get set } - - func middleButtonPressed() - func start() -} - -final class DashboardTabViewController: UITabBarController, UITabBarControllerDelegate { - private var presentQRCodeScanner: Binding - private let viewModel: ViewModel - private let middleIndex = 2 - private lazy var middleButton = makeScanButton() - private var cancellables = Set() - - override var selectedIndex: Int { - didSet { - setupButton(selectedIndex: selectedIndex) - } - } - - init(viewModel: ViewModel, presentQRCodeScanner: Binding) { - self.viewModel = viewModel - self.presentQRCodeScanner = presentQRCodeScanner - super.init(nibName: nil, bundle: nil) - delegate = self - } - - @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override func viewDidLoad() { - super.viewDidLoad() - view.addSubview(middleButton) - tabBar.barTintColor = UIColor.white - tabBar.tintColor = UIColor.cyan - tabBar.unselectedItemTintColor = UIColor.gray - tabBar.backgroundColor = UIColor.white - tabBar.addDropShadow(radius: 20, opacity: 0.14, offset: CGSize(width: 0, height: 4), color: UIColor.darkGray) - - UITabBarItem - .appearance() - .setTitleTextAttributes( - [NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 10)], - for: .normal - ) - UITabBarItem - .appearance() - .setTitleTextAttributes( - [NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 10)], - for: .normal - ) - } - - override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - selectedIndex = middleIndex - _ = middleButton - } - - override func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - } - - private func setupButton(selectedIndex: Int) { - middleButton - .setImage( - getButtonImage(isTabSelected: selectedIndex == middleIndex), - for: .normal - ) - } - - private func makeScanButton() -> UIButton { - let button = UIButton() - button.setImage(getButtonImage(isTabSelected: selectedIndex == middleIndex), for: .normal) - let margin = UIApplication.shared.windows[0].safeAreaInsets.bottom - button.frame = CGRect( - origin: CGPoint( - x: (view.frame.width / 2) - 24, - y: view.frame.height - margin - tabBar.height - 15 - ), - size: .init(width: 48, height: 48) - ) - button.addTarget(self, action: #selector(selectMiddleViewController(sender:)), for: .touchUpInside) - return button - } - - private func getButtonImage(isTabSelected: Bool) -> UIImage { - let name = isTabSelected ? "tab_central_on" : "tab_central_off" - guard let image = UIImage(named: name) else { - fatalError("Image missing") - } - return image - } - - @objc private func selectMiddleViewController( - sender: UIButton - ) { - viewModel.middleButtonPressed() - presentQRCodeScanner.wrappedValue = true - } - - func tabBarController( - _ tabBarController: UITabBarController, - shouldSelect viewController: UIViewController - ) -> Bool { - true - } - - func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) { - viewControllers? - .firstIndex(of: viewController) - .map { setupButton(selectedIndex: $0) } - if viewModel.selectedIndex != selectedIndex { - viewModel.selectedIndex = selectedIndex - } - } -} - -extension UIView { - func addDropShadow( - radius: CGFloat = 4.0, - opacity: Float = 0.4, - offset: CGSize = CGSize(width: 0.0, height: 1.0), - color: UIColor = UIColor.darkGray - ) { - layer.shadowColor = color.cgColor - layer.shadowOpacity = opacity - layer.shadowOffset = offset - layer.shadowRadius = radius - layer.masksToBounds = false - } -} - -extension UIView { - /// Size of view. - var size: CGSize { - get { - return self.frame.size - } - set { - self.width = newValue.width - self.height = newValue.height - } - } - - /// Width of view. - var width: CGFloat { - get { - return self.frame.size.width - } - set { - self.frame.size.width = newValue - } - } - - /// Height of view. - var height: CGFloat { - get { - return self.frame.size.height - } - set { - self.frame.size.height = newValue - } - } -} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Dashboard/DashboardView.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Dashboard/DashboardView.swift deleted file mode 100644 index a5ceada4..00000000 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Dashboard/DashboardView.swift +++ /dev/null @@ -1,82 +0,0 @@ -import SwiftUI -import PrismAgent - -protocol DashboardRouter { - associatedtype QRCodeScannerV: View - associatedtype PresentProofV: View - - func tabViews(qrCodeBinding: Binding) -> [UIViewController] - func routeToQRCodeReader() -> QRCodeScannerV - func routeToPresentProofOfRequest(request: RequestPresentation) -> PresentProofV -} - -struct DashboardView: View { - let router: Router - @StateObject var viewModel: ViewModel - @State var presentProofOfRequest = false - @State var presentQRCodeScanner = false - - var body: some View { - DashboardRepresentableView( - router: router, - viewModel: viewModel, - presentQRCodeScanner: $presentQRCodeScanner - ) - .clearFullScreenCover(isPresented: $presentProofOfRequest, animated: true) { - if let proofOfRequest = self.viewModel.proofOfRequest { - self.router.routeToPresentProofOfRequest(request: proofOfRequest) - } - } - .onChange(of: viewModel.proofOfRequest) { newValue in - if newValue != nil { - self.presentProofOfRequest = true - } - } - .edgesIgnoringSafeArea(.all) - .fullScreenCover( - isPresented: self.$presentQRCodeScanner, - onDismiss: {}, - content: { - self.router.routeToQRCodeReader() - } - ) - .navigationBarHidden(true) - .toastView(toast: $viewModel.toasty) - .onAppear { - self.viewModel.start() - } - } -} - -private struct DashboardRepresentableView< - ViewModel: DashboardViewModel, Router: DashboardRouter ->: UIViewControllerRepresentable { - final class Coordinator { - let viewController: DashboardTabViewController - - init(viewController: DashboardTabViewController) { - self.viewController = viewController - } - } - - let router: Router - @StateObject var viewModel: ViewModel - @Binding var presentQRCodeScanner: Bool - - func makeCoordinator() -> Coordinator { - let viewController = DashboardTabViewController( - viewModel: viewModel, - presentQRCodeScanner: $presentQRCodeScanner - ) - viewController.viewControllers = router.tabViews(qrCodeBinding: $presentQRCodeScanner) - return Coordinator(viewController: viewController) - } - - func makeUIViewController(context: Context) -> DashboardTabViewController { - context.coordinator.viewController - } - - func updateUIViewController(_ uiViewController: DashboardTabViewController, context: Context) { - context.coordinator.viewController.selectedIndex = viewModel.selectedIndex - } -} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Dashboard/DashboardViewModelImpl.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Dashboard/DashboardViewModelImpl.swift deleted file mode 100644 index 4feed262..00000000 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Dashboard/DashboardViewModelImpl.swift +++ /dev/null @@ -1,64 +0,0 @@ -import Combine -import Foundation -import PrismAgent - -final class DashboardViewModelImpl: DashboardViewModel { - @Published var toasty: FancyToast? - // The initial module being presented in the TAB should be the center one - // with index 2. - @Published var selectedIndex: Int = 2 - @Published var proofOfRequest: RequestPresentation? - private var agent: PrismAgent - private var cancellables = Set() - - init(agent: PrismAgent) { - self.agent = agent - bind() - } - - func middleButtonPressed() { - selectedIndex = 2 - } - - func start() { - Task { [weak self] in - do { - try await self?.agent.start() - await MainActor.run { [weak self] in - guard let self else { return } - self.toasty = .init( - type: .info, - title: "Achieved mediation", - message: "RoutingDID: \(self.agent.mediatorRoutingDID!)" - ) - } - } catch { - await MainActor.run { [weak self] in - self?.toasty = .init( - type: .error, - title: "Could not start agent", - message: error.localizedDescription - ) - } - } - } - } - - private func bind() { - agent.requestedPresentations - .map { - $0.filter { !$0.1 } - } - .map { - $0.sorted { $0.0.id < $1.0.id } - } - .map { - $0.first - } - .receive(on: DispatchQueue.main) - .sink { - self.proofOfRequest = $0?.0 - } - .store(in: &cancellables) - } -} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Home/HomeBuilder.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Home/HomeBuilder.swift deleted file mode 100644 index ff6644ad..00000000 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Home/HomeBuilder.swift +++ /dev/null @@ -1,16 +0,0 @@ -import SwiftUI -import PrismAgent - -struct HomeComponent { - let container: DIContainer -} - -struct HomeBuilder: Builder { - func build(component: HomeComponent) -> some View { - let viewModel = HomeViewModelImpl( - agent: component.container.resolve(type: PrismAgent.self)! - ) - - return HomeView(viewModel: viewModel, router: HomeRouterImpl()) - } -} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Home/HomeRouter.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Home/HomeRouter.swift deleted file mode 100644 index e42537b9..00000000 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Home/HomeRouter.swift +++ /dev/null @@ -1,24 +0,0 @@ -import SwiftUI - -struct HomeRouterImpl: HomeRouter { - -// func routeToVerifyCredential() { -// let storyboard = UIStoryboard(name: "VerifyIdTutorial", bundle: nil) -// let viewController = storyboard.instantiateViewController(withIdentifier: "VerifyIdTutorialNavigation") -// if let segueableViewController = viewController as? SegueableScreen { -// segueableViewController.configScreenFromSegue(params: nil) -// } -// viewController.modalPresentationStyle = .fullScreen -// -// weak var navVC = NavigationUtil.findNavigationControllerFromRoot() -// viewController.modalPresentationStyle = .fullScreen -// NavigationUtil.findTopViewController()?.present( -// viewController, -// animated: true, -// completion: { -// guard let navigation = navVC else { return } -// NavigationUtil.popToRootView(navigationVC: navigation, animated: false) -// } -// ) -// } -} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Home/HomeState.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Home/HomeState.swift deleted file mode 100644 index 5ea87a23..00000000 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Home/HomeState.swift +++ /dev/null @@ -1,27 +0,0 @@ -import Foundation - -struct HomeState { - enum Notifications { - case empty - case new(Int) - } - - struct Profile { - let profileImage: Data - let fullName: String - } - - struct ActivityLog { - enum ActivityType { - case connected - case received - case shared - } - - let activityType: ActivityType - let infoText: String - let name: String - let dateFormatter: RelativeDateTimeFormatter - let date: Date - } -} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Home/HomeView.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Home/HomeView.swift deleted file mode 100644 index 9341fde3..00000000 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Home/HomeView.swift +++ /dev/null @@ -1,116 +0,0 @@ -import SwiftUI - -protocol HomeViewModel: ObservableObject { - var profile: HomeState.Profile { get } - var lastActivities: [HomeState.ActivityLog] { get } -} - -protocol HomeRouter { -// associatedtype ProfileV: View -// -// func routeToProfile() -> ProfileV -// func routeToVerifyCredential() -} - -struct HomeView: View { - @StateObject var viewModel: ViewModel - let router: Router - - @State var presentProfile = false - @State var presentNotifications = false - @State var presentMoreInfo = false - @State var presentShareApp = false - - var body: some View { - NavigationView { - ScrollView(.vertical) { - VStack(spacing: 20) { - VStack { - ProfileHeaderView( - profile: viewModel.profile - ) - Spacer() - VStack(spacing: 22) { - if viewModel.lastActivities.isEmpty { - Image("img_empty_activity") - } else { - ActivityListView(activities: viewModel.lastActivities) - } - } - .padding(.horizontal) - } - } - } - .background(Color.white) - .edgesIgnoringSafeArea(.top) - .navigationBarHidden(true) - } - } -} - -struct HomeView_Previews: PreviewProvider { - static var previews: some View { - HomeView( - viewModel: MockViewModel(), - router: MockRouter() - ) - } -} - -private class MockViewModel: HomeViewModel { - var profile: HomeState.Profile = .init( - profileImage: Data(), - fullName: "John Doe" - ) - var lastActivities: [HomeState.ActivityLog] = [ - .init( - activityType: .connected, - infoText: "Info", - name: "Name", - dateFormatter: RelativeDateTimeFormatter(), - date: Date() - ), - .init( - activityType: .shared, - infoText: "Info", - name: "Name", - dateFormatter: RelativeDateTimeFormatter(), - date: Date() - ), - .init( - activityType: .received, - infoText: "Info", - name: "Name", - dateFormatter: RelativeDateTimeFormatter(), - date: Date() - ) - ] -} - -private struct MockRouter: HomeRouter { - func routeToProfile() -> some View { - Text("View not ready") - } - - func routeToActivitys() -> some View { - Text("View not ready") - } - - func routeToMoreInfo() -> some View { - Text("View not ready") - } - - func routeToShareApp() -> some View { - Text("View not ready") - } - - func routeToNotifications() -> some View { - Text("View not ready") - } - - func routeToVerifyCredential() {} - - func routeToPayID() {} - - func routeToPayIDDetail() {} -} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Home/HomeViewModel.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Home/HomeViewModel.swift deleted file mode 100644 index e97f507c..00000000 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Home/HomeViewModel.swift +++ /dev/null @@ -1,25 +0,0 @@ -import Combine -import Foundation -import PrismAgent - -final class HomeViewModelImpl: HomeViewModel { - @Published var profile = HomeState.Profile( - profileImage: Data(), - fullName: "Olivia Rhye" - ) - @Published var lastActivities = [HomeState.ActivityLog]() - - private let agent: PrismAgent - - init( - agent: PrismAgent - ) { - self.agent = agent - - bind() - } - - private func bind() { - lastActivities = [] - } -} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Home/UI/ActivityListView.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Home/UI/ActivityListView.swift deleted file mode 100644 index 788fcc57..00000000 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Home/UI/ActivityListView.swift +++ /dev/null @@ -1,104 +0,0 @@ -import Combine -import SwiftUI - -struct ActivityListView: View { - let activities: [HomeState.ActivityLog] - private let timer = Timer - .publish(every: 1, on: .main, in: .common) - .autoconnect() - - var body: some View { - VStack { - ForEach(activities.zipped(), id: \.0) { _, activity in - card(activity: activity) - } - } - } - - func card(activity: HomeState.ActivityLog) -> some View { - HStack(alignment: .top, spacing: 16) { - activity.activityType.image - .resizable() - .frame(width: 40, height: 40) - .clipShape(RoundedRectangle(cornerRadius: 10)) - VStack(alignment: .leading) { - Text(activity.infoText) - .font(.system(size: 10, weight: .bold)) - .foregroundColor(Color(.gray)) - Text(activity.name) - .font(.system(size: 13, weight: .semibold)) - .foregroundColor(.black) - .kerning(0.4) - } - Spacer() - HStack { - Image("ico_time") - AutoUpdatingDateText(state: activity, timer: timer) - } - } - .frame(height: 68) - .frame(maxWidth: .infinity) - .padding(.horizontal) - .background( - RoundedRectangle(cornerRadius: 10) - .fill(Color.white) - .shadow(color: Color(.gray), radius: 3) - ) - } -} - -private struct AutoUpdatingDateText: View { - let state: HomeState.ActivityLog - let timer: Publishers.Autoconnect - @State private var text: String = "" - - var body: some View { - Text(text) - .font(.system(size: 10, weight: .bold)) - .foregroundColor(Color(.gray)) - .onReceive(timer) { _ in - self.text = state.dateFormatter.localizedString(for: state.date, relativeTo: Date()) - } - } -} - -private extension HomeState.ActivityLog.ActivityType { - var image: Image { - switch self { - case .connected: - return Image("icon_connected") - case .shared: - return Image("icon_shared") - case .received: - return Image("icon_received") - } - } -} - -struct ActivityListView_Previews: PreviewProvider { - static var previews: some View { - ActivityListView(activities: [ - .init( - activityType: .connected, - infoText: "Info", - name: "Name", - dateFormatter: RelativeDateTimeFormatter(), - date: Date() - ), - .init( - activityType: .shared, - infoText: "Info", - name: "Name", - dateFormatter: RelativeDateTimeFormatter(), - date: Date() - ), - .init( - activityType: .received, - infoText: "Info", - name: "Name", - dateFormatter: RelativeDateTimeFormatter(), - date: Date() - ) - ]) - } -} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Main/MainView.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Main/MainView.swift deleted file mode 100644 index 05a75221..00000000 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Main/MainView.swift +++ /dev/null @@ -1,69 +0,0 @@ -import SwiftUI - -protocol MainViewModel: ObservableObject { - var routeToDashboard: Bool { get set } - var didString: String { get set } - var oobString: String { get set } - var toast: FancyToast? { get set } - func startWithMediatorDID() - func startWithMediatorOOB() -} - -protocol MainViewRouter { - associatedtype DashboardV: View - - func routeToDashboard() -> DashboardV -} - -struct MainView: View { - @StateObject var viewModel: ViewModel - let router: Router - - var body: some View { - VStack(spacing: 20) { - TextField(text: $viewModel.oobString) { - Text("Mediator OOB") - } - Button("Start with mediator OOB") { - Task { - viewModel.startWithMediatorOOB() - } - } - .padding() - .frame(maxWidth: .infinity) - .background(Color.red) - .tint(.white) - .clipShape(Capsule(style: .continuous)) - TextField(text: $viewModel.didString) { - Text("Mediator DID") - } - Button("Start with mediator DID") { - Task { - viewModel.startWithMediatorDID() - } - } - .padding() - .frame(maxWidth: .infinity) - .background(Color.red) - .tint(.white) - .clipShape(Capsule(style: .continuous)) - EmptyNavigationLink(isActive: $viewModel.routeToDashboard) { - LazyView { - self.router.routeToDashboard() - } - } - } - .padding() - } -} - -//struct MainView_Previews: PreviewProvider { -// struct Router: MainViewRouter { -// func routeToDashboard() -> some View { -// Text("Dashboard") -// } -// } -// static var previews: some View { -// MainView(viewModel: MainViewModelImpl(router: Router), router: Router()) -// } -//} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Main/MainViewModel.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Main/MainViewModel.swift deleted file mode 100644 index d6169946..00000000 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Main/MainViewModel.swift +++ /dev/null @@ -1,41 +0,0 @@ -import Combine -import Domain -import Foundation -import PrismAgent - -final class MainViewModelImpl: MainViewModel { - @Published var didString: String = "did:peer:2.Ez6LSo3ResPPWyCGRohY1xS5qYWUVkPMusLknMVT9x8FNAAnk.Vz6Mkhs7twmSqp2DvgVXDTWVD8S8x9Q7eLg6J8EaXoyHhnWmh.SeyJpZCI6Im5ldy1pZCIsInQiOiJkbSIsInMiOiJodHRwOi8vcm9vdHNpZC1tZWRpYXRvcjo4MDAwIiwiYSI6WyJkaWRjb21tL3YyIl19" - @Published var oobString: String = "https://mediator.rootsid.cloud?_oob=eyJ0eXBlIjoiaHR0cHM6Ly9kaWRjb21tLm9yZy9vdXQtb2YtYmFuZC8yLjAvaW52aXRhdGlvbiIsImlkIjoiNzk0Mjc4MzctY2MwNi00ODUzLWJiMzktNjg2ZWFjM2U2YjlhIiwiZnJvbSI6ImRpZDpwZWVyOjIuRXo2TFNtczU1NVloRnRobjFXVjhjaURCcFptODZoSzl0cDgzV29qSlVteFBHazFoWi5WejZNa21kQmpNeUI0VFM1VWJiUXc1NHN6bTh5dk1NZjFmdEdWMnNRVllBeGFlV2hFLlNleUpwWkNJNkltNWxkeTFwWkNJc0luUWlPaUprYlNJc0luTWlPaUpvZEhSd2N6b3ZMMjFsWkdsaGRHOXlMbkp2YjNSemFXUXVZMnh2ZFdRaUxDSmhJanBiSW1ScFpHTnZiVzB2ZGpJaVhYMCIsImJvZHkiOnsiZ29hbF9jb2RlIjoicmVxdWVzdC1tZWRpYXRlIiwiZ29hbCI6IlJlcXVlc3RNZWRpYXRlIiwibGFiZWwiOiJNZWRpYXRvciIsImFjY2VwdCI6WyJkaWRjb21tL3YyIl19fQ" - @Published var toast: FancyToast? - @Published var routeToDashboard = false - private let router: MainViewRouterImpl - private var cancellables = [AnyCancellable]() - - init(router: MainViewRouterImpl) { - self.router = router - } - - func startWithMediatorDID() { - do { - let did = try DID(string: didString) - self.router.didOnUse = did - self.routeToDashboard = true - } catch { - self.didString = "" - self.toast = .init(type: .error, title: "Invalid DID", message: didString) - } - } - - func startWithMediatorOOB() { - do { - let invitation = try PrismAgent( - mediatorDID: DID(method: "peer", methodId: "123") - ).parseOOBInvitation(url: oobString) - self.router.didOnUse = try DID(string: invitation.from) - self.routeToDashboard = true - } catch { - self.didString = "" - self.toast = .init(type: .error, title: "Invalid OOB", message: oobString) - } - } -} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Main/MainViewRouter.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Main/MainViewRouter.swift deleted file mode 100644 index 6027fe2a..00000000 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Main/MainViewRouter.swift +++ /dev/null @@ -1,20 +0,0 @@ -import SwiftUI -import Domain -import PrismAgent - -final class MainViewRouterImpl: MainViewRouter { - let defaultDID = try! DID(string: "did:peer:2.Ez6LScc4S6tTSf5PnB7tWAna8Ee2aL7z2nRgo6aCHQwLds3m4.Vz6MktCyutFBcZcAWBnE2shqqUQDyRdnvcwqMTPqWsGHMnHyT.SeyJpZCI6Im5ldy1pZCIsInQiOiJkbSIsInMiOiJodHRwOi8vcm9vdHNpZC1tZWRpYXRvcjo4MDAwIiwiYSI6WyJkaWRjb21tL3YyIl19") - - var didOnUse: DID - - init() { - self.didOnUse = defaultDID - } - - func routeToDashboard() -> some View { - let agent = PrismAgent(mediatorDID: didOnUse) - let container = DIContainerImpl() - container.register(type: PrismAgent.self, component: agent) - return DashboardBuilder().build(component: .init(container: container)) - } -} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Profile/ProfileHeaderView.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Profile/ProfileHeaderView.swift deleted file mode 100644 index c74c5d05..00000000 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/Profile/ProfileHeaderView.swift +++ /dev/null @@ -1,63 +0,0 @@ -import SwiftUI - -struct ProfileHeaderView: View { - let profile: HomeState.Profile - - var body: some View { - ZStack(alignment: .bottom) { - VStack { - VStack(alignment: .leading, spacing: 20) { - profileView - } - .padding(.horizontal, 35) - .padding(.top, 60) - .padding(.bottom, 40) - .frame(maxWidth: .infinity) - .background(Image("bg_home_profile").resizable()) - .clipShape(SpecificRoundedRect( - radius: 10, - corners: [.bottomLeft, .bottomRight] - )) - } - } - } - - private var profileView: some View { - VStack { - HStack(spacing: 20) { - let image = - UIImage(data: profile.profileImage).map { - Image(uiImage: $0) - } ?? Image("ico_placeholder_user") - - image - .resizable() - .frame(width: 98, height: 98) - VStack(alignment: .leading, spacing: 4) { - VStack(alignment: .leading) { - Text("home_profile_hey" - .localize()) - .font(.system(size: 14, weight: .bold)) - .foregroundColor(.white) - Text(profile.fullName) - .font(.system(size: 16, weight: .bold)) - .foregroundColor(.white) - } - } - } - .frame(maxWidth: .infinity, alignment: .leading) - } - } -} - -struct ProfileHeaderView_Previews: PreviewProvider { - - static var previews: some View { - ProfileHeaderView( - profile: .init( - profileImage: Data(), - fullName: "Jonh Doe" - ) - ) - } -} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/ProofOfRequest/ProofOfRequestBuilder.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/ProofOfRequest/ProofOfRequestBuilder.swift deleted file mode 100644 index e3d10455..00000000 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/ProofOfRequest/ProofOfRequestBuilder.swift +++ /dev/null @@ -1,24 +0,0 @@ -import Combine -import Foundation -import PrismAgent -import SwiftUI - -struct ProofOfRequestComponent: ComponentContainer { - let proofOfRequest: RequestPresentation - let container: DIContainer -} - -struct ProofOfRequestBuilder: Builder { - func build(component: ProofOfRequestComponent) -> some View { - let viewModel = getViewModel(component: component) { - ProofOfRequestViewModelImpl( - proofOfRequest: component.proofOfRequest, - agent: component.container.resolve(type: PrismAgent.self)! - ) - } - return ProofOfRequestView(viewModel: viewModel) - .onDisappear { - component.container.unregister(type: ProofOfRequestViewModelImpl.self) - } - } -} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/ProofOfRequest/ProofOfRequestState.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/ProofOfRequest/ProofOfRequestState.swift deleted file mode 100644 index 68d03553..00000000 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/ProofOfRequest/ProofOfRequestState.swift +++ /dev/null @@ -1,27 +0,0 @@ -import Foundation -struct ProofOfRequestState { - enum FlowStep { - case loading - case shareCredentials - case confirm - case error(DisplayError) - } - - enum RequestedCredentials { - case idCredential - case universityDegree - case proofOfEmployment - case insurance - case custom(String) - } - - struct Contact { - let text: String -// let credentialsRequested: [RequestedCredentials] - } - - struct Credential { - let id: String - let text: String - } -} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/ProofOfRequest/ProofOfRequestView.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/ProofOfRequest/ProofOfRequestView.swift deleted file mode 100644 index 5d7d6ab0..00000000 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/ProofOfRequest/ProofOfRequestView.swift +++ /dev/null @@ -1,99 +0,0 @@ -import SwiftUI - -protocol ProofOfRequestViewModel: ProofOfRequestCheckViewModel { - var flowStep: ProofOfRequestState.FlowStep { get } - var dismiss: Bool { get } - func viewDidAppear() - func confirmDismiss() -} - -struct ProofOfRequestView: View { - @StateObject var viewModel: ViewModel - @Environment(\.presentationMode) var presentationMode - - var body: some View { - VStack { - switch viewModel.flowStep { - case .loading: - ProgressView() - .progressViewStyle( - CircularProgressViewStyle() - ) - .onAppear(perform: { - self.viewModel.viewDidAppear() - }) - .frame(maxWidth: .infinity, maxHeight: 55) - .commitDisablePreference() - .disabled(true) - case .shareCredentials: - VStack(spacing: 16) { - Text("Proof Request Received") - .bold() - .foregroundColor(.black) - Text("Credentials Available") - .foregroundColor(Color.black) - if let firstCredential = viewModel.credential.first { - Text(firstCredential.text) - .foregroundColor(Color.black) - } - - Divider() - - HStack { - AtalaButton(configuration: .secondary) { - self.presentationMode.wrappedValue.dismiss() - } label: { - Text("cancel".localize()) - } - - AtalaButton(loading: viewModel.loading) { - self.viewModel.share() - } label: { - Text("proof_request_share".localize()) - } - } - } - .padding(24) - .environmentObject(viewModel) - .commitDisablePreference() - .disabled(viewModel.loading) - case .confirm: - VStack(spacing: 20) { - Image("img_success") - VStack(spacing: 5) { - Text("credentials_detail_share_success_title".localize()) - .font(.caption) - .fontWeight(.light) - Text("credentials_detail_share_success".localize()) - .fontWeight(.heavy) - .foregroundColor(.black) - } - - Divider() - - AtalaButton { - self.viewModel.confirmDismiss() - } label: { - Text("ok".localize()) - } - } - .padding(24) - .commitDisablePreference() - .disabled(viewModel.loading) - case let .error(error): - ErrorDialogView(error: .constant(error)) { - presentationMode.wrappedValue.dismiss() - } - } - } - .background(Color.white) - .clipShape(RoundedRectangle(cornerRadius: 10)) - .padding() - .animation(.default) - .onChange(of: viewModel.dismiss, perform: { value in - if value { - self.presentationMode.wrappedValue.dismiss() - } - }) - } -} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/ProofOfRequest/ProofOfRequestViewModel.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/ProofOfRequest/ProofOfRequestViewModel.swift deleted file mode 100644 index 85355c30..00000000 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/ProofOfRequest/ProofOfRequestViewModel.swift +++ /dev/null @@ -1,116 +0,0 @@ -import Combine -import Domain -import Foundation -import PrismAgent - -final class ProofOfRequestViewModelImpl: ProofOfRequestViewModel { - @Published var contact: ProofOfRequestState.Contact = .init(text: "") - @Published var flowStep: ProofOfRequestState.FlowStep = .loading - @Published var credential = [ProofOfRequestState.Credential]() - @Published var checks = [Bool]() - @Published var loading = false - @Published var dismiss = false - - private let proofOfRequest: RequestPresentation - private let agent: PrismAgent - private var selectedCredential: Credential? - private var cancellables = Set() - - init( - proofOfRequest: RequestPresentation, - agent: PrismAgent - ) { - self.proofOfRequest = proofOfRequest - self.agent = agent - } - - func viewDidAppear() { - contact = .init(text: "Proof request received") - agent.verifiableCredentials() - .first { !$0.isEmpty } - .receive(on: DispatchQueue.main) - .map { credentials -> [Credential] in - self.selectedCredential = credentials.first - return credentials - } - .map { credentials -> [ProofOfRequestState.Credential] in - credentials.map { - ProofOfRequestState.Credential( - id: $0.id, - text: $0.claims.sorted { $0.key < $1.key }.first?.getValueAsString() ?? "" - ) - } - } - .replaceError(with: []) - .map { credentials -> [ProofOfRequestState.Credential] in - self.flowStep = .shareCredentials - return credentials - } - .assign(to: &$credential) - } - - - func sendPresentation() { - guard !loading else { return } - loading = true - } - - func share() { - guard let selectedCredential else { return } - loading = true - Task { - do { - try await self.presentCredentialProof( - request: self.proofOfRequest, - credential: selectedCredential - ) - guard - let index = agent.requestedPresentations - .value - .firstIndex(where: { $0.0.id == proofOfRequest.id }) - else { return } - agent.requestedPresentations.value[index] = (proofOfRequest, true) - await MainActor.run { - self.loading = false - self.dismiss = true - } - } catch { - await MainActor.run { - self.loading = false - } - } - } - } - - func presentCredentialProof( - request: RequestPresentation, - credential: Credential - ) async throws { - guard let jwtBase64 = credential.id.data(using: .utf8)?.base64UrlEncodedString() else { - throw UnknownError.somethingWentWrongError( - customMessage: "Could not decode JWT Credential", - underlyingErrors: nil - ) - } - let presentation = Presentation( - body: .init(goalCode: request.body.goalCode, comment: request.body.comment), - attachments: [try .build( - payload: AttachmentBase64(base64: jwtBase64), - mediaType: "prism/jwt" - )], - thid: request.id, - from: request.to, - to: request.from - ) - _ = try await agent.sendMessage(message: presentation.makeMessage()) - } - - func confirmDismiss() { - guard - let index = agent.requestedPresentations - .value - .firstIndex(where: { $0.0.id == proofOfRequest.id }) - else { return } - agent.requestedPresentations.value[index] = (proofOfRequest, true) - } -} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/ProofOfRequest/UI/ProofOfRequestCheckView.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/ProofOfRequest/UI/ProofOfRequestCheckView.swift deleted file mode 100644 index e4dbc5be..00000000 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/ProofOfRequest/UI/ProofOfRequestCheckView.swift +++ /dev/null @@ -1,88 +0,0 @@ -import SwiftUI - -protocol ProofOfRequestCheckViewModel: ObservableObject { - var contact: ProofOfRequestState.Contact { get } - var credential: [ProofOfRequestState.Credential] { get } - var checks: [Bool] { get set } - var loading: Bool { get } - - func share() -} - -struct ProofOfRequestCheckView: View { - @EnvironmentObject var viewModel: ViewModel - @Environment(\.presentationMode) var presentationMode - - var body: some View { - VStack(alignment: .leading, spacing: 20) { - HStack(spacing: 21) { - Text(viewModel.contact.text) - .fontWeight(.heavy) - .foregroundColor(.black) - Spacer() - } - VStack(alignment: .leading, spacing: 16) { - Text("proof_request_description_first".localize()) - .foregroundColor(.black) - VStack(alignment: .leading, spacing: 8) { - Text("") -// ForEach(Array(viewModel.contact.credentialsRequested.enumerated()), id: \.offset) { -// switch $0.element { -// case .idCredential: -// Text("proof_request_id_credential".localize()) -// .bold() -// case .universityDegree: -// Text("proof_request_university_credential".localize()) -// .bold() -// case .proofOfEmployment: -// Text("proof_request_employment_credential".localize()) -// .bold() -// case .insurance: -// Text("proof_request_insurance_credential".localize()) -// .bold() -// case let .custom(text): -// Text(text) -// .bold() -// } -// } - } - } - - Divider() - - VStack(alignment: .leading, spacing: 8) { - Text("proof_request_select_credentials".localize()) - .font(.caption) - .fontWeight(.light) - .foregroundColor(.black) - VStack(spacing: 12) { - ForEach(viewModel.credential.zipped(), id: \.0) { idx, credential in - HStack { - Text(credential.text) - .bold() - Spacer() - CheckButton(isSelected: $viewModel.checks[idx]) - } - } - } - } - - Divider() - - HStack { - AtalaButton(configuration: .secondary) { - self.presentationMode.wrappedValue.dismiss() - } label: { - Text("cancel".localize()) - } - - AtalaButton(loading: viewModel.loading) { - self.viewModel.share() - } label: { - Text("proof_request_share".localize()) - } - } - } - .padding(24) - } -} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/QRCodeScanner/QRCodeScannerBuilder.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/QRCodeScanner/QRCodeScannerBuilder.swift deleted file mode 100644 index ead06640..00000000 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/QRCodeScanner/QRCodeScannerBuilder.swift +++ /dev/null @@ -1,19 +0,0 @@ -import SwiftUI -import PrismAgent - -struct QRCodeScannerComponent: ComponentContainer { - let container: DIContainer -} - -struct QRCodeScannerBuilder: Builder { - func build(component: QRCodeScannerComponent) -> some View { - let viewModel = getViewModel(component: component) { - QRCodeScannerViewModelImpl(agent: component.container.resolve(type: PrismAgent.self)!) - } - let router = QRCodeScannerRouterImpl(container: component.container) - return QRCodeScannerView(router: router, viewModel: viewModel) - .onDisappear { - component.container.unregister(type: QRCodeScannerViewModelImpl.self) - } - } -} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/QRCodeScanner/QRCodeScannerRouter.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/QRCodeScanner/QRCodeScannerRouter.swift deleted file mode 100644 index eeb47137..00000000 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/QRCodeScanner/QRCodeScannerRouter.swift +++ /dev/null @@ -1,13 +0,0 @@ -import SwiftUI - -struct QRCodeScannerRouterImpl: QRCodeScannerRouter { - let container: DIContainer - - func routeToAddNewContact(token: String?) -> some View { - AddNewContactBuilder().build( - component: .init( - container: container, - token: token - )) - } -} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/QRCodeScanner/QRCodeScannerView.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/QRCodeScanner/QRCodeScannerView.swift deleted file mode 100644 index 752879ba..00000000 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/QRCodeScanner/QRCodeScannerView.swift +++ /dev/null @@ -1,101 +0,0 @@ -import SwiftUI - -protocol QRCodeScannerViewModel: ObservableObject { - var toasty: FancyToast? { get set } - var token: String? { get } - var showInfo: Bool { get set } - var dismiss: Bool { get set } - func qrCodeFound(_ qrCode: String) - func cameraError(_ error: Error) -} - -protocol QRCodeScannerRouter { - associatedtype AddNewContact: View - func routeToAddNewContact(token: String?) -> AddNewContact -} - -struct QRCodeScannerView< - ViewModel: QRCodeScannerViewModel, - Router: QRCodeScannerRouter ->: View { - let router: Router - @StateObject var viewModel: ViewModel - @Environment(\.presentationMode) var presentationMode - - var body: some View { - ZStack { - ZStack { - QRScannerView { - self.viewModel.qrCodeFound($0) - } onCameraError: { - self.viewModel.cameraError($0) - } - LinearGradient( - gradient: Gradient(colors: [ - .black.opacity(0.6), - .clear, - .clear, - .black.opacity(0.6) - ]), - startPoint: .top, - endPoint: .bottom - ) - } - .ignoresSafeArea() - - VStack { - HStack { - Button { - self.viewModel.dismiss = true - } label: { - Image(systemName: "xmark.circle.fill") - .resizable() - .foregroundColor(.white) - .frame(width: 40, height: 40) - } - Spacer() - } - Spacer() - } - .padding(.horizontal, 26) - .padding(.vertical) - } - .preferredColorScheme(.dark) - .clearFullScreenCover( - isPresented: $viewModel.showInfo - ) { - LazyView { - router.routeToAddNewContact(token: viewModel.token) - .environment(\.rootPresentationMode, $viewModel.dismiss) - } - } - .onChange(of: viewModel.dismiss, perform: { value in - if value { - self.presentationMode.wrappedValue.dismiss() - print("Dismissed") - } - }) - } -} - -struct QRCodeScannerView_Previews: PreviewProvider { - static var previews: some View { - QRCodeScannerView(router: MockRouter(), viewModel: MockViewModel()) - } -} - -private class MockViewModel: QRCodeScannerViewModel { - var toasty: FancyToast? = nil - var token: String? = "" - var showInfo = false - var dismiss = false - func qrCodeFound(_ qrCode: String) {} - func cameraError(_ error: Error) {} -} - -private struct MockRouter: QRCodeScannerRouter { - func routeToAddNewContact(token: String?) -> some View { - Text("token") - .foregroundColor(.white) - } -} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/QRCodeScanner/QRCodeScannerViewModel.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/QRCodeScanner/QRCodeScannerViewModel.swift deleted file mode 100644 index 1c071a3c..00000000 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/QRCodeScanner/QRCodeScannerViewModel.swift +++ /dev/null @@ -1,53 +0,0 @@ -import Combine -import Foundation -import PrismAgent - -final class QRCodeScannerViewModelImpl: QRCodeScannerViewModel { - @Published var toasty: FancyToast? - @Published var token: String? - @Published var showInfo = false - @Published var dismiss = false - var parsing = false - - private let agent: PrismAgent - - init(token: String? = nil, showInfo: Bool = false, dismiss: Bool = false, agent: PrismAgent) { - self.token = token - self.showInfo = showInfo - self.dismiss = dismiss - self.agent = agent - } - - func qrCodeFound(_ qrCode: String) { - guard !showInfo, !dismiss, !parsing else { return } - parsing = true - Task { [weak self] in - defer { - parsing = false - } - do { - _ = try await agent.parseInvitation(str: qrCode) - await MainActor.run { [weak self] in - self?.token = qrCode - self?.showInfo = true - } - } catch { - await MainActor.run { [weak self] in - self?.toasty = .init( - type: .error, - title: "Something went wrong accepting invitation", - message: error.localizedDescription - ) - } - } - } - } - - func cameraError(_ error: Error) { - toasty = .init( - type: .error, - title: "Could not start agent", - message: error.localizedDescription - ) - } -} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/QRCodeScanner/UI/CameraViewController.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/QRCodeScanner/UI/CameraViewController.swift deleted file mode 100644 index 8c1b8762..00000000 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/QRCodeScanner/UI/CameraViewController.swift +++ /dev/null @@ -1,146 +0,0 @@ -import AVFoundation -import Combine -import UIKit - -protocol CameraViewDelegate: AVCaptureMetadataOutputObjectsDelegate { - func didFail(error: Error) -} - -final class CameraViewController: UIViewController { - enum CameraError: Error { - case invalidInput - case invalidOutput - } - - let codeTypes: [AVMetadataObject.ObjectType] - let videoCaptureDevice = AVCaptureDevice.default(for: .video) - var captureSession: AVCaptureSession! - var previewLayer: AVCaptureVideoPreviewLayer! - weak var delegate: CameraViewDelegate? - var cancellables = Set() - - init(codeTypes: [AVMetadataObject.ObjectType], delegate: CameraViewDelegate? = nil) { - self.codeTypes = codeTypes - self.delegate = delegate - super.init(nibName: nil, bundle: nil) - } - - @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override func viewDidLoad() { - super.viewDidLoad() - - NotificationCenter.default - .publisher(for: Notification.Name("UIDeviceOrientationDidChangeNotification")) - .sink { [weak self] _ in - self?.updateOrientation() - } - .store(in: &cancellables) - - view.backgroundColor = UIColor.black - captureSession = AVCaptureSession() - - guard let videoCaptureDevice = videoCaptureDevice else { - return - } - - guard let videoInput = try? AVCaptureDeviceInput(device: videoCaptureDevice) else { return } - - if captureSession.canAddInput(videoInput) { - captureSession.addInput(videoInput) - } else { - delegate?.didFail(error: CameraError.invalidInput) - return - } - - let metadataOutput = AVCaptureMetadataOutput() - - if captureSession.canAddOutput(metadataOutput) { - captureSession.addOutput(metadataOutput) - - metadataOutput.setMetadataObjectsDelegate(delegate, queue: DispatchQueue.main) - metadataOutput.metadataObjectTypes = codeTypes - } else { - delegate?.didFail(error: CameraError.invalidOutput) - return - } - - let gesture = UITapGestureRecognizer(target: self, action: #selector(tap(sender:))) - view.addGestureRecognizer(gesture) - } - - override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - - if previewLayer == nil { - previewLayer = AVCaptureVideoPreviewLayer(session: captureSession) - } - previewLayer.frame = view.layer.bounds - previewLayer.videoGravity = .resizeAspectFill - view.layer.addSublayer(previewLayer) - - if captureSession?.isRunning == false { - DispatchQueue(label: "com.prism.qrscanner", qos: .background).async { [weak self] in - self?.captureSession.startRunning() - } - } - } - - override func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - updateOrientation() - } - - override func viewDidDisappear(_ animated: Bool) { - super.viewDidDisappear(animated) - - if captureSession?.isRunning == true { - captureSession.stopRunning() - } - } - - override func viewWillLayoutSubviews() { - previewLayer?.frame = view.layer.bounds - } - - @objc func tap(sender: UITapGestureRecognizer) { - guard let touchView = sender.view else { return } - if sender.state == .ended { - let screenSize = touchView.bounds.size - let touchPoint = sender.location(in: touchView) - let xPoint = touchPoint.y / screenSize.height - let yPoint = 1.0 - touchPoint.x / screenSize.width - let focusPoint = CGPoint(x: xPoint, y: yPoint) - - focus(point: focusPoint) - } - } - - private func updateOrientation() { - guard let connection = captureSession.connections.last, connection.isVideoOrientationSupported else { return } - view.window?.windowScene.map { - connection.videoOrientation = AVCaptureVideoOrientation( - rawValue: $0.interfaceOrientation.rawValue - ) ?? .portrait - } - } - - private func focus(point: CGPoint) { - guard let device = videoCaptureDevice else { return } - - do { - try device.lockForConfiguration() - } catch { - return - } - - device.focusPointOfInterest = point - device.focusMode = .continuousAutoFocus - device.exposurePointOfInterest = point - device.exposureMode = AVCaptureDevice.ExposureMode.continuousAutoExposure - device.unlockForConfiguration() - } -} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/QRCodeScanner/UI/QRScannerView.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/QRCodeScanner/UI/QRScannerView.swift deleted file mode 100644 index 73d74ae8..00000000 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/QRCodeScanner/UI/QRScannerView.swift +++ /dev/null @@ -1,53 +0,0 @@ -import AVFoundation -import SwiftUI - -struct QRScannerView: UIViewControllerRepresentable { - class Coordinator: NSObject, CameraViewDelegate { - let onQRCodeFound: (String) -> Void - let onError: (Error) -> Void - - init( - onQRCodeFound: @escaping (String) -> Void, - onError: @escaping (Error) -> Void - ) { - self.onQRCodeFound = onQRCodeFound - self.onError = onError - } - - func metadataOutput( - _ output: AVCaptureMetadataOutput, - didOutput metadataObjects: [AVMetadataObject], - from connection: AVCaptureConnection - ) { - guard - let metadataObject = metadataObjects.first, - let readableObject = metadataObject as? AVMetadataMachineReadableCodeObject, - let stringValue = readableObject.stringValue - else { return } - onQRCodeFound(stringValue) - } - - func reset() {} - - func didFail(error: Error) { - onError(error) - } - } - - let onQRCodeFound: (String) -> Void - let onCameraError: (Error) -> Void - - func makeCoordinator() -> Coordinator { - return Coordinator( - onQRCodeFound: onQRCodeFound, - onError: onCameraError - ) - } - - func makeUIViewController(context: Context) -> CameraViewController { - let viewController = CameraViewController(codeTypes: [.qr], delegate: context.coordinator) - return viewController - } - - func updateUIViewController(_ uiViewController: CameraViewController, context: Context) {} -} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/UIHelper/AtalaButton.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/UIHelper/AtalaButton.swift deleted file mode 100644 index 9ebe2346..00000000 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/UIHelper/AtalaButton.swift +++ /dev/null @@ -1,61 +0,0 @@ -import SwiftUI - -struct AtalaButton: View { - enum Configuration { - case primary - case secondary - } - - let configuration: Configuration - let loading: Bool - let action: () -> Void - @ViewBuilder var label: () -> Label - - init( - configuration: Configuration = .primary, - loading: Bool = false, - action: @escaping () -> Void, - @ViewBuilder label: @escaping () -> Label - ) { - self.configuration = configuration - self.loading = loading - self.action = action - self.label = label - } - - @Environment(\.isEnabled) var isEnabled: Bool - - var body: some View { - let button = Button(action: action, label: { - HStack(spacing: 6) { - label() - if loading { - ProgressView() - .progressViewStyle( - CircularProgressViewStyle( - tint: .white - ) - ) - } - } - }) - if configuration == .primary { - button - .primeButtonConfiguration() - .environment(\.isLoading, loading) - } else { - button - .secondaryButtonConfiguration() - .environment(\.isLoading, loading) - } - } -} - -struct AtalaButton_Previews: PreviewProvider { - static var previews: some View { - AtalaButton(loading: true, action: {}, label: { - Text("Something") - }) - .disabled(true) - } -} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/UIHelper/Button+Configuration.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/UIHelper/Button+Configuration.swift deleted file mode 100644 index 0f55bb92..00000000 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/UIHelper/Button+Configuration.swift +++ /dev/null @@ -1,67 +0,0 @@ -import SwiftUI - -struct AtalaPrimeButtonViewModifier: ViewModifier { - @Environment(\.isEnabled) var isEnabled: Bool - @Environment(\.isLoading) var isLoading: Bool - - func body(content: Content) -> some View { - content - .padding(.horizontal) - .frame(maxWidth: .infinity) - .frame(height: 45) - .background((isEnabled || isLoading) ? Color(.red) : Color(.lightGray)) - .foregroundColor((isEnabled || isLoading) ? .white : Color(.gray)) - .accentColor((isEnabled || isLoading) ? .white : Color(.gray)) - .clipShape(Capsule()) - } -} - -struct AtalaSecondaryButtonViewModifier: ViewModifier { - func body(content: Content) -> some View { - content - .padding(.horizontal) - .frame(maxWidth: .infinity) - .frame(height: 45) - .background(Color.white) - .foregroundColor(Color(.red)) - .accentColor(Color(.red)) - .overlay( - Capsule() - .stroke(Color(.red), lineWidth: 3) - ) - } -} - -struct AtalaPrimaryButtonConfiguration: ButtonStyle { - func makeBody(configuration: Configuration) -> some View { - configuration.label - .modifier(AtalaPrimeButtonViewModifier()) - } -} - -struct AtalaSecondaryButtonConfiguration: ButtonStyle { - func makeBody(configuration: Configuration) -> some View { - configuration.label - .modifier(AtalaSecondaryButtonViewModifier()) - } -} - -extension View { - func primeButtonModifier() -> some View { - modifier(AtalaPrimeButtonViewModifier()) - } - - func secondaryButtonModifier() -> some View { - modifier(AtalaSecondaryButtonViewModifier()) - } -} - -extension Button { - func primeButtonConfiguration() -> some View { - buttonStyle(AtalaPrimaryButtonConfiguration()) - } - - func secondaryButtonConfiguration() -> some View { - buttonStyle(AtalaSecondaryButtonConfiguration()) - } -} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/UIHelper/CheckButton.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/UIHelper/CheckButton.swift deleted file mode 100644 index ee9e2091..00000000 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/UIHelper/CheckButton.swift +++ /dev/null @@ -1,22 +0,0 @@ -import SwiftUI - -struct CheckButton: View { - @Binding var isSelected: Bool - - var body: some View { - Button(action: { - withAnimation { - self.isSelected = !isSelected - } - }, label: { - if isSelected { - Image("ico_check_on") - .resizable() - } else { - Image("ico_check_off") - .resizable() - } - }) - .frame(width: 40, height: 40) - } -} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/UIHelper/ClearFullCoverModifier.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/UIHelper/ClearFullCoverModifier.swift deleted file mode 100644 index a59a3266..00000000 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/UIHelper/ClearFullCoverModifier.swift +++ /dev/null @@ -1,149 +0,0 @@ -import Combine -import SwiftUI - -private struct ClearFullCoverModifier: ViewModifier { - let animated: Bool - @Binding var isPresented: Bool - @ViewBuilder var presenting: () -> Presenting - @State var contentDisabled = false - - init( - animated: Bool, - isPresented: Binding, - @ViewBuilder presenting: @escaping () -> Presenting - ) { - self.animated = animated - _isPresented = isPresented - self.presenting = presenting - } - - func body(content: Content) -> some View { - content - .fullScreenCover(isPresented: $isPresented) { - ZStack { - presenting() - .disablePreference($contentDisabled) - .onTapGesture { - // Funny "hack" so the dismissal tap on the background view - // doesnt affect the presented. - // Just having this clean tapGesture works - } - } - .ignoresSafeArea(.container) - .frame(maxWidth: .infinity, maxHeight: .infinity) - .background( - BackgroundClearView(isPresented: $isPresented, isDisabled: contentDisabled) - ) - } - } -} - -extension View { - func clearFullScreenCover( - isPresented: Binding, - animated: Bool = true, - @ViewBuilder presenting: @escaping () -> Content - ) -> some View { - modifier(ClearFullCoverModifier( - animated: animated, - isPresented: isPresented, - presenting: presenting - )) - } -} - -private struct BackgroundClearView: UIViewRepresentable { - let isPresented: Binding - let isDisabled: Bool - - @Environment(\.isEnabled) var isEnabled - - class ViewCoordinator { - let view: BackgroundClearUIView - init(view: BackgroundClearUIView) { - self.view = view - } - } - - func makeCoordinator() -> ViewCoordinator { - ViewCoordinator(view: BackgroundClearUIView(action: { - self.isPresented.wrappedValue = false - })) - } - - func makeUIView(context: Context) -> BackgroundClearUIView { - return context.coordinator.view - } - - func updateUIView(_ uiView: BackgroundClearUIView, context: Context) { - uiView.gesture?.isEnabled = !isDisabled - } -} - -private class BackgroundClearUIView: UIView { - let action: () -> Void - var gesture: UITapGestureRecognizer? - var animating = false - var cancellables = Set() - var didAppear = false - - init(action: @escaping () -> Void) { - self.action = action - super.init(frame: .zero) - gesture = UITapGestureRecognizer(target: self, action: #selector(didTouchBackground(_:))) - } - - @available(*, unavailable) - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override func didMoveToWindow() { - super.didMoveToWindow() - superview?.superview?.backgroundColor = .clear - if !UIView.areAnimationsEnabled { - superview? - .superview? - .superview? - .backgroundColor = .init(red: 0, green: 0, blue: 0, alpha: 0.6) - } - - superview?.superview?.layer.publisher(for: \.position) - .receive(on: DispatchQueue.main) - .sink(receiveValue: { [weak self] _ in - guard - let self = self, - let animation = self.superview?.superview?.layer.animation(forKey: "position") - else { return } - - if let gesture = self.gesture, gesture.view == nil { - self.superview?.superview?.superview?.addGestureRecognizer(gesture) - self.superview?.superview?.superview?.isUserInteractionEnabled = true - } - - UIView.animate(withDuration: animation.duration) { [weak self] in - guard let self = self else { return } - self.animating = true - if self.didAppear { - self.superview? - .superview? - .superview? - .backgroundColor = .init(red: 0, green: 0, blue: 0, alpha: 0.0) - } else { - self.superview? - .superview? - .superview? - .backgroundColor = .init(red: 0, green: 0, blue: 0, alpha: 0.6) - } - } completion: { [weak self] _ in - self?.didAppear = true - self?.animating = false - } - }) - .store(in: &cancellables) - } - - @objc func didTouchBackground(_ sender: Any?) { - action() - } -} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/UIHelper/DisablePreferenceKey.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/UIHelper/DisablePreferenceKey.swift deleted file mode 100644 index b532f5c6..00000000 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/UIHelper/DisablePreferenceKey.swift +++ /dev/null @@ -1,35 +0,0 @@ -import SwiftUI - -struct DisablePreferenceKey: PreferenceKey { - static let defaultValue = false - - static func reduce(value: inout Bool, nextValue: () -> Bool) { - let next = nextValue() - if next != value { - value = next - } - } -} - -private struct CommitDisablePreferenceModifier: ViewModifier { - @Environment(\.isEnabled) var isEnabled - - func body(content: Content) -> some View { - content - .preference(key: DisablePreferenceKey.self, value: isEnabled) - } -} - -extension View { - func disablePreference(_ toBinder: Binding) -> some View { - onPreferenceChange(DisablePreferenceKey.self, perform: { value in - DispatchQueue.main.async { - toBinder.wrappedValue = !value - } - }) - } - - func commitDisablePreference() -> some View { - modifier(CommitDisablePreferenceModifier()) - } -} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/UIHelper/DisplayErrorState.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/UIHelper/DisplayErrorState.swift deleted file mode 100644 index 5cd89361..00000000 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/UIHelper/DisplayErrorState.swift +++ /dev/null @@ -1,11 +0,0 @@ -import Foundation - -struct DisplayErrorState: DisplayError { - let message: String - let debugMessage: String? - - init(error: Error) { - message = "default_error_message".localize() - debugMessage = error.localizedDescription - } -} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/UIHelper/ErrorDialogView.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/UIHelper/ErrorDialogView.swift deleted file mode 100644 index 379371b8..00000000 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/UIHelper/ErrorDialogView.swift +++ /dev/null @@ -1,77 +0,0 @@ -import SwiftUI - -protocol DisplayError: Error { - var message: String { get } - var debugMessage: String? { get } -} - -struct ErrorDialogView: View { - // The idea in here is to be able to nil the error - // once it is presented. It seems more streamlined. - // But open to discussion. - @Binding var error: DisplayError? - private let errorMessage: String - private let debugMessage: String? - private let action: () -> Void - - init( - error: Binding, - action: @escaping () -> Void - ) { - _error = error - errorMessage = error.wrappedValue?.message ?? "" - debugMessage = error.wrappedValue?.debugMessage - self.action = action - } - - var body: some View { - VStack(spacing: 30) { - VStack(spacing: 8) { - Image("img_verifyId_error") - Text("error".localize()) - .font(.system(size: 18, weight: .bold)) - .foregroundColor(.black) - Text(errorMessage) - .font(.system(size: 16, weight: .regular)) - .foregroundColor(.gray) - if let debugError = debugMessage { - Text("DEBUG:\n\(debugError)") - .font(.system(size: 16, weight: .regular)) - .multilineTextAlignment(.leading) - } - } - AtalaButton { - self.error = nil - self.action() - } label: { - Text("accept".localize()) - } - } - .padding(24) - .background(Color.white) - .clipShape(RoundedRectangle(cornerRadius: 10)) - .padding() - } -} - -private struct MockError: DisplayError { - var message: String = "Something went wrong." - var debugMessage: String? -} - -struct ErrorDialogView_Previews: PreviewProvider { - static var previews: some View { - ErrorDialogView(error: .constant(MockError())) {} - } -} - -extension View { - func showErrorDialog(error: Binding) -> some View { - clearFullScreenCover( - isPresented: .init(get: { - error.wrappedValue != nil - }, set: { _ in })) { - ErrorDialogView(error: error) {} - } - } -} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/UIHelper/IsLoadingEnvironmentValue.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/UIHelper/IsLoadingEnvironmentValue.swift deleted file mode 100644 index df716490..00000000 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/UIHelper/IsLoadingEnvironmentValue.swift +++ /dev/null @@ -1,12 +0,0 @@ -import SwiftUI - -private struct LoadingKey: EnvironmentKey { - static let defaultValue = false -} - -extension EnvironmentValues { - var isLoading: Bool { - get { self[LoadingKey.self] } - set { self[LoadingKey.self] = newValue } - } -} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/UIHelper/LoadingModifier.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/UIHelper/LoadingModifier.swift deleted file mode 100644 index 5abfdf66..00000000 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/UIHelper/LoadingModifier.swift +++ /dev/null @@ -1,40 +0,0 @@ -import SwiftUI - -private struct LoadingModifier: ViewModifier { - @Binding var enabled: Bool - - func body(content: Content) -> some View { - content - .clearFullScreenCover(isPresented: $enabled) { - ProgressView("loading_title".localize()) - .progressViewStyle(CircularProgressViewStyle()) - .frame(alignment: .center) - .padding() - .background( - Color(.gray) - .opacity(0.8) - .blur(radius: 2.0, opaque: true) - .clipShape(RoundedRectangle(cornerRadius: 20)) - ) - .onAppear { - UIView.setAnimationsEnabled(true) - } - .onDisappear { - UIView.setAnimationsEnabled(true) - } - } - } -} - -extension View { - func addLoading(enabled: Bool) -> some View { - modifier(LoadingModifier( - enabled: .init( - get: { - if enabled {} - return enabled - }, set: { _ in } - ) - )) - } -} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/UIHelper/NavigationTypeUtilModifiers.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/UIHelper/NavigationTypeUtilModifiers.swift deleted file mode 100644 index 5c740dba..00000000 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/UIHelper/NavigationTypeUtilModifiers.swift +++ /dev/null @@ -1,37 +0,0 @@ -import SwiftUI - -private struct NavigationControllerIntrospect: UIViewControllerRepresentable { - var configure: (UINavigationController) -> Void = { _ in } - - func makeUIViewController( - context: UIViewControllerRepresentableContext - ) -> UIViewController { - UIViewController() - } - - func updateUIViewController( - _ uiViewController: UIViewController, - context: UIViewControllerRepresentableContext - ) { - guard - let navigationContoller = uiViewController.navigationController - else { return } - configure(navigationContoller) - } -} - -extension View { - func configureNavigationBar(_ configure: @escaping (UINavigationBar) -> Void) -> some View { - modifier(NavigationConfigurationViewModifier(configure: configure)) - } -} - -struct NavigationConfigurationViewModifier: ViewModifier { - let configure: (UINavigationBar) -> Void - - func body(content: Content) -> some View { - content.background(NavigationControllerIntrospect(configure: { - configure($0.navigationBar) - })) - } -} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/UIHelper/RoundedRectWithBorderText.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/UIHelper/RoundedRectWithBorderText.swift deleted file mode 100644 index 92fab7e9..00000000 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/UIHelper/RoundedRectWithBorderText.swift +++ /dev/null @@ -1,39 +0,0 @@ -import SwiftUI - -private struct RoundedRectBorderWithText: ViewModifier { - let text: String - let borderColor: Color - - func body(content: Content) -> some View { - content - .background( - border.overlay( - Text(text) - .font(.footnote) - .bold() - .foregroundColor(.gray) - .background(Color(.white)) - .padding(.leading, 16) - .padding(.top, -7), - alignment: .topLeading - ) - ) - } - - var border: some View { - RoundedRectangle(cornerRadius: 10) - .strokeBorder( - borderColor, - lineWidth: 1 - ) - } -} - -extension View { - func roundedRectBorderWithText( - _ text: String, - borderColor: Color = Color(.gray).opacity(0.7) - ) -> some View { - modifier(RoundedRectBorderWithText(text: text, borderColor: borderColor)) - } -} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/UIHelper/SpecificRoundedRect.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/UIHelper/SpecificRoundedRect.swift deleted file mode 100644 index 92fa8984..00000000 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo/UIHelper/SpecificRoundedRect.swift +++ /dev/null @@ -1,15 +0,0 @@ -import SwiftUI - -struct SpecificRoundedRect: Shape { - let radius: CGFloat - let corners: UIRectCorner - - func path(in rect: CGRect) -> Path { - let path = UIBezierPath( - roundedRect: rect, - byRoundingCorners: corners, - cornerRadii: CGSize(width: radius, height: radius) - ) - return Path(path.cgPath) - } -} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Credentials/CredentialDetail/CredentialDetailView.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Credentials/CredentialDetail/CredentialDetailView.swift new file mode 100644 index 00000000..f6ea7b98 --- /dev/null +++ b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Credentials/CredentialDetail/CredentialDetailView.swift @@ -0,0 +1,65 @@ +import SwiftUI + +protocol CredentialDetailViewModel: ObservableObject { + var credential: CredentialDetailViewState { get } +} + +struct CredentialDetailView: View { + + @StateObject var viewModel: ViewModel + + var body: some View { + VStack(alignment: .leading, spacing: 16) { + HStack { + Text("Issuer:") + .font(.title2) + Text(viewModel.credential.issuer) + .lineLimit(nil) + } + + if let id = viewModel.credential.credentialDefinitionId { + HStack { + Text("Credential Definition ID:") + .font(.title2) + Text(id) + .lineLimit(nil) + } + } + if let id = viewModel.credential.schemaId { + HStack { + Text("Schema ID:") + .font(.title2) + Text(id) + .lineLimit(nil) + } + } + Text("Claims") + .font(.title2) + VStack(spacing: 8) { + ForEach(viewModel.credential.claims.sorted(by: >), id: \.key) { key, value in + Text("\(key) - \(value)") + } + } + .padding(.leading) + + Spacer() + } + .padding() + } +} + +private class MockViewModel: CredentialDetailViewModel { + var credential: CredentialDetailViewState = .init( + issuer: "did:test:adasdasd", + claims: [ + "test1": "test", + "test2": "test" + ], + credentialDefinitionId: "testId1", + schemaId: "testId2" + ) +} + +#Preview { + CredentialDetailView(viewModel: MockViewModel()) +} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Credentials/CredentialDetail/CredentialDetailViewModel.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Credentials/CredentialDetail/CredentialDetailViewModel.swift new file mode 100644 index 00000000..0e77f2f9 --- /dev/null +++ b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Credentials/CredentialDetail/CredentialDetailViewModel.swift @@ -0,0 +1,54 @@ +import Combine +import Domain +import Foundation +import PrismAgent + +final class CredentialDetailViewModelImpl: CredentialDetailViewModel { + @Published var credential = CredentialDetailViewState( + issuer: "", + claims: [:], + credentialDefinitionId: nil, + schemaId: nil + ) + + private let agent: PrismAgent + private let credentialId: String + + init(agent: PrismAgent, credentialId: String) { + self.agent = agent + self.credentialId = credentialId + bind() + } + + private func bind() { + let credentialId = self.credentialId + return self.agent + .verifiableCredentials() + .map { + if let credential = $0.first(where: { $0.id == credentialId }) { + return CredentialDetailViewState( + issuer: credential.issuer, + claims: credential.claims.reduce(into: [String: String](), { partialResult, claim in + partialResult[claim.key] = claim.getValueAsString() + }), + credentialDefinitionId: credential.properties["credentialDefinitionId"] as? String, + schemaId: credential.properties["schemaId"] as? String + ) + } else { + return CredentialDetailViewState( + issuer: "", + claims: [:], + credentialDefinitionId: nil, + schemaId: nil + ) + } + } + .replaceError(with: CredentialDetailViewState( + issuer: "", + claims: [:], + credentialDefinitionId: nil, + schemaId: nil + )) + .assign(to: &$credential) + } +} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Credentials/CredentialDetail/CredentialDetailViewState.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Credentials/CredentialDetail/CredentialDetailViewState.swift new file mode 100644 index 00000000..eb41e457 --- /dev/null +++ b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Credentials/CredentialDetail/CredentialDetailViewState.swift @@ -0,0 +1,8 @@ +import Foundation + +struct CredentialDetailViewState { + let issuer: String + let claims: [String: String] + let credentialDefinitionId: String? + let schemaId: String? +} diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Credentials/CredentialsList/CredentialListRouter.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Credentials/CredentialsList/CredentialListRouter.swift index c45cbdeb..b6526d2a 100644 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Credentials/CredentialsList/CredentialListRouter.swift +++ b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Credentials/CredentialsList/CredentialListRouter.swift @@ -1,9 +1,13 @@ +import PrismAgent import SwiftUI struct CredentialListRouterImpl: CredentialListRouter { let container: DIContainer func routeToCredentialDetail(id: String) -> some View { - Text("") + CredentialDetailView(viewModel: CredentialDetailViewModelImpl( + agent: container.resolve(type: PrismAgent.self)!, + credentialId: id + )) } } diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Credentials/CredentialsList/CredentialListView.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Credentials/CredentialsList/CredentialListView.swift index d4051b20..8467d9d2 100644 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Credentials/CredentialsList/CredentialListView.swift +++ b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Credentials/CredentialsList/CredentialListView.swift @@ -22,7 +22,7 @@ struct CredentialListView< List(viewModel.credentials, id: \.id) { credential in NavigationLink(value: credential) { VStack(alignment: .leading) { - Text(credential.credentialType) + Text(credential.id) .font(.headline) Text(credential.issuer) .lineLimit(1) @@ -32,19 +32,9 @@ struct CredentialListView< Text(credential.issuanceDate) .font(.subheadline) .foregroundColor(.secondary) - if !credential.type.isEmpty { - Spacer() - HStack { - ForEach(credential.type, id: \.self) { type in - Text(type) - .font(.caption) - .padding(.horizontal, 8) - .padding(.vertical, 4) - .background(Color.blue.opacity(0.2)) - .cornerRadius(4) - } - } - } + Text(credential.type) + .font(.subheadline) + .foregroundColor(.secondary) } } } diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Credentials/CredentialsList/CredentialListViewModel.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Credentials/CredentialsList/CredentialListViewModel.swift index eb78a503..cf22521e 100644 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Credentials/CredentialsList/CredentialListViewModel.swift +++ b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Credentials/CredentialsList/CredentialListViewModel.swift @@ -19,12 +19,10 @@ final class CredentialListViewModelImpl: CredentialListViewModel { .map { $0.map { CredentialListViewState.Credential( - credentialType: "", id: $0.id, issuer: $0.issuer, - issuanceDate: "",//$0.issuanceDate.formatted(), - context: [""], - type: [""] + issuanceDate: $0.storable?.queryCredentialCreated?.formatted() ?? "", + type: $0.credentialType ) } } diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Credentials/CredentialsList/CredentialListViewState.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Credentials/CredentialsList/CredentialListViewState.swift index 37243c3e..7df7a71a 100644 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Credentials/CredentialsList/CredentialListViewState.swift +++ b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Credentials/CredentialsList/CredentialListViewState.swift @@ -1,10 +1,8 @@ struct CredentialListViewState { struct Credential: Hashable { - let credentialType: String let id: String let issuer: String let issuanceDate: String - let context: [String] - let type: [String] + let type: String } } diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Main/Main2Router.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Main/Main2Router.swift index eb1d8729..a8d30580 100644 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Main/Main2Router.swift +++ b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Main/Main2Router.swift @@ -97,10 +97,7 @@ private func createSecretsStream( Future { try await array.asyncMap { did, privateKeys, _ in let privateKeys = try await privateKeys.asyncMap { - try await keyRestoration.restorePrivateKey( - identifier: $0.restorationIdentifier, - data: $0.storableData - ) + try await keyRestoration.restorePrivateKey($0) } let secrets = try parsePrivateKeys( did: did, diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Mediator/MediatorPage/MediatorPageViewModel.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Mediator/MediatorPage/MediatorPageViewModel.swift index 360e66ad..4eb583c3 100644 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Mediator/MediatorPage/MediatorPageViewModel.swift +++ b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Mediator/MediatorPage/MediatorPageViewModel.swift @@ -55,7 +55,6 @@ final class MediatorViewModelImpl: MediatorPageViewModel { } } catch let error as LocalizedError { await MainActor.run { [weak self] in - print(error.localizedDescription) self?.error = FancyToast( type: .error, title: error.localizedDescription, diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Messages/MessageDetail/MessageDetailViewModel.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Messages/MessageDetail/MessageDetailViewModel.swift index 4702a88e..0ff14703 100644 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Messages/MessageDetail/MessageDetailViewModel.swift +++ b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Messages/MessageDetail/MessageDetailViewModel.swift @@ -19,7 +19,6 @@ final class MessageDetailViewModelImpl: MessageDetailViewModel { ) @Published var messagesThread = [MessageDetailViewState.Message]() - @Published var credentials = [] @Published var error: FancyToast? @Published var loading = false @Published var dismiss = false @@ -75,7 +74,6 @@ final class MessageDetailViewModelImpl: MessageDetailViewModel { let msgType = ProtocolTypes(rawValue: message.piuri) else { return } let agent = self.agent - let pluto = self.pluto Task.detached { [weak self] in do { switch msgType { @@ -89,11 +87,11 @@ final class MessageDetailViewModelImpl: MessageDetailViewModel { credential: credential ) _ = try await agent.sendMessage(message: try presentation.makeMessage()) - case .didcommOfferCredential: + case .didcommOfferCredential, .didcommOfferCredential3_0: let newPrismDID = try await agent.createNewPrismDID() guard let requestCredential = try await agent.prepareRequestCredentialWithIssuer( did: newPrismDID, - offer: try OfferCredential(fromMessage: message) + offer: try OfferCredential3_0(fromMessage: message) ) else { throw UnknownError.somethingWentWrongError() } _ = try await agent.sendMessage(message: try requestCredential.makeMessage()) case .didcommconnectionRequest: @@ -143,18 +141,20 @@ private func getSpecificByType(msg: Message) -> MessageDetailViewState.SpecificD } catch { return .finishedThreads } - case .didcommIssueCredential: + case .didcommIssueCredential, .didcommIssueCredential3_0: return .finishedThreads case .didcommOfferCredential: do { let (domain, challenge) = try getDomainAndChallenge(msg: msg) return .credentialDomainChallenge(domain: domain, challenge: challenge) } catch { - return .finishedThreads + return .acceptRefuse } - case .didcommProposeCredential: + case .didcommOfferCredential3_0: return .acceptRefuse - case .didcommRequestCredential: + case .didcommProposeCredential, .didcommProposeCredential3_0: + return .acceptRefuse + case .didcommRequestCredential, .didcommRequestCredential3_0: return .acceptRefuse case .didcommconnectionRequest: return .acceptRefuse @@ -234,12 +234,20 @@ private func getTitle(for protocolType: ProtocolTypes) -> String { return "Credential Preview" case .didcommIssueCredential: return "Issue Credential" + case .didcommIssueCredential3_0: + return "Issue Credential 3.0" case .didcommOfferCredential: return "Offer Credential" + case .didcommOfferCredential3_0: + return "Offer Credential 3.0" case .didcommProposeCredential: return "Propose Credential" + case .didcommProposeCredential3_0: + return "Propose Credential 3.0" case .didcommRequestCredential: return "Request Credential" + case .didcommRequestCredential3_0: + return "Request Credential 3.0" case .didcommconnectionRequest: return "Connection Request" case .didcommconnectionResponse: @@ -256,5 +264,7 @@ private func getTitle(for protocolType: ProtocolTypes) -> String { return "Pickup Status" case .pickupReceived: return "Pickup Received" + case .didcommCredentialPreview3_0: + return "Credential Preview" } } diff --git a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Messages/MessagesList/MessagesListViewModel.swift b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Messages/MessagesList/MessagesListViewModel.swift index 746f370e..2b26df1f 100644 --- a/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Messages/MessagesList/MessagesListViewModel.swift +++ b/Sample/AtalaPrismWalletDemo/AtalaPrismWalletDemo/Modules/WalletDemo2/Messages/MessagesList/MessagesListViewModel.swift @@ -69,12 +69,20 @@ final class MessagesListViewModelImpl: MessageListViewModel { return "Credential Preview" case .didcommIssueCredential: return "Issue Credential" + case .didcommIssueCredential3_0: + return "Issue Credential 3.0" case .didcommOfferCredential: return "Offer Credential" + case .didcommOfferCredential3_0: + return "Offer Credential 3.0" case .didcommProposeCredential: return "Propose Credential" + case .didcommProposeCredential3_0: + return "Propose Credential 3.0" case .didcommRequestCredential: return "Request Credential" + case .didcommRequestCredential3_0: + return "Request Credential 3.0" case .didcommconnectionRequest: return "Connection Request" case .didcommconnectionResponse: @@ -91,6 +99,8 @@ final class MessagesListViewModelImpl: MessageListViewModel { return "Pickup Status" case .pickupReceived: return "Pickup Received" + case .didcommCredentialPreview3_0: + return "Credential Preview" } } @@ -114,8 +124,8 @@ final class MessagesListViewModelImpl: MessageListViewModel { other: to, name: nil )) - case .didcommIssueCredential: - let issueCredential = try IssueCredential(fromMessage: message) + case .didcommIssueCredential, .didcommIssueCredential3_0: + let issueCredential = try IssueCredential3_0(fromMessage: message) _ = try await agent.processIssuedCredentialMessage(message: issueCredential) default: break