diff --git a/Example/SmileID/Home/HomeView.swift b/Example/SmileID/Home/HomeView.swift index 086b7fb4..6e095f64 100644 --- a/Example/SmileID/Home/HomeView.swift +++ b/Example/SmileID/Home/HomeView.swift @@ -59,11 +59,8 @@ struct HomeView: View { viewModel.onProductClicked() }, content: { - SmileID.smartSelfieEnrollmentScreen( + SmileID.smartSelfieEnrollmentScreenEnhanced( userId: viewModel.newUserId, - jobId: viewModel.newJobId, - allowAgentMode: true, - useStrictMode: true, delegate: SmartSelfieEnrollmentDelegate( userId: viewModel.newUserId, onEnrollmentSuccess: viewModel.onSmartSelfieEnrollment, @@ -79,9 +76,8 @@ struct HomeView: View { viewModel.onProductClicked() }, content: { - SmartSelfieAuthWithUserIdEntry( + SmartSelfieAuthEnhancedWithUserIdEntry( initialUserId: viewModel.lastSelfieEnrollmentUserId ?? "", - useStrictMode: true, delegate: viewModel ) } @@ -196,7 +192,6 @@ struct SmartSelfieEnrollmentDelegate: SmartSelfieResultDelegate { private struct SmartSelfieAuthWithUserIdEntry: View { let initialUserId: String - var useStrictMode: Bool = false let delegate: SmartSelfieResultDelegate @State private var userId: String? @@ -206,7 +201,26 @@ private struct SmartSelfieAuthWithUserIdEntry: View { SmileID.smartSelfieAuthenticationScreen( userId: userId, allowAgentMode: true, - useStrictMode: useStrictMode, + delegate: delegate + ) + } else { + EnterUserIDView(initialUserId: initialUserId) { userId in + self.userId = userId + } + } + } +} + +private struct SmartSelfieAuthEnhancedWithUserIdEntry: View { + let initialUserId: String + let delegate: SmartSelfieResultDelegate + + @State private var userId: String? + + var body: some View { + if let userId { + SmileID.smartSelfieAuthenticationScreenEnhanced( + userId: userId, delegate: delegate ) } else { diff --git a/Sources/SmileID/Classes/SelfieCapture/EnhancedSmartSelfieViewModel.swift b/Sources/SmileID/Classes/SelfieCapture/EnhancedSmartSelfieViewModel.swift index 105a5bb7..b6165a82 100644 --- a/Sources/SmileID/Classes/SelfieCapture/EnhancedSmartSelfieViewModel.swift +++ b/Sources/SmileID/Classes/SelfieCapture/EnhancedSmartSelfieViewModel.swift @@ -67,11 +67,8 @@ public class EnhancedSmartSelfieViewModel: ObservableObject { // MARK: Injected Properties private let isEnroll: Bool private let userId: String - private let jobId: String private let allowNewEnroll: Bool - private let skipApiSubmission: Bool private let extraPartnerParams: [String: String] - private let useStrictMode: Bool private let onResult: SmartSelfieResultDelegate private var localMetadata: LocalMetadata @@ -92,21 +89,15 @@ public class EnhancedSmartSelfieViewModel: ObservableObject { public init( isEnroll: Bool, userId: String, - jobId: String, allowNewEnroll: Bool, - skipApiSubmission: Bool, extraPartnerParams: [String: String], - useStrictMode: Bool, onResult: SmartSelfieResultDelegate, localMetadata: LocalMetadata ) { self.isEnroll = isEnroll self.userId = userId - self.jobId = jobId self.allowNewEnroll = allowNewEnroll - self.skipApiSubmission = skipApiSubmission self.extraPartnerParams = extraPartnerParams - self.useStrictMode = useStrictMode self.onResult = onResult self.localMetadata = localMetadata self.initialSetup() @@ -294,8 +285,9 @@ extension EnhancedSmartSelfieViewModel { throw SmileIDError.unknown("Error resizing selfie image") } self.selfieImage = flipImageForPreview(uiImage) + // we use a userId and not a jobId here self.selfieImageURL = try LocalStorage.createSelfieFile( - jobId: jobId, selfieFile: imageData) + jobId: userId, selfieFile: imageData) } catch { handleError(error) } @@ -345,8 +337,9 @@ extension EnhancedSmartSelfieViewModel { else { throw SmileIDError.unknown("Error resizing liveness image") } + // we use a userId and not a jobId here let imageUrl = try LocalStorage.createLivenessFile( - jobId: jobId, livenessFile: imageData) + jobId: userId, livenessFile: imageData) livenessImages.append(imageUrl) } catch { handleError(error) @@ -485,15 +478,9 @@ extension EnhancedSmartSelfieViewModel: SelfieSubmissionDelegate { // Add metadata before submission addSelfieCaptureMetaData() - if skipApiSubmission { - // Skip API submission and update processing state to success - self.selfieCaptureState = .processing(.success) - return - } // Create an instance of SelfieSubmissionManager to manage the submission process let submissionManager = SelfieSubmissionManager( userId: self.userId, - jobId: self.jobId, isEnroll: self.isEnroll, numLivenessImages: self.numLivenessImages, allowNewEnroll: self.allowNewEnroll, diff --git a/Sources/SmileID/Classes/SelfieCapture/SelfieSubmissionManager.swift b/Sources/SmileID/Classes/SelfieCapture/SelfieSubmissionManager.swift index c25217b0..3fef8fa3 100644 --- a/Sources/SmileID/Classes/SelfieCapture/SelfieSubmissionManager.swift +++ b/Sources/SmileID/Classes/SelfieCapture/SelfieSubmissionManager.swift @@ -14,7 +14,6 @@ protocol SelfieSubmissionDelegate: AnyObject { final class SelfieSubmissionManager { // MARK: - Properties private let userId: String - private let jobId: String private let isEnroll: Bool private let numLivenessImages: Int private let allowNewEnroll: Bool @@ -28,7 +27,6 @@ final class SelfieSubmissionManager { // MARK: - Initializer init( userId: String, - jobId: String, isEnroll: Bool, numLivenessImages: Int, allowNewEnroll: Bool, @@ -38,7 +36,6 @@ final class SelfieSubmissionManager { localMetadata: LocalMetadata ) { self.userId = userId - self.jobId = jobId self.isEnroll = isEnroll self.numLivenessImages = numLivenessImages self.allowNewEnroll = allowNewEnroll @@ -102,14 +99,14 @@ final class SelfieSubmissionManager { return AuthenticationRequest( jobType: jobType, enrollment: isEnroll, - jobId: jobId, userId: userId ) } + // we need to discuss this private func saveOfflineMode(jobType: JobType) throws { try LocalStorage.saveOfflineJob( - jobId: jobId, + jobId: userId, userId: userId, jobType: jobType, enrollment: isEnroll, @@ -187,17 +184,17 @@ final class SelfieSubmissionManager { private func updateLocalStorageAfterSuccess() throws { // Move the job to the submitted jobs directory for record-keeping - try LocalStorage.moveToSubmittedJobs(jobId: self.jobId) + try LocalStorage.moveToSubmittedJobs(jobId: self.userId) // Update the references to the submitted selfie and liveness images self.selfieImageUrl = try LocalStorage.getFileByType( - jobId: jobId, + jobId: userId, fileType: FileType.selfie, submitted: true ) self.livenessImages = try LocalStorage.getFilesByType( - jobId: jobId, + jobId: userId, fileType: FileType.liveness, submitted: true ) ?? [] @@ -205,11 +202,11 @@ final class SelfieSubmissionManager { private func handleJobSubmissionFailure(_ smileIDError: SmileIDError) { do { - let didMove = try LocalStorage.handleOfflineJobFailure(jobId: self.jobId, error: smileIDError) + let didMove = try LocalStorage.handleOfflineJobFailure(jobId: self.userId, error: smileIDError) if didMove { - self.selfieImageUrl = try LocalStorage.getFileByType(jobId: jobId, fileType: .selfie, submitted: true) + self.selfieImageUrl = try LocalStorage.getFileByType(jobId: userId, fileType: .selfie, submitted: true) self.livenessImages = - try LocalStorage.getFilesByType(jobId: jobId, fileType: .liveness, submitted: true) ?? [] + try LocalStorage.getFilesByType(jobId: userId, fileType: .liveness, submitted: true) ?? [] } } catch { let (errorMessageRes, errorMessage) = toErrorMessage(error: smileIDError) diff --git a/Sources/SmileID/Classes/SelfieCapture/View/OrchestratedEnhancedSelfieCaptureScreen.swift b/Sources/SmileID/Classes/SelfieCapture/View/OrchestratedEnhancedSelfieCaptureScreen.swift index 6cd1b67c..074570e1 100644 --- a/Sources/SmileID/Classes/SelfieCapture/View/OrchestratedEnhancedSelfieCaptureScreen.swift +++ b/Sources/SmileID/Classes/SelfieCapture/View/OrchestratedEnhancedSelfieCaptureScreen.swift @@ -4,7 +4,6 @@ import SwiftUI /// Orchestrates the selfie capture flow - navigates between instructions, requesting permissions, /// showing camera view, and displaying processing screen public struct OrchestratedEnhancedSelfieCaptureScreen: View { - public let allowAgentMode: Bool public let showAttribution: Bool public let showInstructions: Bool public let onResult: SmartSelfieResultDelegate @@ -12,29 +11,21 @@ public struct OrchestratedEnhancedSelfieCaptureScreen: View { public init( userId: String, - jobId: String, isEnroll: Bool, allowNewEnroll: Bool, - allowAgentMode: Bool, showAttribution: Bool, showInstructions: Bool, - useStrictMode: Bool, extraPartnerParams: [String: String], - skipApiSubmission: Bool, onResult: SmartSelfieResultDelegate ) { - self.allowAgentMode = allowAgentMode self.showAttribution = showAttribution self.showInstructions = showInstructions self.onResult = onResult self.viewModel = EnhancedSmartSelfieViewModel( isEnroll: isEnroll, userId: userId, - jobId: jobId, allowNewEnroll: allowNewEnroll, - skipApiSubmission: skipApiSubmission, extraPartnerParams: extraPartnerParams, - useStrictMode: useStrictMode, onResult: onResult, localMetadata: LocalMetadata() ) diff --git a/Sources/SmileID/Classes/SmileID.swift b/Sources/SmileID/Classes/SmileID.swift index 50497faf..777617b0 100644 --- a/Sources/SmileID/Classes/SmileID.swift +++ b/Sources/SmileID/Classes/SmileID.swift @@ -308,21 +308,6 @@ public class SmileID { extraPartnerParams: [String: String] = [:], delegate: SmartSelfieResultDelegate ) -> some View { - if useStrictMode { - OrchestratedEnhancedSelfieCaptureScreen( - userId: userId, - jobId: jobId, - isEnroll: true, - allowNewEnroll: allowNewEnroll, - allowAgentMode: allowAgentMode, - showAttribution: showAttribution, - showInstructions: showInstructions, - useStrictMode: useStrictMode, - extraPartnerParams: extraPartnerParams, - skipApiSubmission: skipApiSubmission, - onResult: delegate - ) - } else { OrchestratedSelfieCaptureScreen( userId: userId, jobId: jobId, @@ -335,7 +320,39 @@ public class SmileID { skipApiSubmission: skipApiSubmission, onResult: delegate ) - } + } + + /// Perform a SmartSelfie™ Enrollment + /// + /// Docs: https://docs.usesmileid.com/products/for-individuals-kyc/biometric-authentication + /// + /// - Parameters: + /// - userId: The user ID to associate with the SmartSelfie™ Enrollment. Most often, this will + /// correspond to a unique User ID within your own system. If not provided, a random user ID + /// will be generated. + /// - allowNewEnroll: Allows a partner to enroll the same user id again + /// - showAttribution: Whether to show the Smile ID attribution or not on the Instructions + /// screen + /// - showInstructions: Whether to deactivate capture screen's instructions for SmartSelfie. + /// - extraPartnerParams: Custom values specific to partners + /// - delegate: Callback to be invoked when the SmartSelfie™ Enrollment is complete. + @ViewBuilder public class func smartSelfieEnrollmentScreenEnhanced( + userId: String = generateUserId(), + allowNewEnroll: Bool = false, + showAttribution: Bool = true, + showInstructions: Bool = true, + extraPartnerParams: [String: String] = [:], + delegate: SmartSelfieResultDelegate + ) -> some View { + OrchestratedEnhancedSelfieCaptureScreen( + userId: userId, + isEnroll: true, + allowNewEnroll: allowNewEnroll, + showAttribution: showAttribution, + showInstructions: showInstructions, + extraPartnerParams: extraPartnerParams, + onResult: delegate + ) } /// Perform a SmartSelfie™ Authentication @@ -370,34 +387,58 @@ public class SmileID { extraPartnerParams: [String: String] = [:], delegate: SmartSelfieResultDelegate ) -> some View { - if useStrictMode { - OrchestratedEnhancedSelfieCaptureScreen( - userId: userId, - jobId: jobId, - isEnroll: false, - allowNewEnroll: allowNewEnroll, - allowAgentMode: allowAgentMode, - showAttribution: showAttribution, - showInstructions: showInstructions, - useStrictMode: useStrictMode, - extraPartnerParams: extraPartnerParams, - skipApiSubmission: false, - onResult: delegate - ) - } else { - OrchestratedSelfieCaptureScreen( - userId: userId, - jobId: jobId, - isEnroll: false, - allowNewEnroll: allowNewEnroll, - allowAgentMode: allowAgentMode, - showAttribution: showAttribution, - showInstructions: showInstructions, - extraPartnerParams: extraPartnerParams, - skipApiSubmission: false, - onResult: delegate - ) - } + OrchestratedSelfieCaptureScreen( + userId: userId, + jobId: jobId, + isEnroll: false, + allowNewEnroll: allowNewEnroll, + allowAgentMode: allowAgentMode, + showAttribution: showAttribution, + showInstructions: showInstructions, + extraPartnerParams: extraPartnerParams, + skipApiSubmission: false, + onResult: delegate + ) + } + + /// Perform a SmartSelfie™ Authentication + /// + /// Docs: https://docs.usesmileid.com/products/for-individuals-kyc/biometric-authentication + /// + /// - Parameters: + /// - userId: The user ID to associate with the SmartSelfie™ Enrollment. Most often, this will + /// correspond to a unique User ID within your own system. If not provided, a random user ID + /// will be generated. + /// - jobId: The job ID to associate with the SmartSelfie™ Enrollment. Most often, this will + /// correspond to a unique Job ID within your own system. If not provided, a random job ID + /// will be generated. + /// - allowNewEnroll: Allows a partner to enroll the same user id again + /// - allowAgentMode: Whether to allow Agent Mode or not. If allowed, a switch will be + /// displayed allowing toggling between the back camera and front camera. If not allowed, + /// only the front camera will be used. + /// - showAttribution: Whether to show the Smile ID attribution or not on the Instructions + /// screen + /// - showInstructions: Whether to deactivate capture screen's instructions for SmartSelfie. + /// - skipApiSubmission: Whether to skip api submission to SmileID and return only captured images + /// - extraPartnerParams: Custom values specific to partners + /// - delegate: Callback to be invoked when the SmartSelfie™ Authentication is complete. + @ViewBuilder public class func smartSelfieAuthenticationScreenEnhanced( + userId: String, + allowNewEnroll: Bool = false, + showAttribution: Bool = true, + showInstructions: Bool = true, + extraPartnerParams: [String: String] = [:], + delegate: SmartSelfieResultDelegate + ) -> some View { + OrchestratedEnhancedSelfieCaptureScreen( + userId: userId, + isEnroll: false, + allowNewEnroll: allowNewEnroll, + showAttribution: showAttribution, + showInstructions: showInstructions, + extraPartnerParams: extraPartnerParams, + onResult: delegate + ) } /// Perform a Document Verification