Skip to content

Commit

Permalink
Address review comments
Browse files Browse the repository at this point in the history
  • Loading branch information
thisisabhash committed Apr 18, 2024
1 parent cef241a commit 728a743
Show file tree
Hide file tree
Showing 12 changed files with 79 additions and 60 deletions.
10 changes: 0 additions & 10 deletions HostApp/HostApp.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,6 @@
9070FFBD285112B5009867D5 /* HostAppUITests */,
9070FFA1285112B4009867D5 /* Products */,
90215EED291E9FB60050F2AD /* Frameworks */,
A5A9AF5054D0FF13505B212A /* AmplifyConfig */,
);
sourceTree = "<group>";
};
Expand Down Expand Up @@ -215,15 +214,6 @@
path = Model;
sourceTree = "<group>";
};
A5A9AF5054D0FF13505B212A /* AmplifyConfig */ = {
isa = PBXGroup;
children = (
973619242BA378690003A590 /* awsconfiguration.json */,
973619232BA378690003A590 /* amplifyconfiguration.json */,
);
name = AmplifyConfig;
sourceTree = "<group>";
};
/* End PBXGroup section */

/* Begin PBXNativeTarget section */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/aws-amplify/amplify-swift",
"state" : {
"branch" : "no-light-implementation",
"revision" : "19c7af19dfa1aa1ca49d63a82605ee0f367b9f64"
"branch" : "feat/no-light-support",
"revision" : "7c1fa2f7a766208f5af69ca8dce5fd02e6de4db6"
}
},
{
Expand Down
4 changes: 2 additions & 2 deletions Package.resolved
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/aws-amplify/amplify-swift",
"state" : {
"branch" : "no-light-implementation",
"revision" : "19c7af19dfa1aa1ca49d63a82605ee0f367b9f64"
"branch" : "feat/no-light-support",
"revision" : "7c1fa2f7a766208f5af69ca8dce5fd02e6de4db6"
}
},
{
Expand Down
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ let package = Package(
],
dependencies: [
// TODO: Change this before merge to main
.package(url: "https://github.com/aws-amplify/amplify-swift", branch: "no-light-implementation")
.package(url: "https://github.com/aws-amplify/amplify-swift", branch: "feat/no-light-support")
],
targets: [
.target(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,15 @@ extension FaceDetectorShortRange {
)
}

weak var faceDetectionSessionConfiguration: FaceDetectionSessionConfiguration?
weak var faceDetectionSessionConfiguration: FaceDetectionSessionConfigurationWrapper?
weak var detectionResultHandler: FaceDetectionResultHandler?

func setResultHandler(detectionResultHandler: FaceDetectionResultHandler) {
self.detectionResultHandler = detectionResultHandler
}

func setFaceLivenessDetectionViewModel(faceDetectionSessionConfiguration: FaceDetectionSessionConfiguration) {
self.faceDetectionSessionConfiguration = faceDetectionSessionConfiguration
func setFaceDetectionSessionConfigurationWrapper(configuration: FaceDetectionSessionConfigurationWrapper) {
self.faceDetectionSessionConfiguration = configuration
}

func detectFaces(from buffer: CVPixelBuffer) {
Expand Down Expand Up @@ -113,7 +113,7 @@ extension FaceDetectorShortRange {
)

let blazeFaceDetectionThreshold: Float
if let sessionConfiguration = faceDetectionSessionConfiguration?.getFaceDetectionSessionConfiguration() {
if let sessionConfiguration = faceDetectionSessionConfiguration?.sessionConfiguration {
blazeFaceDetectionThreshold = Float(sessionConfiguration.ovalMatchChallenge.faceDetectionThreshold)
} else {
blazeFaceDetectionThreshold = confidenceScoreThreshold
Expand Down
5 changes: 2 additions & 3 deletions Sources/FaceLiveness/FaceDetection/FaceDetector.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,8 @@ protocol FaceDetectionResultHandler: AnyObject {
func process(newResult: FaceDetectionResult)
}

protocol FaceDetectionSessionConfiguration: AnyObject {
func getFaceDetectionSessionConfiguration()
-> FaceLivenessSession.SessionConfiguration?
protocol FaceDetectionSessionConfigurationWrapper: AnyObject {
var sessionConfiguration: FaceLivenessSession.SessionConfiguration? { get }
}

enum FaceDetectionResult {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import SwiftUI
import Combine
@_spi(PredictionsFaceLiveness) import AWSPredictionsPlugin

struct InstructionContainerView: View {
@ObservedObject var viewModel: FaceLivenessDetectionViewModel
Expand Down Expand Up @@ -109,12 +110,17 @@ struct InstructionContainerView: View {
)
}
case .faceMatched:
InstructionView(
text: LocalizedStrings.challenge_instruction_hold_still,
backgroundColor: .livenessPrimaryBackground,
textColor: .livenessPrimaryLabel,
font: .title
)
if let challenge = viewModel.challenge,
case .faceMovementAndLightChallenge = challenge.type {
InstructionView(
text: LocalizedStrings.challenge_instruction_hold_still,
backgroundColor: .livenessPrimaryBackground,
textColor: .livenessPrimaryLabel,
font: .title
)
} else {
EmptyView()
}
default:
EmptyView()
}
Expand Down
45 changes: 33 additions & 12 deletions Sources/FaceLiveness/Views/Liveness/FaceLivenessDetectionView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ public struct FaceLivenessDetectorView: View {
sessionID: sessionID
)
)

faceDetector.setFaceDetectionSessionConfigurationWrapper(configuration: viewModel)
}

init(
Expand Down Expand Up @@ -140,37 +142,37 @@ public struct FaceLivenessDetectorView: View {
do {
let session = try await sessionTask.value
viewModel.livenessService = session
viewModel.registerServiceEvents(onChallengeTypeReceived: {
self.displayState = DisplayState.awaitingLivenessSession
viewModel.registerServiceEvents(onChallengeTypeReceived: { challenge in
self.displayState = DisplayState.awaitingLivenessSession(challenge)
})
viewModel.initializeLivenessStream()
} catch {
throw FaceLivenessDetectionError.accessDenied
}
}
}
case .awaitingLivenessSession:
case .awaitingLivenessSession(let challenge):
Color.clear
.onAppear {
Task {
do {
let newState = disableStartView
? DisplayState.displayingLiveness
: DisplayState.displayingGetReadyView
: DisplayState.displayingGetReadyView(challenge)
guard self.displayState != newState else { return }
self.displayState = newState
}
}
}

case .displayingGetReadyView:
case .displayingGetReadyView(let challenge):
GetReadyPageView(
onBegin: {
guard displayState != .displayingLiveness else { return }
displayState = .displayingLiveness
},
beginCheckButtonDisabled: false,
challenge: viewModel.challenge!
challenge: challenge
)
.onAppear {
DispatchQueue.main.async {
Expand Down Expand Up @@ -234,7 +236,8 @@ public struct FaceLivenessDetectorView: View {
for: .video,
completionHandler: { accessGranted in
guard accessGranted == true else { return }
displayState = .awaitingLivenessSession
guard let challenge = viewModel.challenge else { return }
displayState = .awaitingLivenessSession(challenge)
}
)

Expand All @@ -252,19 +255,37 @@ public struct FaceLivenessDetectorView: View {
case .restricted, .denied:
alertCameraAccessNeeded()
case .authorized:
displayState = .awaitingLivenessSession
guard let challenge = viewModel.challenge else { return }
displayState = .awaitingLivenessSession(challenge)
@unknown default:
break
}
}
}

enum DisplayState {
case awaitingLivenessSession
case displayingGetReadyView
enum DisplayState: Equatable {
case awaitingChallengeType
case awaitingLivenessSession(Challenge)
case displayingGetReadyView(Challenge)
case displayingLiveness
case awaitingCameraPermission
case awaitingChallengeType

static func == (lhs: DisplayState, rhs: DisplayState) -> Bool {
switch (lhs, rhs) {
case (.awaitingChallengeType, .awaitingChallengeType):
return true
case (let .awaitingLivenessSession(c1), let .awaitingLivenessSession(c2)):
return c1.type == c2.type && c1.version == c2.version
case (let .displayingGetReadyView(c1), let .displayingGetReadyView(c2)):
return c1.type == c2.type && c1.version == c2.version
case (.displayingLiveness, .displayingLiveness):
return true
case (.awaitingCameraPermission, .awaitingCameraPermission):
return true
default:
return false
}
}
}

enum InstructionState {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ class FaceLivenessDetectionViewModel: ObservableObject {
NotificationCenter.default.removeObserver(self)
}

func registerServiceEvents(onChallengeTypeReceived: @escaping () -> Void) {
func registerServiceEvents(onChallengeTypeReceived: @escaping (Challenge) -> Void) {
livenessService?.register(onComplete: { [weak self] reason in
self?.stopRecording()

Expand All @@ -117,7 +117,7 @@ class FaceLivenessDetectionViewModel: ObservableObject {
livenessService?.register(
listener: { [weak self] _challenge in
self?.challenge = _challenge
onChallengeTypeReceived()
onChallengeTypeReceived(_challenge)
},
on: .challenge)
}
Expand Down Expand Up @@ -186,10 +186,13 @@ class FaceLivenessDetectionViewModel: ObservableObject {

func initializeLivenessStream() {
do {
try livenessService?.initializeLivenessStream(
guard let livenessSession = livenessService as? FaceLivenessSession else {
throw FaceLivenessDetectionError.unknown
}

try livenessSession.initializeLivenessStream(
withSessionID: sessionID,
userAgent: UserAgentValues.standard().userAgentString,
challenges: nil
userAgent: UserAgentValues.standard().userAgentString
)
} catch {
DispatchQueue.main.async {
Expand Down Expand Up @@ -235,6 +238,8 @@ class FaceLivenessDetectionViewModel: ObservableObject {
videoStartTime: UInt64
) {
guard initialClientEvent == nil else { return }
guard let challenge else { return }

videoChunker.start()

let initialFace = FaceDetection(
Expand All @@ -253,8 +258,8 @@ class FaceLivenessDetectionViewModel: ObservableObject {
do {
try livenessService?.send(
.initialFaceDetected(event: _initialClientEvent,
challenge: .init(version: challenge?.version ?? "2.0.0",
type: challenge?.type ?? .faceMovementAndLightChallenge)),
challenge: .init(version: challenge.version,
type: challenge.type)),
eventDate: { .init() }
)
} catch {
Expand All @@ -272,7 +277,8 @@ class FaceLivenessDetectionViewModel: ObservableObject {
guard
let sessionConfiguration,
let initialClientEvent,
let faceMatchedTimestamp
let faceMatchedTimestamp,
let challenge
else { return }

let finalClientEvent = FinalClientEvent(
Expand All @@ -287,8 +293,8 @@ class FaceLivenessDetectionViewModel: ObservableObject {
do {
try livenessService?.send(
.final(event: finalClientEvent,
challenge: .init(version: challenge?.version ?? "2.0.0",
type: challenge?.type ?? .faceMovementAndLightChallenge)),
challenge: .init(version: challenge.version,
type: challenge.type)),
eventDate: { .init() }
)

Expand Down Expand Up @@ -383,8 +389,4 @@ class FaceLivenessDetectionViewModel: ObservableObject {
}
}

extension FaceLivenessDetectionViewModel: FaceDetectionSessionConfiguration {
func getFaceDetectionSessionConfiguration() -> FaceLivenessSession.SessionConfiguration? {
sessionConfiguration
}
}
extension FaceLivenessDetectionViewModel: FaceDetectionSessionConfigurationWrapper { }
Original file line number Diff line number Diff line change
Expand Up @@ -171,8 +171,9 @@ extension _LivenessViewController: FaceLivenessViewControllerPresenter {
}

func completeNoLightCheck() {
guard let faceGuideRect = self.faceGuideRect else { return }
self.viewModel.completeNoLightCheck(
faceGuide: self.faceGuideRect!
faceGuide: faceGuideRect
)
}
}
6 changes: 3 additions & 3 deletions Tests/FaceLivenessTests/LivenessTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ final class FaceLivenessDetectionViewModelTestCase: XCTestCase {
/// Then: The end state of this flow is `.faceMatched`
func testHappyPathToMatchedFace() async throws {
viewModel.livenessService = self.livenessService
viewModel.challenge = Challenge(version: "2.0.0", type: .faceMovementAndLightChallenge)

viewModel.livenessState.checkIsFacePrepared()
XCTAssertEqual(viewModel.livenessState.state, .pendingFacePreparedConfirmation(.pendingCheck))
Expand Down Expand Up @@ -103,16 +104,15 @@ final class FaceLivenessDetectionViewModelTestCase: XCTestCase {
XCTAssertEqual(faceDetector.interactions, [
"setResultHandler(detectionResultHandler:) (FaceLivenessDetectionViewModel)"
])
XCTAssertEqual(livenessService.interactions, [
"initializeLivenessStream(withSessionID:userAgent:challenges:)"
])
XCTAssertEqual(livenessService.interactions, [])
}

/// Given: A `FaceLivenessDetectionViewModel`
/// When: The viewModel is processes a single face result with a face distance less than the inital face distance
/// Then: The end state of this flow is `.recording(ovalDisplayed: false)`
func testTransitionToRecordingState() async throws {
viewModel.livenessService = self.livenessService
viewModel.challenge = Challenge(version: "2.0.0", type: .faceMovementAndLightChallenge)

let face = FaceLivenessSession.OvalMatchChallenge.Face(
distanceThreshold: 0.32,
Expand Down
2 changes: 1 addition & 1 deletion Tests/FaceLivenessTests/MockLivenessService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ extension MockLivenessService: LivenessService {
func initializeLivenessStream(
withSessionID sessionID: String,
userAgent: String,
challenges: [Challenge]?
challenges: [Challenge]
) throws {
interactions.append(#function)
onInitializeLivenessStream(sessionID, userAgent, challenges)
Expand Down

0 comments on commit 728a743

Please sign in to comment.