Skip to content

Commit 5b47401

Browse files
committed
fix: update no match timeout check from frame count to elapsed time (#41)
* fix: update no match timeout check from frame count to elapsed time * chore: fix spacing * fix: resolve build errors in latest fix * add unit test * fix failed unit test
1 parent c09aba6 commit 5b47401

File tree

3 files changed

+31
-5
lines changed

3 files changed

+31
-5
lines changed

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,12 @@ extension FaceLivenessDetectionViewModel: FaceDetectionResultHandler {
9090
}
9191

9292
func handleNoMatch(instruction: Instructor.Instruction, percentage: Double) {
93+
let noMatchTimeoutInterval: TimeInterval = 7
9394
self.livenessState.awaitingFaceMatch(with: instruction, nearnessPercentage: percentage)
94-
noMatchCount += 1
95-
if noMatchCount >= 210 {
95+
if noMatchStartTime == nil {
96+
noMatchStartTime = Date()
97+
}
98+
if let elapsedTime = noMatchStartTime?.timeIntervalSinceNow, abs(elapsedTime) >= noMatchTimeoutInterval {
9699
self.livenessState
97100
.unrecoverableStateEncountered(.timedOut)
98101
self.captureSession.stopRunning()
@@ -112,7 +115,7 @@ extension FaceLivenessDetectionViewModel: FaceDetectionResultHandler {
112115
self.livenessViewControllerDelegate?.displayFreshness(colorSequences: colorSequences)
113116
let generator = UINotificationFeedbackGenerator()
114117
generator.notificationOccurred(.success)
115-
self.noMatchCount = 0
118+
self.noMatchStartTime = nil
116119

117120
case .tooClose(_, let percentage),
118121
.tooFar(_, let percentage),

Sources/FaceLiveness/Views/Liveness/FaceLivenessDetectionViewModel.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,8 @@ class FaceLivenessDetectionViewModel: ObservableObject {
3939
var faceGuideRect: CGRect!
4040
var initialClientEvent: InitialClientEvent?
4141
var faceMatchedTimestamp: UInt64?
42-
var noMatchCount = 0
43-
42+
var noMatchStartTime: Date?
43+
4444
init(
4545
faceDetector: FaceDetector,
4646
faceInOvalMatching: FaceInOvalMatching,

Tests/FaceLivenessTests/LivenessTests.swift

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,13 @@ final class FaceLivenessDetectionViewModelTestCase: XCTestCase {
3838
self.videoChunker = videoChunker
3939
self.viewModel = viewModel
4040
}
41+
42+
override func tearDown() {
43+
self.faceDetector = nil
44+
self.livenessService = nil
45+
self.videoChunker = nil
46+
self.viewModel = nil
47+
}
4148

4249
/// Given: A `FaceLivenessDetectionViewModel`
4350
/// When: The viewModel is first initialized
@@ -100,4 +107,20 @@ final class FaceLivenessDetectionViewModelTestCase: XCTestCase {
100107
"initializeLivenessStream(withSessionID:userAgent:)"
101108
])
102109
}
110+
111+
/// Given: A `FaceLivenessDetectionViewModel`
112+
/// When: The viewModel handles a no match event over a duration of 7 seconds
113+
/// Then: The end state is `.encounteredUnrecoverableError(.timedOut)`
114+
func testNoMatchTimeoutCheck() async throws {
115+
viewModel.livenessService = self.livenessService
116+
self.viewModel.handleNoMatch(instruction: .tooFar(nearnessPercentage: 0.2), percentage: 0.2)
117+
118+
XCTAssertNotEqual(self.viewModel.livenessState.state, .encounteredUnrecoverableError(.timedOut))
119+
try await Task.sleep(seconds: 6)
120+
self.viewModel.handleNoMatch(instruction: .tooFar(nearnessPercentage: 0.2), percentage: 0.2)
121+
XCTAssertNotEqual(self.viewModel.livenessState.state, .encounteredUnrecoverableError(.timedOut))
122+
try await Task.sleep(seconds: 1)
123+
self.viewModel.handleNoMatch(instruction: .tooFar(nearnessPercentage: 0.2), percentage: 0.2)
124+
XCTAssertEqual(self.viewModel.livenessState.state, .encounteredUnrecoverableError(.timedOut))
125+
}
103126
}

0 commit comments

Comments
 (0)