Skip to content

Commit 819d2ff

Browse files
tobitechjumaallan
andauthored
Fix Selfie and Liveness Image Sizes, Fix Layout for Small Screen Devices (#266)
* Fixed Missing Document Type (#264) * added missing document type in document jobs * Update CHANGELOG.md * add new submission status icons for success and failed states. * remove setting initial value for user instruction. * handle multiple faces detected and throw an error * change order of face validator so that luminance is checked before head position. * reduce the stroke size of the face in bounds indicator. * remove done button and hide cancel button if submission was successful. * use new status icons in status view. * add a placeholder for api error 2214 * update selfie actions view parameters * restore camera manager to have accessible initializer. add camera session size preset for selfie capture image * code formatting and set line limit of 2 to user instructions title. * change capture screen layout to accommodate small screen devices. remove cancel delegate method from selfie result. * dismiss the selfie capture when submission is successful. --------- Co-authored-by: Juma Allan <allanjuma@gmail.com>
1 parent 8b7279e commit 819d2ff

27 files changed

+149
-73
lines changed

CHANGELOG.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
# Release Notes
22

3+
## Unreleased
4+
5+
* Fixed missing idType on Document Verification Jobs
6+
37
## 10.2.17
4-
### Added skipApiSubmission: Whether to skip api submission to SmileID and return only captured images on SmartSelfie enrollment, SmartSelfie authentic , Document verification and Enhanced DocV
8+
9+
* Added skipApiSubmission: Whether to skip api submission to SmileID and return only captured images on SmartSelfie enrollment, SmartSelfie authentic , Document verification and Enhanced DocV
510

611
## 10.2.16
12+
713
### Fixed
814
* Clear images on retry or start capture with the same jobId
915

Example/SmileID/Home/ProductCell.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,6 @@ struct ProductCell<Content: View>: View {
5757
}
5858
}
5959
}
60-
.environment(\.modalMode, $isPresented)
6160
}
6261
)
6362
}

