Skip to content

Commit 421480f

Browse files
authored
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 0c0873a commit 421480f

File tree

3 files changed

+32
-6
lines changed

3 files changed

+32
-6
lines changed

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

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

8686
func handleNoMatch(instruction: Instructor.Instruction, percentage: Double) {
87+
let noMatchTimeoutInterval: TimeInterval = 7
8788
self.livenessState.awaitingFaceMatch(with: instruction, nearnessPercentage: percentage)
88-
noMatchCount += 1
89-
if noMatchCount >= 210 {
89+
if noMatchStartTime == nil {
90+
noMatchStartTime = Date()
91+
}
92+
if let elapsedTime = noMatchStartTime?.timeIntervalSinceNow, abs(elapsedTime) >= noMatchTimeoutInterval {
9093
self.livenessState
9194
.unrecoverableStateEncountered(.timedOut)
9295
self.captureSession.stopRunning()
@@ -106,7 +109,7 @@ extension FaceLivenessDetectionViewModel: FaceDetectionResultHandler {
106109
self.livenessViewControllerDelegate?.displayFreshness(colorSequences: colorSequences)
107110
let generator = UINotificationFeedbackGenerator()
108111
generator.notificationOccurred(.success)
109-
self.noMatchCount = 0
112+
self.noMatchStartTime = nil
110113

111114
case .tooClose(_, let percentage),
112115
.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: 24 additions & 1 deletion
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
@@ -113,7 +120,7 @@ final class FaceLivenessDetectionViewModelTestCase: XCTestCase {
113120
"setResultHandler(detectionResultHandler:) (FaceLivenessDetectionViewModel)"
114121
])
115122
XCTAssertEqual(livenessService.interactions, [])
116-
123+
117124
let boundingBox = CGRect(x: 0.26788579725878847, y: 0.40317180752754211, width: 0.45549795395626447, height: 0.34162446856498718)
118125
let leftEye = CGPoint(x: 0.61124476128552629, y: 0.4918237030506134)
119126
let rightEye = CGPoint(x: 0.38036393762719456, y: 0.48050540685653687)
@@ -131,4 +138,20 @@ final class FaceLivenessDetectionViewModelTestCase: XCTestCase {
131138
"initializeLivenessStream(withSessionID:userAgent:)"
132139
])
133140
}
141+
142+
/// Given: A `FaceLivenessDetectionViewModel`
143+
/// When: The viewModel handles a no match event over a duration of 7 seconds
144+
/// Then: The end state is `.encounteredUnrecoverableError(.timedOut)`
145+
func testNoMatchTimeoutCheck() async throws {
146+
viewModel.livenessService = self.livenessService
147+
self.viewModel.handleNoMatch(instruction: .tooFar(nearnessPercentage: 0.2), percentage: 0.2)
148+
149+
XCTAssertNotEqual(self.viewModel.livenessState.state, .encounteredUnrecoverableError(.timedOut))
150+
try await Task.sleep(seconds: 6)
151+
self.viewModel.handleNoMatch(instruction: .tooFar(nearnessPercentage: 0.2), percentage: 0.2)
152+
XCTAssertNotEqual(self.viewModel.livenessState.state, .encounteredUnrecoverableError(.timedOut))
153+
try await Task.sleep(seconds: 1)
154+
self.viewModel.handleNoMatch(instruction: .tooFar(nearnessPercentage: 0.2), percentage: 0.2)
155+
XCTAssertEqual(self.viewModel.livenessState.state, .encounteredUnrecoverableError(.timedOut))
156+
}
134157
}

0 commit comments

Comments
 (0)