diff --git a/Sources/FaceLiveness/AV/OutputSampleBufferCapturer.swift b/Sources/FaceLiveness/AV/OutputSampleBufferCapturer.swift index 23f6defb..7328de04 100644 --- a/Sources/FaceLiveness/AV/OutputSampleBufferCapturer.swift +++ b/Sources/FaceLiveness/AV/OutputSampleBufferCapturer.swift @@ -10,11 +10,9 @@ import CoreImage class OutputSampleBufferCapturer: NSObject, AVCaptureVideoDataOutputSampleBufferDelegate { let faceDetector: FaceDetector - let videoChunker: VideoChunker - init(faceDetector: FaceDetector, videoChunker: VideoChunker) { + init(faceDetector: FaceDetector) { self.faceDetector = faceDetector - self.videoChunker = videoChunker } func captureOutput( @@ -22,8 +20,6 @@ class OutputSampleBufferCapturer: NSObject, AVCaptureVideoDataOutputSampleBuffer didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection ) { - videoChunker.consume(sampleBuffer) - guard let imageBuffer = sampleBuffer.imageBuffer else { return } diff --git a/Sources/FaceLiveness/Views/Liveness/FaceLivenessDetectionView.swift b/Sources/FaceLiveness/Views/Liveness/FaceLivenessDetectionView.swift index 320da58e..5d8c7046 100644 --- a/Sources/FaceLiveness/Views/Liveness/FaceLivenessDetectionView.swift +++ b/Sources/FaceLiveness/Views/Liveness/FaceLivenessDetectionView.swift @@ -28,7 +28,7 @@ public struct FaceLivenessDetectorView: View { sessionID: String, credentialsProvider: AWSCredentialsProvider? = nil, region: String, - disableStartView: Bool = false, + disableStartView: Bool = true, isPresented: Binding, onCompletion: @escaping (Result) -> Void ) { @@ -48,15 +48,6 @@ public struct FaceLivenessDetectorView: View { } let faceDetector = try! FaceDetectorShortRange.Model() - let faceInOvalStateMatching = FaceInOvalMatching( - instructor: Instructor() - ) - - let videoChunker = VideoChunker( - assetWriter: LivenessAVAssetWriter(), - assetWriterDelegate: VideoChunker.AssetWriterDelegate(), - assetWriterInput: LivenessAVAssetWriterInput() - ) let avCpatureDevice = AVCaptureDevice.DiscoverySession( deviceTypes: [.builtInWideAngleCamera], @@ -66,18 +57,13 @@ public struct FaceLivenessDetectorView: View { let captureSession = LivenessCaptureSession( captureDevice: .init(avCaptureDevice: avCpatureDevice), - outputDelegate: OutputSampleBufferCapturer( - faceDetector: faceDetector, - videoChunker: videoChunker - ) + outputDelegate: OutputSampleBufferCapturer(faceDetector: faceDetector) ) self._viewModel = StateObject( wrappedValue: .init( faceDetector: faceDetector, - faceInOvalMatching: faceInOvalStateMatching, captureSession: captureSession, - videoChunker: videoChunker, closeButtonAction: { onCompletion(.failure(.userCancelled)) }, sessionID: sessionID ) @@ -88,7 +74,7 @@ public struct FaceLivenessDetectorView: View { sessionID: String, credentialsProvider: AWSCredentialsProvider? = nil, region: String, - disableStartView: Bool = false, + disableStartView: Bool = true, isPresented: Binding, onCompletion: @escaping (Result) -> Void, captureSession: LivenessCaptureSession @@ -108,16 +94,10 @@ public struct FaceLivenessDetectorView: View { return session } - let faceInOvalStateMatching = FaceInOvalMatching( - instructor: Instructor() - ) - self._viewModel = StateObject( wrappedValue: .init( faceDetector: captureSession.outputSampleBufferCapturer!.faceDetector, - faceInOvalMatching: faceInOvalStateMatching, captureSession: captureSession, - videoChunker: captureSession.outputSampleBufferCapturer!.videoChunker, closeButtonAction: { onCompletion(.failure(.userCancelled)) }, sessionID: sessionID ) @@ -135,9 +115,6 @@ public struct FaceLivenessDetectorView: View { ? DisplayState.displayingLiveness : DisplayState.displayingGetReadyView guard self.displayState != newState else { return } - let session = try await sessionTask.value - viewModel.livenessService = session - viewModel.registerServiceEvents() self.displayState = newState } catch { throw FaceLivenessDetectionError.accessDenied @@ -182,7 +159,6 @@ public struct FaceLivenessDetectorView: View { onCompletion(.success(())) case .encounteredUnrecoverableError(let error): let closeCode = error.webSocketCloseCode ?? .normalClosure - viewModel.livenessService?.closeSocket(with: closeCode) isPresented = false onCompletion(.failure(mapError(error))) default: diff --git a/Sources/FaceLiveness/Views/Liveness/FaceLivenessDetectionViewModel+FaceDetectionResultHandler.swift b/Sources/FaceLiveness/Views/Liveness/FaceLivenessDetectionViewModel+FaceDetectionResultHandler.swift index 99e92ee2..caf62307 100644 --- a/Sources/FaceLiveness/Views/Liveness/FaceLivenessDetectionViewModel+FaceDetectionResultHandler.swift +++ b/Sources/FaceLiveness/Views/Liveness/FaceLivenessDetectionViewModel+FaceDetectionResultHandler.swift @@ -30,111 +30,56 @@ extension FaceLivenessDetectionViewModel: FaceDetectionResultHandler { var normalizedFace = normalizeFace(face) normalizedFace.boundingBox = normalizedFace.boundingBoxFromLandmarks(ovalRect: ovalRect) - switch livenessState.state { - case .pendingFacePreparedConfirmation: - if face.faceDistance <= initialFaceDistanceThreshold { - DispatchQueue.main.async { - self.livenessState.awaitingRecording() - self.initializeLivenessStream() - } - DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { - self.livenessState.beginRecording() - } - return - } else { - DispatchQueue.main.async { - self.livenessState.faceNotPrepared(reason: .faceTooClose) - } - return - } - case .recording(ovalDisplayed: false): - drawOval(onComplete: { - self.sendInitialFaceDetectedEvent( - initialFace: normalizedFace.boundingBox, - videoStartTime: Date().timestampMilliseconds - ) - }) - case .recording(ovalDisplayed: true): - guard let sessionConfiguration = sessionConfiguration else { return } - let instruction = faceInOvalMatching.faceMatchState( - for: normalizedFace.boundingBox, - in: ovalRect, - challengeConfig: sessionConfiguration.ovalMatchChallenge - ) - - handleInstruction( - instruction, - colorSequences: sessionConfiguration.colorChallenge.colors - ) - case .awaitingFaceInOvalMatch: - guard let sessionConfiguration = sessionConfiguration else { return } - let instruction = faceInOvalMatching.faceMatchState( - for: normalizedFace.boundingBox, - in: ovalRect, - challengeConfig: sessionConfiguration.ovalMatchChallenge - ) - handleInstruction( - instruction, - colorSequences: sessionConfiguration.colorChallenge.colors - ) - default: break - - } - } - } - - func handleNoFaceFit(instruction: Instructor.Instruction, percentage: Double) { - self.livenessState.awaitingFaceMatch(with: instruction, nearnessPercentage: percentage) - if noFitStartTime == nil { - noFitStartTime = Date() - } - if let elapsedTime = noFitStartTime?.timeIntervalSinceNow, abs(elapsedTime) >= noFitTimeoutInterval { - handleSessionTimedOut() - } - } - - func handleNoFaceDetected() { - if noFitStartTime == nil { - noFitStartTime = Date() - } - if let elapsedTime = noFitStartTime?.timeIntervalSinceNow, abs(elapsedTime) >= noFitTimeoutInterval { - handleSessionTimedOut() - } - } - - func handleInstruction( - _ instruction: Instructor.Instruction, - colorSequences: [FaceLivenessSession.DisplayColor] - ) { - DispatchQueue.main.async { - switch instruction { - case .match: - self.livenessState.faceMatched() - self.faceMatchedTimestamp = Date().timestampMilliseconds - DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { - self.livenessViewControllerDelegate?.displayFreshness(colorSequences: colorSequences) - } - let generator = UINotificationFeedbackGenerator() - generator.notificationOccurred(.success) - self.noFitStartTime = nil - - case .tooClose(_, let percentage), - .tooFar(_, let percentage), - .tooFarLeft(_, let percentage), - .tooFarRight(_, let percentage): - self.handleNoFaceFit(instruction: instruction, percentage: percentage) - case .none: - self.handleNoFaceDetected() - } - } - } - - private func handleSessionTimedOut() { - noFitStartTime = nil - DispatchQueue.main.async { - self.livenessState - .unrecoverableStateEncountered(.timedOut) - self.captureSession.stopRunning() +// switch livenessState.state { +// case .pendingFacePreparedConfirmation: +// if face.faceDistance <= initialFaceDistanceThreshold { +// DispatchQueue.main.async { +// self.livenessState.awaitingRecording() +// self.initializeLivenessStream() +// } +// DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) { +// self.livenessState.beginRecording() +// } +// return +// } else { +// DispatchQueue.main.async { +// self.livenessState.faceNotPrepared(reason: .faceTooClose) +// } +// return +// } +// case .recording(ovalDisplayed: false): +// drawOval(onComplete: { +// self.sendInitialFaceDetectedEvent( +// initialFace: normalizedFace.boundingBox, +// videoStartTime: Date().timestampMilliseconds +// ) +// }) +// case .recording(ovalDisplayed: true): +// guard let sessionConfiguration = sessionConfiguration else { return } +// let instruction = faceInOvalMatching.faceMatchState( +// for: normalizedFace.boundingBox, +// in: ovalRect, +// challengeConfig: sessionConfiguration.ovalMatchChallenge +// ) +// +// handleInstruction( +// instruction, +// colorSequences: sessionConfiguration.colorChallenge.colors +// ) +// case .awaitingFaceInOvalMatch: +// guard let sessionConfiguration = sessionConfiguration else { return } +// let instruction = faceInOvalMatching.faceMatchState( +// for: normalizedFace.boundingBox, +// in: ovalRect, +// challengeConfig: sessionConfiguration.ovalMatchChallenge +// ) +// handleInstruction( +// instruction, +// colorSequences: sessionConfiguration.colorChallenge.colors +// ) +// default: break +// +// } } } } diff --git a/Sources/FaceLiveness/Views/Liveness/FaceLivenessDetectionViewModel+VideoSegmentProcessor.swift b/Sources/FaceLiveness/Views/Liveness/FaceLivenessDetectionViewModel+VideoSegmentProcessor.swift index c2ed2b39..9da08793 100644 --- a/Sources/FaceLiveness/Views/Liveness/FaceLivenessDetectionViewModel+VideoSegmentProcessor.swift +++ b/Sources/FaceLiveness/Views/Liveness/FaceLivenessDetectionViewModel+VideoSegmentProcessor.swift @@ -6,16 +6,16 @@ // import Foundation - -extension FaceLivenessDetectionViewModel: VideoSegmentProcessor { - func process(initalSegment: Data, currentSeparableSegment: Data) { - let chunk = chunk(initial: initalSegment, current: currentSeparableSegment) - sendVideoEvent(data: chunk, videoEventTime: .zero) - if !hasSentFinalVideoEvent, - case .completedDisplayingFreshness = livenessState.state { - DispatchQueue.global(qos: .default).asyncAfter(deadline: .now() + 0.9) { - self.sendFinalVideoEvent() - } - } - } -} +// +//extension FaceLivenessDetectionViewModel: VideoSegmentProcessor { +// func process(initalSegment: Data, currentSeparableSegment: Data) { +// let chunk = chunk(initial: initalSegment, current: currentSeparableSegment) +// sendVideoEvent(data: chunk, videoEventTime: .zero) +// if !hasSentFinalVideoEvent, +// case .completedDisplayingFreshness = livenessState.state { +// DispatchQueue.global(qos: .default).asyncAfter(deadline: .now() + 0.9) { +// self.sendFinalVideoEvent() +// } +// } +// } +//} diff --git a/Sources/FaceLiveness/Views/Liveness/FaceLivenessDetectionViewModel.swift b/Sources/FaceLiveness/Views/Liveness/FaceLivenessDetectionViewModel.swift index db1c2b87..a8ed6886 100644 --- a/Sources/FaceLiveness/Views/Liveness/FaceLivenessDetectionViewModel.swift +++ b/Sources/FaceLiveness/Views/Liveness/FaceLivenessDetectionViewModel.swift @@ -8,7 +8,6 @@ import Foundation import SwiftUI import AVFoundation -@_spi(PredictionsFaceLiveness) import AWSPredictionsPlugin fileprivate let videoSize: CGSize = .init(width: 480, height: 640) fileprivate let defaultNoFitTimeoutInterval: TimeInterval = 7 @@ -22,50 +21,31 @@ class FaceLivenessDetectionViewModel: ObservableObject { weak var livenessViewControllerDelegate: FaceLivenessViewControllerPresenter? let captureSession: LivenessCaptureSession var closeButtonAction: () -> Void - let videoChunker: VideoChunker let sessionID: String - var livenessService: LivenessService? let faceDetector: FaceDetector - let faceInOvalMatching: FaceInOvalMatching let challengeID: String = UUID().uuidString - var colorSequences: [ColorSequence] = [] var hasSentFinalVideoEvent = false var hasSentFirstVideo = false var layerRectConverted: (CGRect) -> CGRect = { $0 } - var sessionConfiguration: FaceLivenessSession.SessionConfiguration? var normalizeFace: (DetectedFace) -> DetectedFace = { $0 } var provideSingleFrame: ((UIImage) -> Void)? var cameraViewRect = CGRect.zero var ovalRect = CGRect.zero var faceGuideRect: CGRect! - var initialClientEvent: InitialClientEvent? var faceMatchedTimestamp: UInt64? - var noFitStartTime: Date? - - var noFitTimeoutInterval: TimeInterval { - if let sessionTimeoutMilliSec = sessionConfiguration?.ovalMatchChallenge.oval.ovalFitTimeout { - return TimeInterval(sessionTimeoutMilliSec/1_000) - } else { - return defaultNoFitTimeoutInterval - } - } init( faceDetector: FaceDetector, - faceInOvalMatching: FaceInOvalMatching, captureSession: LivenessCaptureSession, - videoChunker: VideoChunker, stateMachine: LivenessStateMachine = .init(state: .initial), closeButtonAction: @escaping () -> Void, sessionID: String ) { self.closeButtonAction = closeButtonAction - self.videoChunker = videoChunker self.livenessState = stateMachine self.sessionID = sessionID self.captureSession = captureSession self.faceDetector = faceDetector - self.faceInOvalMatching = faceInOvalMatching self.closeButtonAction = { [weak self] in guard let self else { return } @@ -76,7 +56,6 @@ class FaceLivenessDetectionViewModel: ObservableObject { } faceDetector.setResultHandler(detectionResultHandler: self) - videoChunker.assetWriterDelegate.set(segmentProcessor: self) NotificationCenter.default.addObserver( self, @@ -89,31 +68,6 @@ class FaceLivenessDetectionViewModel: ObservableObject { NotificationCenter.default.removeObserver(self) } - func registerServiceEvents() { - livenessService?.register(onComplete: { [weak self] reason in - self?.stopRecording() - - switch reason { - case .disconnectionEvent: - DispatchQueue.main.async { - self?.livenessState.complete() - } - case .unexpectedClosure: - DispatchQueue.main.async { - self?.livenessState - .unrecoverableStateEncountered(.socketClosed) - } - } - }) - - livenessService?.register( - listener: { [weak self] _sessionConfiguration in - self?.sessionConfiguration = _sessionConfiguration - }, - on: .challenge - ) - } - @objc func willResignActive(_ notification: Notification) { guard self.livenessState.state != .initial else { return } DispatchQueue.main.async { @@ -144,72 +98,7 @@ class FaceLivenessDetectionViewModel: ObservableObject { } } - func drawOval(onComplete: @escaping () -> Void) { - guard livenessState.state == .recording(ovalDisplayed: false), - let ovalParameters = sessionConfiguration?.ovalMatchChallenge.oval - else { return } - - let scaleRatio = cameraViewRect.width / videoSize.width - let rect = CGRect( - x: ovalParameters.boundingBox.x, - y: ovalParameters.boundingBox.y, - width: ovalParameters.boundingBox.width, - height: ovalParameters.boundingBox.height - ) - - let normalizedOvalRect = CGRect( - x: rect.minX * scaleRatio, - y: rect.minY * scaleRatio, - width: rect.width * scaleRatio, - height: rect.height * scaleRatio - ) - - livenessViewControllerDelegate?.drawOvalInCanvas(normalizedOvalRect) - DispatchQueue.main.async { - self.livenessState.ovalDisplayed() - onComplete() - } - ovalRect = normalizedOvalRect - } - - - func initializeLivenessStream() { - do { - try livenessService?.initializeLivenessStream( - withSessionID: sessionID, - userAgent: UserAgentValues.standard().userAgentString - ) - } catch { - DispatchQueue.main.async { - self.livenessState.unrecoverableStateEncountered(.couldNotOpenStream) - } - } - } - - func sendColorDisplayedEvent( - _ event: Freshness.ColorEvent - ) { - let freshnessEvent = FreshnessEvent( - challengeID: challengeID, - color: event.currentColor.rgb._values, - sequenceNumber: event.sequenceNumber, - timestamp: event.colorStartTime, - previousColor: event.previousColor.rgb._values - ) - - do { - try livenessService?.send( - .freshness(event: freshnessEvent), - eventDate: { .init() } - ) - } catch { - DispatchQueue.main.async { - self.livenessState.unrecoverableStateEncountered(.unknown) - } - } - } - - func boundingBox(for cgRect: CGRect, relativeTo canvas: CGRect) -> FaceLivenessSession.BoundingBox { + func boundingBox(for cgRect: CGRect, relativeTo canvas: CGRect) -> BoundingBox { .init( x: cgRect.minX / cameraViewRect.width, y: cgRect.minY / cameraViewRect.height, @@ -218,115 +107,6 @@ class FaceLivenessDetectionViewModel: ObservableObject { ) } - func sendInitialFaceDetectedEvent( - initialFace: CGRect, - videoStartTime: UInt64 - ) { - guard initialClientEvent == nil else { return } - videoChunker.start() - - let initialFace = FaceDetection( - boundingBox: boundingBox(for: initialFace, relativeTo: cameraViewRect), - startTimestamp: videoStartTime - ) - - let _initialClientEvent = InitialClientEvent( - challengeID: challengeID, - initialFaceLocation: initialFace, - videoStartTime: videoStartTime - ) - - initialClientEvent = _initialClientEvent - - do { - try livenessService?.send( - .initialFaceDetected(event: _initialClientEvent), - eventDate: { .init() } - ) - } catch { - DispatchQueue.main.async { - self.livenessState.unrecoverableStateEncountered(.unknown) - } - } - } - - func sendFinalEvent( - targetFaceRect: CGRect, - viewSize: CGSize, - faceMatchedEnd: UInt64 - ) { - guard - let sessionConfiguration, - let initialClientEvent, - let faceMatchedTimestamp - else { return } - - let finalClientEvent = FinalClientEvent( - sessionConfiguration: sessionConfiguration, - initialClientEvent: initialClientEvent, - videoSize: videoSize, - faceMatchedStart: faceMatchedTimestamp, - faceMatchedEnd: faceMatchedEnd, - videoEnd: Date().timestampMilliseconds - ) - - do { - try livenessService?.send( - .final(event: finalClientEvent), - eventDate: { .init() } - ) - - sendVideoEvent( - data: .init(), - videoEventTime: Date().timestampMilliseconds - ) - hasSentFinalVideoEvent = true - - } catch { - DispatchQueue.main.async { - self.livenessState.unrecoverableStateEncountered(.unknown) - } - } - } - - func sendFinalVideoEvent() { - sendFinalEvent( - targetFaceRect: faceGuideRect, - viewSize: videoSize, - faceMatchedEnd: Date().timestampMilliseconds - ) - - videoChunker.finish { [weak livenessViewControllerDelegate] image in - livenessViewControllerDelegate?.displaySingleFrame(uiImage: image) - } - } - - func handleFreshnessComplete(faceGuide: CGRect) { - DispatchQueue.main.async { - self.livenessState.completedDisplayingFreshness() - self.faceGuideRect = faceGuide - } - } - - func sendVideoEvent(data: Data, videoEventTime: UInt64) { - guard !hasSentFinalVideoEvent else { return } - let eventDate = Date() - let timestamp = eventDate.timestampMilliseconds - - let videoEvent = VideoEvent.init(chunk: data, timestamp: timestamp) - - do { - try livenessService?.send( - .video(event: videoEvent), - eventDate: { eventDate } - ) - } catch { - DispatchQueue.main.async { - self.livenessState.unrecoverableStateEncountered(.unknown) - } - } - } - private func generateLivenessError(from captureSessionError: Error) -> LivenessStateMachine.LivenessError { guard let captureSessionError = captureSessionError as? LivenessCaptureSessionError else { return .unknown } @@ -359,3 +139,22 @@ class FaceLivenessDetectionViewModel: ObservableObject { return data } } + +public struct BoundingBox: Codable { + public let x: Double + public let y: Double + public let width: Double + public let height: Double + + public init( + x: Double, + y: Double, + width: Double, + height: Double + ) { + self.x = x + self.y = y + self.width = width + self.height = height + } +} diff --git a/Sources/FaceLiveness/Views/Liveness/LivenessViewController.swift b/Sources/FaceLiveness/Views/Liveness/LivenessViewController.swift index 5e5111a7..dccdab00 100644 --- a/Sources/FaceLiveness/Views/Liveness/LivenessViewController.swift +++ b/Sources/FaceLiveness/Views/Liveness/LivenessViewController.swift @@ -132,15 +132,15 @@ extension _LivenessViewController: FaceLivenessViewControllerPresenter { height: UIScreen.main.bounds.height, view: self.freshnessView, onNewColor: { [weak self] colorEvent in - self?.viewModel.sendColorDisplayedEvent(colorEvent) + //self?.viewModel.sendColorDisplayedEvent(colorEvent) }, onComplete: { [weak self] in guard let self else { return } self.freshnessView.removeFromSuperview() - self.viewModel.handleFreshnessComplete( - faceGuide: self.faceGuideRect! - ) +// self.viewModel.handleFreshnessComplete( +// faceGuide: self.faceGuideRect! +// ) } ) } diff --git a/Sources/FaceLiveness/Views/Liveness/_FaceLivenessDetectionView.swift b/Sources/FaceLiveness/Views/Liveness/_FaceLivenessDetectionView.swift index 5113bf54..8f2dd588 100644 --- a/Sources/FaceLiveness/Views/Liveness/_FaceLivenessDetectionView.swift +++ b/Sources/FaceLiveness/Views/Liveness/_FaceLivenessDetectionView.swift @@ -45,10 +45,6 @@ struct _FaceLivenessDetectionView: View { } .padding() - InstructionContainerView( - viewModel: viewModel - ) - Spacer() } .padding([.leading, .trailing]) diff --git a/Tests/IntegrationTestApp/IntegrationTestApp.xcodeproj/project.pbxproj b/Tests/IntegrationTestApp/IntegrationTestApp.xcodeproj/project.pbxproj index cae7d428..7d96495a 100644 --- a/Tests/IntegrationTestApp/IntegrationTestApp.xcodeproj/project.pbxproj +++ b/Tests/IntegrationTestApp/IntegrationTestApp.xcodeproj/project.pbxproj @@ -10,7 +10,6 @@ 732CF8492A3183F3004D0BE3 /* FaceLiveness in Frameworks */ = {isa = PBXBuildFile; productRef = 732CF8482A3183F3004D0BE3 /* FaceLiveness */; }; 732CF84E2A31871D004D0BE3 /* MockLivenessCaptureSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 732CF84D2A31871D004D0BE3 /* MockLivenessCaptureSession.swift */; }; 732CF8502A3187E0004D0BE3 /* FaceLivenessDetectorView+Mock.swift in Sources */ = {isa = PBXBuildFile; fileRef = 732CF84F2A3187E0004D0BE3 /* FaceLivenessDetectorView+Mock.swift */; }; - 732CF8522A318F7D004D0BE3 /* mock.mov in Resources */ = {isa = PBXBuildFile; fileRef = 732CF8512A318F7D004D0BE3 /* mock.mov */; }; 7336965E2A312F17009448F0 /* ExampleLivenessViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 733696542A312F17009448F0 /* ExampleLivenessViewModel.swift */; }; 7336965F2A312F17009448F0 /* StartSessionView+PresentationState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 733696552A312F17009448F0 /* StartSessionView+PresentationState.swift */; }; 733696602A312F17009448F0 /* LivenessResultView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 733696562A312F17009448F0 /* LivenessResultView.swift */; }; @@ -30,13 +29,13 @@ 733696782A312FC3009448F0 /* amplifyconfiguration.json in Resources */ = {isa = PBXBuildFile; fileRef = 733696772A312FC3009448F0 /* amplifyconfiguration.json */; }; 733696892A31329A009448F0 /* LivenessIntegrationUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 733696882A31329A009448F0 /* LivenessIntegrationUITests.swift */; }; 735A62472A313B8F00837642 /* UIConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 735A62462A313B8E00837642 /* UIConstants.swift */; }; + 736458EE2BA2002F006A5B39 /* AWSAPIPlugin in Frameworks */ = {isa = PBXBuildFile; productRef = 736458ED2BA2002F006A5B39 /* AWSAPIPlugin */; }; + 736458F02BA2002F006A5B39 /* AWSCognitoAuthPlugin in Frameworks */ = {isa = PBXBuildFile; productRef = 736458EF2BA2002F006A5B39 /* AWSCognitoAuthPlugin */; }; + 736458F22BA2002F006A5B39 /* AWSPredictionsPlugin in Frameworks */ = {isa = PBXBuildFile; productRef = 736458F12BA2002F006A5B39 /* AWSPredictionsPlugin */; }; + 738F1ECD2BA9E2A400718C92 /* mock.mov in Resources */ = {isa = PBXBuildFile; fileRef = 738F1ECC2BA9E2A400718C92 /* mock.mov */; }; 73B8F4202A2D7A27004215B5 /* IntegrationTestApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 73B8F41F2A2D7A27004215B5 /* IntegrationTestApp.swift */; }; 73B8F4242A2D7A28004215B5 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 73B8F4232A2D7A28004215B5 /* Assets.xcassets */; }; 73B8F4282A2D7A28004215B5 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 73B8F4272A2D7A28004215B5 /* Preview Assets.xcassets */; }; - 73F5DACB2A312594004CD4FC /* AWSAPIPlugin in Frameworks */ = {isa = PBXBuildFile; productRef = 73F5DACA2A312594004CD4FC /* AWSAPIPlugin */; }; - 73F5DACD2A312594004CD4FC /* AWSCognitoAuthPlugin in Frameworks */ = {isa = PBXBuildFile; productRef = 73F5DACC2A312594004CD4FC /* AWSCognitoAuthPlugin */; }; - 73F5DACF2A312594004CD4FC /* AWSPluginsCore in Frameworks */ = {isa = PBXBuildFile; productRef = 73F5DACE2A312594004CD4FC /* AWSPluginsCore */; }; - 73F5DAD12A312594004CD4FC /* Amplify in Frameworks */ = {isa = PBXBuildFile; productRef = 73F5DAD02A312594004CD4FC /* Amplify */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -52,7 +51,6 @@ /* Begin PBXFileReference section */ 732CF84D2A31871D004D0BE3 /* MockLivenessCaptureSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockLivenessCaptureSession.swift; sourceTree = ""; }; 732CF84F2A3187E0004D0BE3 /* FaceLivenessDetectorView+Mock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FaceLivenessDetectorView+Mock.swift"; sourceTree = ""; }; - 732CF8512A318F7D004D0BE3 /* mock.mov */ = {isa = PBXFileReference; lastKnownFileType = video.quicktime; path = mock.mov; sourceTree = ""; }; 733696542A312F17009448F0 /* ExampleLivenessViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExampleLivenessViewModel.swift; sourceTree = ""; }; 733696552A312F17009448F0 /* StartSessionView+PresentationState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "StartSessionView+PresentationState.swift"; sourceTree = ""; }; 733696562A312F17009448F0 /* LivenessResultView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LivenessResultView.swift; sourceTree = ""; }; @@ -74,6 +72,9 @@ 733696882A31329A009448F0 /* LivenessIntegrationUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LivenessIntegrationUITests.swift; sourceTree = ""; }; 735A62462A313B8E00837642 /* UIConstants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIConstants.swift; sourceTree = ""; }; 735A62492A317F6000837642 /* amplify-ui-swift-liveness */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = "amplify-ui-swift-liveness"; path = ../..; sourceTree = ""; }; + 736458F62BA2029D006A5B39 /* amplify-swift */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = "amplify-swift"; path = "../../../amplify-swift"; sourceTree = ""; }; + 7383D51B2BA22AAF00588470 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 738F1ECC2BA9E2A400718C92 /* mock.mov */ = {isa = PBXFileReference; lastKnownFileType = video.quicktime; name = mock.mov; path = Views/mock.mov; sourceTree = ""; }; 73B8F41C2A2D7A27004215B5 /* IntegrationTestApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = IntegrationTestApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; 73B8F41F2A2D7A27004215B5 /* IntegrationTestApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntegrationTestApp.swift; sourceTree = ""; }; 73B8F4232A2D7A28004215B5 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; @@ -93,11 +94,10 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 73F5DACF2A312594004CD4FC /* AWSPluginsCore in Frameworks */, - 73F5DAD12A312594004CD4FC /* Amplify in Frameworks */, + 736458EE2BA2002F006A5B39 /* AWSAPIPlugin in Frameworks */, 732CF8492A3183F3004D0BE3 /* FaceLiveness in Frameworks */, - 73F5DACB2A312594004CD4FC /* AWSAPIPlugin in Frameworks */, - 73F5DACD2A312594004CD4FC /* AWSCognitoAuthPlugin in Frameworks */, + 736458F22BA2002F006A5B39 /* AWSPredictionsPlugin in Frameworks */, + 736458F02BA2002F006A5B39 /* AWSCognitoAuthPlugin in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -170,6 +170,7 @@ 735A62482A317F6000837642 /* Packages */ = { isa = PBXGroup; children = ( + 736458F62BA2029D006A5B39 /* amplify-swift */, 735A62492A317F6000837642 /* amplify-ui-swift-liveness */, ); name = Packages; @@ -198,14 +199,15 @@ 73B8F41E2A2D7A27004215B5 /* IntegrationTestApp */ = { isa = PBXGroup; children = ( + 7383D51B2BA22AAF00588470 /* Info.plist */, 732CF84A2A31866D004D0BE3 /* Extension */, 733696762A312F7C009448F0 /* AmplifyConfig */, 733696682A312F2D009448F0 /* Model */, 7336966B2A312F2D009448F0 /* Utilities */, + 738F1ECC2BA9E2A400718C92 /* mock.mov */, 733696532A312F17009448F0 /* Views */, 73B8F41F2A2D7A27004215B5 /* IntegrationTestApp.swift */, 73B8F4232A2D7A28004215B5 /* Assets.xcassets */, - 732CF8512A318F7D004D0BE3 /* mock.mov */, 73B8F4252A2D7A28004215B5 /* IntegrationTestApp.entitlements */, 73B8F4262A2D7A28004215B5 /* Preview Content */, ); @@ -262,11 +264,10 @@ ); name = IntegrationTestApp; packageProductDependencies = ( - 73F5DACA2A312594004CD4FC /* AWSAPIPlugin */, - 73F5DACC2A312594004CD4FC /* AWSCognitoAuthPlugin */, - 73F5DACE2A312594004CD4FC /* AWSPluginsCore */, - 73F5DAD02A312594004CD4FC /* Amplify */, 732CF8482A3183F3004D0BE3 /* FaceLiveness */, + 736458ED2BA2002F006A5B39 /* AWSAPIPlugin */, + 736458EF2BA2002F006A5B39 /* AWSCognitoAuthPlugin */, + 736458F12BA2002F006A5B39 /* AWSPredictionsPlugin */, ); productName = IntegrationTestApp; productReference = 73B8F41C2A2D7A27004215B5 /* IntegrationTestApp.app */; @@ -301,7 +302,6 @@ ); mainGroup = 73B8F4132A2D7A27004215B5; packageReferences = ( - 73F5DAC92A312594004CD4FC /* XCRemoteSwiftPackageReference "amplify-swift" */, ); productRefGroup = 73B8F41D2A2D7A27004215B5 /* Products */; projectDirPath = ""; @@ -325,9 +325,9 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 732CF8522A318F7D004D0BE3 /* mock.mov in Resources */, 73B8F4282A2D7A28004215B5 /* Preview Assets.xcassets in Resources */, 733696782A312FC3009448F0 /* amplifyconfiguration.json in Resources */, + 738F1ECD2BA9E2A400718C92 /* mock.mov in Resources */, 73B8F4242A2D7A28004215B5 /* Assets.xcassets in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -543,10 +543,11 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"IntegrationTestApp/Preview Content\""; - DEVELOPMENT_TEAM = D8BB58X7QJ; + DEVELOPMENT_TEAM = 4TQQ7884GF; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = IntegrationTestApp/Info.plist; INFOPLIST_KEY_NSCameraUsageDescription = "Liveness requires access to device camera."; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; @@ -582,11 +583,12 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"IntegrationTestApp/Preview Content\""; - DEVELOPMENT_TEAM = D8BB58X7QJ; + DEVELOPMENT_TEAM = 4TQQ7884GF; ENABLE_HARDENED_RUNTIME = YES; ENABLE_PREVIEWS = YES; ENABLE_TESTABILITY = YES; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = IntegrationTestApp/Info.plist; INFOPLIST_KEY_NSCameraUsageDescription = "Liveness requires access to device camera."; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphoneos*]" = YES; "INFOPLIST_KEY_UIApplicationSceneManifest_Generation[sdk=iphonesimulator*]" = YES; @@ -645,41 +647,22 @@ }; /* End XCConfigurationList section */ -/* Begin XCRemoteSwiftPackageReference section */ - 73F5DAC92A312594004CD4FC /* XCRemoteSwiftPackageReference "amplify-swift" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/aws-amplify/amplify-swift"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 2.0.0; - }; - }; -/* End XCRemoteSwiftPackageReference section */ - /* Begin XCSwiftPackageProductDependency section */ 732CF8482A3183F3004D0BE3 /* FaceLiveness */ = { isa = XCSwiftPackageProductDependency; productName = FaceLiveness; }; - 73F5DACA2A312594004CD4FC /* AWSAPIPlugin */ = { + 736458ED2BA2002F006A5B39 /* AWSAPIPlugin */ = { isa = XCSwiftPackageProductDependency; - package = 73F5DAC92A312594004CD4FC /* XCRemoteSwiftPackageReference "amplify-swift" */; productName = AWSAPIPlugin; }; - 73F5DACC2A312594004CD4FC /* AWSCognitoAuthPlugin */ = { + 736458EF2BA2002F006A5B39 /* AWSCognitoAuthPlugin */ = { isa = XCSwiftPackageProductDependency; - package = 73F5DAC92A312594004CD4FC /* XCRemoteSwiftPackageReference "amplify-swift" */; productName = AWSCognitoAuthPlugin; }; - 73F5DACE2A312594004CD4FC /* AWSPluginsCore */ = { - isa = XCSwiftPackageProductDependency; - package = 73F5DAC92A312594004CD4FC /* XCRemoteSwiftPackageReference "amplify-swift" */; - productName = AWSPluginsCore; - }; - 73F5DAD02A312594004CD4FC /* Amplify */ = { + 736458F12BA2002F006A5B39 /* AWSPredictionsPlugin */ = { isa = XCSwiftPackageProductDependency; - package = 73F5DAC92A312594004CD4FC /* XCRemoteSwiftPackageReference "amplify-swift" */; - productName = Amplify; + productName = AWSPredictionsPlugin; }; /* End XCSwiftPackageProductDependency section */ }; diff --git a/Tests/IntegrationTestApp/IntegrationTestApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Tests/IntegrationTestApp/IntegrationTestApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index c96856c0..433497e1 100644 --- a/Tests/IntegrationTestApp/IntegrationTestApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Tests/IntegrationTestApp/IntegrationTestApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,21 +1,12 @@ { "pins" : [ - { - "identity" : "amplify-swift", - "kind" : "remoteSourceControl", - "location" : "https://github.com/aws-amplify/amplify-swift", - "state" : { - "revision" : "90e7163cb7d1320ade541ec9b0029781d71a8a95", - "version" : "2.15.4" - } - }, { "identity" : "amplify-swift-utils-notifications", "kind" : "remoteSourceControl", "location" : "https://github.com/aws-amplify/amplify-swift-utils-notifications.git", "state" : { - "revision" : "f970384ad1035732f99259255cd2f97564807e41", - "version" : "1.1.0" + "revision" : "959eec669ba97c7d923b963c3e66ca8a0b2737f6", + "version" : "1.1.1" } }, { @@ -23,8 +14,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/aws-amplify/aws-appsync-realtime-client-ios.git", "state" : { - "revision" : "c7ec93dcbbcd8abc90c74203937f207a7fcaa611", - "version" : "3.1.1" + "revision" : "a08684c5004e2049c29f57a5938beae9695a1ef7", + "version" : "3.1.2" } }, { @@ -32,8 +23,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/awslabs/aws-crt-swift", "state" : { - "revision" : "6feec6c3787877807aa9a00fad09591b96752376", - "version" : "0.6.1" + "revision" : "0d0a0cf2e2cb780ceeceac190b4ede94f4f96902", + "version" : "0.26.0" } }, { @@ -41,17 +32,35 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/awslabs/aws-sdk-swift.git", "state" : { - "revision" : "24bae88a2391fe75da8a940a544d1ef6441f5321", - "version" : "0.13.0" + "revision" : "485501db8b0c57d6636c2b33a03dcd77ee0911f9", + "version" : "0.36.1" + } + }, + { + "identity" : "cwlcatchexception", + "kind" : "remoteSourceControl", + "location" : "https://github.com/mattgallagher/CwlCatchException.git", + "state" : { + "revision" : "3ef6999c73b6938cc0da422f2c912d0158abb0a0", + "version" : "2.2.0" + } + }, + { + "identity" : "cwlpreconditiontesting", + "kind" : "remoteSourceControl", + "location" : "https://github.com/mattgallagher/CwlPreconditionTesting.git", + "state" : { + "revision" : "2ef56b2caf25f55fa7eef8784c30d5a767550f54", + "version" : "2.2.1" } }, { "identity" : "smithy-swift", "kind" : "remoteSourceControl", - "location" : "https://github.com/awslabs/smithy-swift", + "location" : "https://github.com/smithy-lang/smithy-swift", "state" : { - "revision" : "7b28da158d92cd06a3549140d43b8fbcf64a94a6", - "version" : "0.15.0" + "revision" : "64e66f3e3ac07d4180c2460a7af6e4ba4b92e9cb", + "version" : "0.41.0" } }, { @@ -72,31 +81,13 @@ "version" : "4.0.4" } }, - { - "identity" : "swift-collections", - "kind" : "remoteSourceControl", - "location" : "https://github.com/apple/swift-collections", - "state" : { - "revision" : "937e904258d22af6e447a0b72c0bc67583ef64a2", - "version" : "1.0.4" - } - }, { "identity" : "swift-log", "kind" : "remoteSourceControl", "location" : "https://github.com/apple/swift-log.git", "state" : { - "revision" : "532d8b529501fb73a2455b179e0bbb6d49b652ed", - "version" : "1.5.3" - } - }, - { - "identity" : "xmlcoder", - "kind" : "remoteSourceControl", - "location" : "https://github.com/MaxDesiatov/XMLCoder.git", - "state" : { - "revision" : "b1e944cbd0ef33787b13f639a5418d55b3bed501", - "version" : "0.17.1" + "revision" : "e97a6fcb1ab07462881ac165fdbb37f067e205d5", + "version" : "1.5.4" } } ], diff --git a/Tests/IntegrationTestApp/IntegrationTestApp/Extension/FaceLivenessDetectorView+Mock.swift b/Tests/IntegrationTestApp/IntegrationTestApp/Extension/FaceLivenessDetectorView+Mock.swift index cc8bcbe7..46c1e22a 100644 --- a/Tests/IntegrationTestApp/IntegrationTestApp/Extension/FaceLivenessDetectorView+Mock.swift +++ b/Tests/IntegrationTestApp/IntegrationTestApp/Extension/FaceLivenessDetectorView+Mock.swift @@ -27,15 +27,8 @@ extension FaceLivenessDetectorView { let captureDevice = LivenessCaptureDevice(avCaptureDevice: avCaptureDevice) let faceDetector = try! FaceDetectorShortRange.Model() - - let videoChunker = VideoChunker( - assetWriter: LivenessAVAssetWriter(), - assetWriterDelegate: VideoChunker.AssetWriterDelegate(), - assetWriterInput: LivenessAVAssetWriterInput() - ) - let outputDelegate = OutputSampleBufferCapturer(faceDetector: faceDetector, videoChunker: videoChunker - ) + let outputDelegate = OutputSampleBufferCapturer(faceDetector: faceDetector) let inputUrl = Bundle.main.url(forResource: "mock", withExtension: "mov")! let captureSession = MockLivenessCaptureSession(captureDevice: captureDevice, outputDelegate: outputDelegate, inputFile: inputUrl) let detectorView = FaceLivenessDetectorView(sessionID: sessionID, region: region, isPresented: isPresented, onCompletion: onCompletion, captureSession: captureSession) diff --git a/Tests/IntegrationTestApp/IntegrationTestApp/Extension/MockLivenessCaptureSession.swift b/Tests/IntegrationTestApp/IntegrationTestApp/Extension/MockLivenessCaptureSession.swift index 248cf8a6..315f9bce 100644 --- a/Tests/IntegrationTestApp/IntegrationTestApp/Extension/MockLivenessCaptureSession.swift +++ b/Tests/IntegrationTestApp/IntegrationTestApp/Extension/MockLivenessCaptureSession.swift @@ -95,7 +95,6 @@ final class MockLivenessCaptureSession: LivenessCaptureSession { sampleTiming: &timingInfo, sampleBufferOut: &sampleBuffer) if let sampleBuffer = sampleBuffer { - self.outputSampleBufferCapturer?.videoChunker.consume(sampleBuffer) guard let imageBuffer = sampleBuffer.imageBuffer else { return } self.outputSampleBufferCapturer?.faceDetector.detectFaces(from: imageBuffer) diff --git a/Tests/IntegrationTestApp/IntegrationTestApp/Info.plist b/Tests/IntegrationTestApp/IntegrationTestApp/Info.plist index b72ec3ba..354a9b17 100644 --- a/Tests/IntegrationTestApp/IntegrationTestApp/Info.plist +++ b/Tests/IntegrationTestApp/IntegrationTestApp/Info.plist @@ -11,5 +11,10 @@ + NSAppTransportSecurity + + NSAllowsArbitraryLoads + +