Skip to content

Commit b746324

Browse files
committed
Update ChallengeOptions and use camera position based on challenge type received
1 parent b9a3aec commit b746324

8 files changed

+102
-83
lines changed

HostApp/HostApp/Views/ExampleLivenessView.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ struct ExampleLivenessView: View {
2323
FaceLivenessDetectorView(
2424
sessionID: viewModel.sessionID,
2525
region: "us-east-1",
26-
challengeOption: .faceMovementAndLightChallenge,
26+
challengeOptions: .init(faceMovementChallengeOption: FaceMovementChallengeOption(camera: .front),
27+
faceMovementAndLightChallengeOption: FaceMovementAndLightChallengeOption()),
2728
isPresented: Binding(
2829
get: { viewModel.presentationState == .liveness },
2930
set: { _ in }
@@ -47,6 +48,10 @@ struct ExampleLivenessView: View {
4748
viewModel.presentationState = .error(.countdownFaceTooClose)
4849
case .failure(.invalidSignature):
4950
viewModel.presentationState = .error(.invalidSignature)
51+
case .failure(.faceInOvalMatchExceededTimeLimitError):
52+
viewModel.presentationState = .error(.faceInOvalMatchExceededTimeLimitError)
53+
case .failure(.internalServer):
54+
viewModel.presentationState = .error(.internalServer)
5055
case .failure(.cameraNotAvailable):
5156
viewModel.presentationState = .error(.cameraNotAvailable)
5257
default:

HostApp/HostApp/Views/LivenessCheckErrorContentView.swift

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -60,11 +60,6 @@ extension LivenessCheckErrorContentView {
6060
name: "The camera could not be started.",
6161
description: "There might be a hardware issue with the camera."
6262
)
63-
64-
static let invalidCameraPositionSelected = LivenessCheckErrorContentView(
65-
name: "The camera position selected is incompatible with the liveness challenge type requested.",
66-
description: "Please ensure the camera position is supported for the liveness challenge type requested."
67-
)
6863

6964
}
7065

Sources/FaceLiveness/Views/Instruction/InstructionContainerView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ struct InstructionContainerView: View {
110110
)
111111
}
112112
case .faceMatched:
113-
if let challenge = viewModel.challenge,
113+
if let challenge = viewModel.challengeReceived,
114114
case .faceMovementAndLightChallenge = challenge.type {
115115
InstructionView(
116116
text: LocalizedStrings.challenge_instruction_hold_still,

Sources/FaceLiveness/Views/Liveness/FaceLivenessDetectionView.swift

Lines changed: 41 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public struct FaceLivenessDetectorView: View {
2020
@State var displayingCameraPermissionsNeededAlert = false
2121

2222
let disableStartView: Bool
23-
let cameraPosition: LivenessCamera
23+
let challengeOptions: ChallengeOptions
2424
let onCompletion: (Result<Void, FaceLivenessDetectionError>) -> Void
2525

2626
let sessionTask: Task<FaceLivenessSession, Error>
@@ -30,14 +30,14 @@ public struct FaceLivenessDetectorView: View {
3030
credentialsProvider: AWSCredentialsProvider? = nil,
3131
region: String,
3232
disableStartView: Bool = false,
33-
challengeOption: ChallengeOption,
33+
challengeOptions: ChallengeOptions,
3434
isPresented: Binding<Bool>,
3535
onCompletion: @escaping (Result<Void, FaceLivenessDetectionError>) -> Void
3636
) {
3737
self.disableStartView = disableStartView
3838
self._isPresented = isPresented
39-
self.cameraPosition = challengeOption.camera
4039
self.onCompletion = onCompletion
40+
self.challengeOptions = challengeOptions
4141

4242
self.sessionTask = Task {
4343
let session = try await AWSPredictionsPlugin.startFaceLivenessSession(
@@ -59,31 +59,16 @@ public struct FaceLivenessDetectorView: View {
5959
assetWriterDelegate: VideoChunker.AssetWriterDelegate(),
6060
assetWriterInput: LivenessAVAssetWriterInput()
6161
)
62-
63-
let avCaptureDevice = AVCaptureDevice.default(
64-
.builtInWideAngleCamera,
65-
for: .video,
66-
position: cameraPosition == .front ? .front : .back)
67-
68-
let captureSession = LivenessCaptureSession(
69-
captureDevice: .init(avCaptureDevice: avCaptureDevice),
70-
outputDelegate: OutputSampleBufferCapturer(
71-
faceDetector: faceDetector,
72-
videoChunker: videoChunker
73-
)
74-
)
7562

7663
self._viewModel = StateObject(
7764
wrappedValue: .init(
7865
faceDetector: faceDetector,
7966
faceInOvalMatching: faceInOvalStateMatching,
80-
captureSession: captureSession,
8167
videoChunker: videoChunker,
8268
closeButtonAction: { onCompletion(.failure(.userCancelled)) },
8369
sessionID: sessionID,
8470
isPreviewScreenEnabled: !disableStartView,
85-
cameraPosition: challengeOption.camera,
86-
challengeOption: challengeOption
71+
challengeOptions: challengeOptions
8772
)
8873
)
8974
}
@@ -93,15 +78,15 @@ public struct FaceLivenessDetectorView: View {
9378
credentialsProvider: AWSCredentialsProvider? = nil,
9479
region: String,
9580
disableStartView: Bool = false,
96-
challengeOption: ChallengeOption,
81+
challengeOptions: ChallengeOptions,
9782
isPresented: Binding<Bool>,
9883
onCompletion: @escaping (Result<Void, FaceLivenessDetectionError>) -> Void,
9984
captureSession: LivenessCaptureSession
10085
) {
10186
self.disableStartView = disableStartView
10287
self._isPresented = isPresented
10388
self.onCompletion = onCompletion
104-
self.cameraPosition = challengeOption.camera
89+
self.challengeOptions = challengeOptions
10590

10691
self.sessionTask = Task {
10792
let session = try await AWSPredictionsPlugin.startFaceLivenessSession(
@@ -121,13 +106,11 @@ public struct FaceLivenessDetectorView: View {
121106
wrappedValue: .init(
122107
faceDetector: captureSession.outputSampleBufferCapturer!.faceDetector,
123108
faceInOvalMatching: faceInOvalStateMatching,
124-
captureSession: captureSession,
125109
videoChunker: captureSession.outputSampleBufferCapturer!.videoChunker,
126110
closeButtonAction: { onCompletion(.failure(.userCancelled)) },
127111
sessionID: sessionID,
128112
isPreviewScreenEnabled: !disableStartView,
129-
cameraPosition: challengeOption.camera,
130-
challengeOption: challengeOption
113+
challengeOptions: challengeOptions
131114
)
132115
)
133116
}
@@ -172,6 +155,14 @@ public struct FaceLivenessDetectorView: View {
172155
.onAppear {
173156
Task {
174157
do {
158+
let cameraPosition: LivenessCamera
159+
switch challenge.type {
160+
case .faceMovementAndLightChallenge:
161+
cameraPosition = challengeOptions.faceMovementAndLightChallengeOption.camera
162+
case .faceMovementChallenge:
163+
cameraPosition = challengeOptions.faceMovementChallengeOption.camera
164+
}
165+
175166
let newState = disableStartView
176167
? DisplayState.displayingLiveness
177168
: DisplayState.displayingGetReadyView(challenge, cameraPosition)
@@ -255,7 +246,7 @@ public struct FaceLivenessDetectorView: View {
255246
for: .video,
256247
completionHandler: { accessGranted in
257248
guard accessGranted == true else { return }
258-
guard let challenge = viewModel.challenge else { return }
249+
guard let challenge = viewModel.challengeReceived else { return }
259250
displayState = .awaitingLivenessSession(challenge)
260251
}
261252
)
@@ -274,7 +265,7 @@ public struct FaceLivenessDetectorView: View {
274265
case .restricted, .denied:
275266
alertCameraAccessNeeded()
276267
case .authorized:
277-
guard let challenge = viewModel.challenge else { return }
268+
guard let challenge = viewModel.challengeReceived else { return }
278269
displayState = .awaitingLivenessSession(challenge)
279270
@unknown default:
280271
break
@@ -341,32 +332,38 @@ private func map(detectionCompletion: @escaping (Result<Void, FaceLivenessDetect
341332
}
342333
}
343334

344-
enum LivenessCamera {
335+
public enum LivenessCamera {
345336
case front
346337
case back
347338
}
348339

349-
public struct ChallengeOption {
340+
public struct ChallengeOptions {
341+
let faceMovementChallengeOption: FaceMovementChallengeOption
342+
let faceMovementAndLightChallengeOption: FaceMovementAndLightChallengeOption
343+
344+
public init(faceMovementChallengeOption: FaceMovementChallengeOption,
345+
faceMovementAndLightChallengeOption: FaceMovementAndLightChallengeOption) {
346+
self.faceMovementChallengeOption = faceMovementChallengeOption
347+
self.faceMovementAndLightChallengeOption = faceMovementAndLightChallengeOption
348+
}
349+
}
350+
351+
public struct FaceMovementChallengeOption {
350352
let challenge: Challenge
351353
let camera: LivenessCamera
352354

353-
init(challenge: Challenge, camera: LivenessCamera) {
354-
self.challenge = challenge
355+
public init(camera: LivenessCamera) {
356+
self.challenge = .init(version: "1.0.0", type: .faceMovementChallenge)
355357
self.camera = camera
356358
}
359+
}
360+
361+
public struct FaceMovementAndLightChallengeOption {
362+
let challenge: Challenge
363+
let camera: LivenessCamera
357364

358-
public static let faceMovementAndLightChallenge = Self.init(
359-
challenge: .init(version: "2.0.0",
360-
type: .faceMovementAndLightChallenge),
361-
camera: .front)
362-
363-
public static let faceMovementChallengeWithFrontCamera = Self.init(
364-
challenge: .init(version: "1.0.0",
365-
type: .faceMovementAndLightChallenge),
366-
camera: .front)
367-
368-
public static let faceMovementChallengeWithBackCamera = Self.init(
369-
challenge: .init(version: "1.0.0",
370-
type: .faceMovementAndLightChallenge),
371-
camera: .back)
365+
public init() {
366+
self.challenge = .init(version: "2.0.0", type: .faceMovementAndLightChallenge)
367+
self.camera = .front
368+
}
372369
}

Sources/FaceLiveness/Views/Liveness/FaceLivenessDetectionViewModel+FaceDetectionResultHandler.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ extension FaceLivenessDetectionViewModel: FaceDetectionResultHandler {
113113
self.faceMatchedTimestamp = Date().timestampMilliseconds
114114

115115
// next step after face match
116-
switch self.challenge?.type {
116+
switch self.challengeReceived?.type {
117117
case .faceMovementAndLightChallenge:
118118
if let colorSequences = colorSequences {
119119
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
@@ -146,7 +146,7 @@ extension FaceLivenessDetectionViewModel: FaceDetectionResultHandler {
146146
DispatchQueue.main.async {
147147
self.livenessState
148148
.unrecoverableStateEncountered(.timedOut)
149-
self.captureSession.stopRunning()
149+
self.captureSession?.stopRunning()
150150
}
151151
}
152152
}

0 commit comments

Comments
 (0)