Sources/SmileID/Classes/Camera/CameraManager.swift

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,7 @@ class CameraManager: NSObject, ObservableObject {
4444
@Published private(set) var status = Status.unconfigured
4545
private var orientation: Orientation
4646

47-
static let shared: CameraManager = CameraManager(orientation: .portrait)
48-
49-
private init(orientation: Orientation) {
47+
init(orientation: Orientation) {
5048
self.orientation = orientation
5149
super.init()
5250
sessionQueue.async {

Sources/SmileID/Classes/DocumentVerification/Model/DocumentCaptureViewModel.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ class DocumentCaptureViewModel: ObservableObject {
3939
@Published var documentImageToConfirm: Data?
4040
@Published var captureError: Error?
4141
@Published var isCapturing = false
42-
var cameraManager = CameraManager.shared
42+
@Published var cameraManager = CameraManager(orientation: .portrait)
4343

4444
init(
4545
knownAspectRatio: Double? = nil,

Sources/SmileID/Classes/DocumentVerification/Model/OrchestratedDocumentVerificationViewModel.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ class IOrchestratedDocumentVerificationViewModel<T, U: JobResult>: ObservableObj
165165
}
166166
let info = try LocalStorage.createInfoJsonFile(
167167
jobId: jobId,
168-
idInfo: IdInfo(country: countryCode),
168+
idInfo: IdInfo(country: countryCode, idType: documentType),
169169
documentFront: frontDocumentUrl,
170170
documentBack: backDocumentUrl,
171171
selfie: selfieFile,

Sources/SmileID/Classes/FaceDetector/EnhancedFaceDetector.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ enum FaceDetectorError: Error {
77
case unableToLoadSelfieModel
88
case invalidSelfieModelOutput
99
case noFaceDetected
10+
case multipleFacesDetected
1011
case unableToCropImage
1112
}
1213

@@ -71,6 +72,11 @@ class EnhancedFaceDetector: NSObject {
7172
return
7273
}
7374

75+
guard faceDetections.count == 1 else {
76+
self.resultDelegate?.faceDetector(self, didFailWithError: FaceDetectorError.multipleFacesDetected)
77+
return
78+
}
79+
7480
let convertedBoundingBox =
7581
self.viewDelegate?.convertFromMetadataToPreviewRect(
7682
rect: faceObservation.boundingBox) ?? .zero

Sources/SmileID/Classes/FaceDetector/FaceValidator.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,15 +89,15 @@ final class FaceValidator {
8989
}
9090
}
9191
return nil
92+
} else if !isAcceptableFaceQuality || !isAcceptableBrightness {
93+
return .goodLight
9294
} else if faceBoundsState == .detectedFaceOffCentre
9395
|| faceBoundsState == .detectedFaceNotWithinFrame {
9496
return .headInFrame
9597
} else if faceBoundsState == .detectedFaceTooSmall {
9698
return .moveCloser
9799
} else if faceBoundsState == .detectedFaceTooLarge {
98100
return .moveBack
99-
} else if !isAcceptableFaceQuality || !isAcceptableBrightness {
100-
return .goodLight
101101
}
102102
return nil
103103
}

Sources/SmileID/Classes/Helpers/SmileIDResourcesHelper.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ public class SmileIDResourcesHelper {
7777
public static var ConsentDocumentInfo = SmileIDResourcesHelper.image("ConsentDocumentInfo")!
7878
public static var ConsentPersonalInfo = SmileIDResourcesHelper.image("ConsentPersonalInfo")!
7979
public static var Loader = SmileIDResourcesHelper.image("Loader")!
80+
public static var Checkmark = SmileIDResourcesHelper.image("Checkmark")!
81+
public static var Xmark = SmileIDResourcesHelper.image("Xmark")!
8082

8183
/// Size of font.
8284
public static let pointSize: CGFloat = 16

Sources/SmileID/Classes/SelfieCapture/EnhancedSmartSelfieViewModel.swift

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import SwiftUI
66
public class EnhancedSmartSelfieViewModel: ObservableObject {
77
// MARK: Dependencies
88
private let motionManager = CMMotionManager()
9-
let cameraManager = CameraManager.shared
9+
let cameraManager = CameraManager(orientation: .portrait)
1010
let faceDetector = EnhancedFaceDetector()
1111
private let faceValidator = FaceValidator()
1212
var livenessCheckManager = LivenessCheckManager()
@@ -121,7 +121,6 @@ public class EnhancedSmartSelfieViewModel: ObservableObject {
121121
self.livenessCheckManager.delegate = self
122122

123123
self.faceValidator.setLayoutGuideFrame(with: faceLayoutGuideFrame)
124-
self.userInstruction = .headInFrame
125124

126125
livenessCheckManager.$lookLeftProgress
127126
.merge(
@@ -136,6 +135,9 @@ public class EnhancedSmartSelfieViewModel: ObservableObject {
136135
}
137136
.store(in: &subscribers)
138137

138+
if cameraManager.session.canSetSessionPreset(.vga640x480) {
139+
cameraManager.session.sessionPreset = .vga640x480
140+
}
139141
cameraManager.$status
140142
.receive(on: DispatchQueue.main)
141143
.filter { $0 == .unauthorized }
@@ -200,8 +202,8 @@ public class EnhancedSmartSelfieViewModel: ObservableObject {
200202
handleWindowSizeChanged(to: windowRect, edgeInsets: safeAreaInsets)
201203
case .onViewAppear:
202204
handleViewAppeared()
203-
case .jobProcessingDone:
204-
onFinished(callback: onResult)
205+
case .cancelSelfieCapture:
206+
handleCancelSelfieCapture()
205207
case .retryJobSubmission:
206208
handleSubmission()
207209
case .openApplicationSettings:
@@ -353,6 +355,21 @@ extension EnhancedSmartSelfieViewModel {
353355
guard let settingsURL = URL(string: UIApplication.openSettingsURLString) else { return }
354356
UIApplication.shared.open(settingsURL)
355357
}
358+
359+
private func handleCancelSelfieCapture() {
360+
invalidateSubmissionTask()
361+
UIApplication.shared.windows.first?.rootViewController?.dismiss(animated: true)
362+
}
363+
364+
private func dismissSelfieCapture() {
365+
UIApplication.shared.windows.first?.rootViewController?.dismiss(
366+
animated: true,
367+
completion: { [weak self] in
368+
guard let self else { return }
369+
self.onFinished(callback: self.onResult)
370+
}
371+
)
372+
}
356373
}
357374

358375
// MARK: FaceDetectorResultDelegate Methods
@@ -488,8 +505,6 @@ extension EnhancedSmartSelfieViewModel: SelfieSubmissionDelegate {
488505
)
489506
} else if let error = error {
490507
callback.didError(error: error)
491-
} else {
492-
callback.didCancel()
493508
}
494509
}
495510

@@ -502,6 +517,10 @@ extension EnhancedSmartSelfieViewModel: SelfieSubmissionDelegate {
502517
self.apiResponse = apiResponse
503518
self.selfieCaptureState = .processing(.success)
504519
}
520+
521+
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
522+
self.dismissSelfieCapture()
523+
}
505524
}
506525

507526
func submissionDidFail(

Sources/SmileID/Classes/SelfieCapture/SelfieViewModel.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public class SelfieViewModel: ObservableObject, ARKitSmileDelegate {
2828
private var localMetadata: LocalMetadata
2929
private let faceDetector = FaceDetector()
3030

31-
var cameraManager = CameraManager.shared
31+
var cameraManager = CameraManager(orientation: .portrait)
3232
var shouldAnalyzeImages = true
3333
var lastAutoCaptureTime = Date()
3434
var previousHeadRoll = Double.infinity
@@ -78,6 +78,9 @@ public class SelfieViewModel: ObservableObject, ARKitSmileDelegate {
7878
self.extraPartnerParams = extraPartnerParams
7979
self.localMetadata = localMetadata
8080

81+
if cameraManager.session.canSetSessionPreset(.vga640x480) {
82+
cameraManager.session.sessionPreset = .vga640x480
83+
}
8184
cameraManager.$status
8285
.receive(on: DispatchQueue.main)
8386
.filter { $0 == .unauthorized }

Sources/SmileID/Classes/SelfieCapture/SelfieViewModelAction.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ enum SelfieViewModelAction {
66
case windowSizeDetected(CGSize, EdgeInsets)
77

88
// Job Submission Actions
9-
case jobProcessingDone
9+
case cancelSelfieCapture
1010
case retryJobSubmission
1111

1212
// Others

Sources/SmileID/Classes/SelfieCapture/SmartSelfieResultDelegate.swift

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,3 @@ public protocol SmartSelfieResultDelegate {
1919
/// - Parameter error: The error returned from a failed selfie capture
2020
func didError(error: Error)
2121
}
22-
23-
extension SmartSelfieResultDelegate {
24-
/// The selfie capture operation was canceled.
25-
func didCancel() {}
26-
}

Sources/SmileID/Classes/SelfieCapture/View/EnhancedSelfieCaptureScreen.swift

Lines changed: 18 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,8 @@ public struct EnhancedSelfieCaptureScreen: View {
55
let showAttribution: Bool
66

77
private let faceShape = FaceShape()
8-
@Environment(\.modalMode) private var modalMode
9-
108
private(set) var originalBrightness = UIScreen.main.brightness
9+
private let cameraContainerHeight: CGFloat = 480
1110

1211
public var body: some View {
1312
GeometryReader { proxy in
@@ -20,14 +19,16 @@ public struct EnhancedSelfieCaptureScreen: View {
2019
selfieViewModel: viewModel
2120
)
2221
.cornerRadius(40)
22+
.frame(height: cameraContainerHeight)
2323

2424
RoundedRectangle(cornerRadius: 40)
2525
.fill(SmileID.theme.tertiary.opacity(0.8))
2626
.reverseMask(alignment: .top) {
2727
faceShape
2828
.frame(width: 250, height: 350)
29-
.padding(.top, 60)
29+
.padding(.top, 50)
3030
}
31+
.frame(height: cameraContainerHeight)
3132
VStack {
3233
ZStack {
3334
FaceBoundingArea(
@@ -55,7 +56,7 @@ public struct EnhancedSelfieCaptureScreen: View {
5556
}
5657
}
5758
}
58-
.selfieCaptureFrameBackground()
59+
.selfieCaptureFrameBackground(cameraContainerHeight)
5960
if showAttribution {
6061
Image(uiImage: SmileIDResourcesHelper.SmileEmblem)
6162
}
@@ -69,8 +70,9 @@ public struct EnhancedSelfieCaptureScreen: View {
6970
.reverseMask(alignment: .top) {
7071
faceShape
7172
.frame(width: 250, height: 350)
72-
.padding(.top, 60)
73+
.padding(.top, 50)
7374
}
75+
.frame(height: cameraContainerHeight)
7476
VStack {
7577
Spacer()
7678
UserInstructionsView(
@@ -84,33 +86,21 @@ public struct EnhancedSelfieCaptureScreen: View {
8486
SubmissionStatusView(processState: processingState)
8587
.padding(.bottom, 40)
8688
}
87-
.selfieCaptureFrameBackground()
89+
.selfieCaptureFrameBackground(cameraContainerHeight)
8890
if showAttribution {
8991
Image(uiImage: SmileIDResourcesHelper.SmileEmblem)
9092
}
91-
92-
Spacer()
93-
SelfieActionsView(
94-
processingState: processingState,
95-
retryAction: { viewModel.perform(action: .retryJobSubmission) },
96-
doneAction: {
97-
modalMode.wrappedValue = false
98-
viewModel.perform(action: .jobProcessingDone)
99-
}
100-
)
10193
}
102-
10394
Spacer()
104-
105-
Button {
106-
modalMode.wrappedValue = false
107-
viewModel.perform(action: .jobProcessingDone)
108-
} label: {
109-
Text(SmileIDResourcesHelper.localizedString(for: "Action.Cancel"))
110-
.font(SmileID.theme.button)
111-
.foregroundColor(SmileID.theme.error)
112-
}
95+
SelfieActionsView(
96+
captureState: viewModel.selfieCaptureState,
97+
retryAction: { viewModel.perform(action: .retryJobSubmission) },
98+
cancelAction: {
99+
viewModel.perform(action: .cancelSelfieCapture)
100+
}
101+
)
113102
}
103+
.padding(.vertical, 20)
114104
.navigationBarHidden(true)
115105
.onAppear {
116106
UIScreen.main.brightness = 1
@@ -143,11 +133,10 @@ public struct EnhancedSelfieCaptureScreen: View {
143133
}
144134

145135
extension View {
146-
func selfieCaptureFrameBackground() -> some View {
136+
func selfieCaptureFrameBackground(_ containerHeight: CGFloat) -> some View {
147137
self
148138
.shadow(color: .black.opacity(0.25), radius: 4, x: 0, y: 4)
149-
.frame(height: 520)
139+
.frame(height: containerHeight)
150140
.padding(.horizontal)
151-
.padding(.top, 40)
152141
}
153142
}

Sources/SmileID/Classes/SelfieCapture/View/FaceBoundingArea.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ struct FaceBoundingArea: View {
1616
faceShape
1717
.stroke(
1818
faceInBounds ? selfieCaptured ? .clear : SmileID.theme.success : SmileID.theme.error,
19-
style: StrokeStyle(lineWidth: 10)
19+
style: StrokeStyle(lineWidth: 8)
2020
)
21-
.frame(width: 270, height: 370)
21+
.frame(width: 260, height: 360)
2222

2323
if let guideAnimation = guideAnimation,
2424
showGuideAnimation {
Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,39 @@
11
import SwiftUI
22

33
struct SelfieActionsView: View {
4-
var processingState: ProcessingState
4+
var captureState: EnhancedSmartSelfieViewModel.SelfieCaptureState
55
var retryAction: () -> Void
6-
var doneAction: () -> Void
6+
var cancelAction: () -> Void
77

88
var body: some View {
99
VStack {
10-
Spacer()
11-
switch processingState {
12-
case .inProgress:
13-
EmptyView()
14-
case .success:
15-
SmileButton(title: "Action.Done") {
16-
doneAction()
17-
}
18-
case .error:
19-
SmileButton(title: "Confirmation.Retry") {
20-
retryAction()
10+
switch captureState {
11+
case .capturingSelfie:
12+
cancelButton
13+
case .processing(let processingState):
14+
switch processingState {
15+
case .inProgress:
16+
cancelButton
17+
case .success:
18+
EmptyView()
19+
case .error:
20+
SmileButton(title: "Confirmation.Retry") {
21+
retryAction()
22+
}
23+
cancelButton
2124
}
2225
}
2326
}
2427
.padding(.horizontal, 65)
2528
}
29+
30+
var cancelButton: some View {
31+
Button {
32+
cancelAction()
33+
} label: {
34+
Text(SmileIDResourcesHelper.localizedString(for: "Action.Cancel"))
35+
.font(SmileID.theme.button)
36+
.foregroundColor(SmileID.theme.error)
37+
}
38+
}
2639
}

Sources/SmileID/Classes/SelfieCapture/View/SelfiePreviewView.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ struct SelfiePreviewView: View {
77
Image(uiImage: image)
88
.resizable()
99
.aspectRatio(contentMode: .fill)
10-
.frame(height: 520)
10+
.frame(height: 480)
1111
.clipShape(.rect(cornerRadius: 40))
1212
}
1313
}

0 commit comments

Comments
 (0)