Skip to content

Commit

Permalink
add lottie animation, product cells for selfie enrollment and selfie …
Browse files Browse the repository at this point in the history
…authentication with strict mode.
  • Loading branch information
tobitech committed Jul 22, 2024
1 parent 4080479 commit d823d16
Show file tree
Hide file tree
Showing 6 changed files with 237 additions and 37 deletions.
37 changes: 37 additions & 0 deletions Example/SmileID/Home/HomeView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,40 @@ struct HomeView: View {
)
}
),
ProductCell(
image: "smart_selfie_enroll",
name: "SmartSelfie™ Enrollment (Strict)",
onClick: {
viewModel.onProductClicked()
},
content: {
SmileID.smartSelfieEnrollmentScreen(
userId: viewModel.smartSelfieEnrollmentUserId,
jobId: viewModel.newJobId,
allowAgentMode: true,
useStrictMode: true,
delegate: SmartSelfieEnrollmentDelegate(
userId: viewModel.smartSelfieEnrollmentUserId,
onEnrollmentSuccess: viewModel.onSmartSelfieEnrollment,
onError: viewModel.didError
)
)
}
),
ProductCell(
image: "smart_selfie_authentication",
name: "SmartSelfie™ Authentication (Strict)",
onClick: {
viewModel.onProductClicked()
},
content: {
SmartSelfieAuthWithUserIdEntry(
initialUserId: viewModel.smartSelfieEnrollmentUserId,
useStrictMode: true,
delegate: viewModel
)
}
),
ProductCell(
image: "enhanced_kyc",
name: "Enhanced KYC",
Expand Down Expand Up @@ -165,14 +199,17 @@ struct SmartSelfieEnrollmentDelegate: SmartSelfieResultDelegate {

private struct SmartSelfieAuthWithUserIdEntry: View {
let initialUserId: String
var useStrictMode: Bool = false
let delegate: SmartSelfieResultDelegate

@State private var userId: String?

var body: some View {
if let userId {
SmileID.smartSelfieAuthenticationScreen(
userId: userId,
allowAgentMode: true,
useStrictMode: useStrictMode,
delegate: delegate
)
} else {
Expand Down
Binary file added Resources/si_anim_face.lottie
Binary file not shown.
15 changes: 15 additions & 0 deletions Sources/SmileID/Classes/SelfieCapture/SelfieViewModelV2.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import Foundation

public class SelfieViewModelV2: ObservableObject {
@Published var processingState: ProcessingState?

let useStrictMode: Bool

var cameraManager = CameraManager(orientation: .portrait)

init(
useStrictMode: Bool = false
) {
self.useStrictMode = useStrictMode
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import Foundation
import SwiftUI

/// Orchestrates the selfie capture flow - navigates between instructions, requesting permissions,
/// showing camera view, and displaying processing screen
public struct OrchestratedSelfieCaptureScreenV2: View {
public let allowAgentMode: Bool
public let showAttribution: Bool
public let showInstructions: Bool
public let onResult: SmartSelfieResultDelegate
@ObservedObject var viewModel: SelfieViewModelV2

@State private var acknowledgedInstructions = false
private var originalBrightness = UIScreen.main.brightness

public init(
userId: String,
jobId: String,
isEnroll: Bool,
allowNewEnroll: Bool,
allowAgentMode: Bool,
showAttribution: Bool,
showInstructions: Bool,
useStrictMode: Bool,
extraPartnerParams: [String: String],
skipApiSubmission: Bool,
onResult: SmartSelfieResultDelegate
) {
self.allowAgentMode = allowAgentMode
self.showAttribution = showAttribution
self.showInstructions = showInstructions
self.onResult = onResult
viewModel = SelfieViewModelV2()
}

public var body: some View {
if showInstructions, !acknowledgedInstructions {
SmartSelfieInstructionsScreen(showAttribution: showAttribution) {
acknowledgedInstructions = true
}
} else {
SelfieCaptureScreenV2()
}
}
}
Original file line number Diff line number Diff line change
@@ -1,27 +1,96 @@
import SwiftUI
import Lottie

public struct SelfieCaptureScreenV2: View {
@ObservedObject var viewModel = SelfieViewModelV2()
@State private var playbackMode: LottiePlaybackMode = LottiePlaybackMode.paused

public var body: some View {
VStack(spacing: 40) {
LottieView {
try await DotLottieFile.named("si_anim_face", bundle: SmileIDResourcesHelper.bundle)
}
.playing(loopMode: .autoReverse)
.frame(width: 80, height: 80)

struct SelfieCaptureScreenV2: View {
var body: some View {
VStack(spacing: 20) {
Image(systemName: "face.smiling")
.font(.largeTitle)
Text("Look up")
.font(SmileID.theme.header2)
.foregroundColor(.primary)
ZStack {
RoundedRectangle(cornerRadius: 25)
.stroke(Color.black, lineWidth: 10.0)
.stroke(Color.black, lineWidth: 20.0)
CameraView(cameraManager: viewModel.cameraManager)
.clipShape(.rect(cornerRadius: 25))
.onAppear {
viewModel.cameraManager.switchCamera(to: .front)
}
CornerShapes()
RoundedRectangle(cornerRadius: 25)
.foregroundColor(.black.opacity(0.2))
Ellipse()
.frame(width: 220, height: 280)
.blendMode(.destinationOut)
.foregroundColor(.white.opacity(0.8))
.cutout(Ellipse().scale(x: 0.8, y: 0.8))
}
.compositingGroup()
.frame(width: 300, height: 400)

Image(uiImage: SmileIDResourcesHelper.SmileEmblem)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
}

// swiftlint:disable identifier_name
@ViewBuilder func CornerShapes() -> some View {
VStack {
HStack {
// Top Left Corner
CornerShape()
.stroke(SmileID.theme.success, style: StrokeStyle(lineWidth: 5))
.frame(width: 40, height: 40)
.rotationEffect(.degrees(90))
.offset(x: -2.0, y: -2.0)
Spacer()
// Top Right Corner
CornerShape()
.stroke(SmileID.theme.success, style: StrokeStyle(lineWidth: 5))
.frame(width: 40, height: 40)
.rotationEffect(.degrees(180))
.offset(x: 2.0, y: -2.0)
}
Spacer()
HStack {
// Bottom Left Corner
CornerShape()
.stroke(SmileID.theme.success, style: StrokeStyle(lineWidth: 5))
.frame(width: 40, height: 40)
.offset(x: -2.0, y: 2.0)
Spacer()
// Bottom Right Corner
CornerShape()
.stroke(SmileID.theme.success, style: StrokeStyle(lineWidth: 5))
.frame(width: 40, height: 40)
.rotationEffect(.degrees(270))
.offset(x: 2.0, y: 2.0)
}
}
}
}

struct CornerShape: Shape {
let width: CGFloat = 40
let height: CGFloat = 40
let cornerRadius: CGFloat = 25

func path(in rect: CGRect) -> Path {
var path = Path()
path.move(to: CGPoint(x: 0, y: 0))
path.addLine(to: CGPoint(x: 0, y: height - cornerRadius))
path.addArc(
center: CGPoint(x: cornerRadius, y: height - cornerRadius),
radius: cornerRadius,
startAngle: .degrees(180),
endAngle: .degrees(90),
clockwise: true
)
path.addLine(to: CGPoint(x: width, y: height))
return path
}
}

Expand Down
86 changes: 60 additions & 26 deletions Sources/SmileID/Classes/SmileID.swift
Original file line number Diff line number Diff line change
Expand Up @@ -254,28 +254,45 @@ public class SmileID {
/// - showInstructions: Whether to deactivate capture screen's instructions for SmartSelfie.
/// - extraPartnerParams: Custom values specific to partners
/// - delegate: Callback to be invoked when the SmartSelfie™ Enrollment is complete.
public class func smartSelfieEnrollmentScreen(
@ViewBuilder public class func smartSelfieEnrollmentScreen(
userId: String = generateUserId(),
jobId: String = generateJobId(),
allowNewEnroll: Bool = false,
allowAgentMode: Bool = false,
showAttribution: Bool = true,
showInstructions: Bool = true,
useStrictMode: Bool = false,
extraPartnerParams: [String: String] = [:],
delegate: SmartSelfieResultDelegate
) -> some View {
OrchestratedSelfieCaptureScreen(
userId: userId,
jobId: jobId,
isEnroll: true,
allowNewEnroll: allowNewEnroll,
allowAgentMode: allowAgentMode,
showAttribution: showAttribution,
showInstructions: showInstructions,
extraPartnerParams: extraPartnerParams,
skipApiSubmission: false,
onResult: delegate
)
if useStrictMode {
OrchestratedSelfieCaptureScreenV2(
userId: userId,
jobId: jobId,
isEnroll: true,
allowNewEnroll: allowNewEnroll,
allowAgentMode: allowAgentMode,
showAttribution: showAttribution,
showInstructions: showInstructions,
useStrictMode: useStrictMode,
extraPartnerParams: extraPartnerParams,
skipApiSubmission: false,
onResult: delegate
)
} else {
OrchestratedSelfieCaptureScreen(
userId: userId,
jobId: jobId,
isEnroll: true,
allowNewEnroll: allowNewEnroll,
allowAgentMode: allowAgentMode,
showAttribution: showAttribution,
showInstructions: showInstructions,
extraPartnerParams: extraPartnerParams,
skipApiSubmission: false,
onResult: delegate
)
}
}

/// Perform a SmartSelfie™ Authentication
Expand All @@ -298,28 +315,45 @@ public class SmileID {
/// - showInstructions: Whether to deactivate capture screen's instructions for SmartSelfie.
/// - extraPartnerParams: Custom values specific to partners
/// - delegate: Callback to be invoked when the SmartSelfie™ Authentication is complete.
public class func smartSelfieAuthenticationScreen(
@ViewBuilder public class func smartSelfieAuthenticationScreen(
userId: String,
jobId: String = generateJobId(),
allowNewEnroll: Bool = false,
allowAgentMode: Bool = false,
showAttribution: Bool = true,
showInstructions: Bool = true,
useStrictMode: Bool = false,
extraPartnerParams: [String: String] = [:],
delegate: SmartSelfieResultDelegate
) -> some View {
OrchestratedSelfieCaptureScreen(
userId: userId,
jobId: jobId,
isEnroll: false,
allowNewEnroll: allowNewEnroll,
allowAgentMode: allowAgentMode,
showAttribution: showAttribution,
showInstructions: showInstructions,
extraPartnerParams: extraPartnerParams,
skipApiSubmission: false,
onResult: delegate
)
if useStrictMode {
OrchestratedSelfieCaptureScreenV2(
userId: userId,
jobId: jobId,
isEnroll: true,
allowNewEnroll: allowNewEnroll,
allowAgentMode: allowAgentMode,
showAttribution: showAttribution,
showInstructions: showInstructions,
useStrictMode: useStrictMode,
extraPartnerParams: extraPartnerParams,
skipApiSubmission: false,
onResult: delegate
)
} else {
OrchestratedSelfieCaptureScreen(
userId: userId,
jobId: jobId,
isEnroll: false,
allowNewEnroll: allowNewEnroll,
allowAgentMode: allowAgentMode,
showAttribution: showAttribution,
showInstructions: showInstructions,
extraPartnerParams: extraPartnerParams,
skipApiSubmission: false,
onResult: delegate
)
}
}

/// Perform a Document Verification
Expand Down

0 comments on commit d823d16

Please sign in to comment.