Sample Code
Learn how to implement Lens in an iOS application
Initialization & Enrollment - Application Delegate
We recommend initializing Lens only once per application launch, and since enrollment can take several seconds to complete (depending on connection), Lens should be initialized before presenting the camera. For these reasons, we recommend that you initialize Lens in your application delegate or in a view controller or other object which will be around for the lifecycle of your application
Call the Lens
designated initializer, passing your API Key. Since this call may throw, it should be wrapped in a do/try/catch
block:
import UIKit
import LensSDK
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var lens: Lens?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
// Initiates the enrollment process with your API key. It may take a few
// seconds, so it's best to do so early in your app's startup sequence.
do {
let lens = try Lens(with: "API_KEY")
self.lens = lens
// Use dependency injection to pass the camera to the camera controller.
if let cameraController = window?.rootViewController as? CameraViewController {
cameraController.camera = lens.camera
}
}
catch {
print("\(error.localizedDescription)")
}
return true
}
func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
// LensCamera uses background URLSession tasks, and this completion
// handler is required to be set.
LensCamera.shared?.activityCompletionHandler = completionHandler
}
}
Alternatively, consider SomeParentViewController
which is created early in the application's lifecycle and will be in memory for as long as the application is running:
import UIKit
import LensSDK
class SomeParentViewController: UIViewController {
private var lens: Lens?
override func viewDidLoad {
super.viewDidLoad()
do {
let lens = try Lens(with: "API_KEY")
self.lens = lens
}
catch {
print("\(error.localizedDescription)")
}
}
@IBAction func openCamera() {
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
guard let vc = mainStoryboard.instantiateViewController(withIdentifier: "CameraViewController") as? CameraViewController else { return }
vc.camera = self.lens?.camera
navigationController?.pushViewController(vc, animated: true)
}
}
Example Camera Implementation
import UIKit
import LensSDK
import Dispatch
class CameraViewController: UIViewController, LensCameraDelegate {
// `cameraPreviewView` is a subview of the view controller's view, and is passed to Lens
// in the `configureSession` call. It will host Lens' camera preview.
// The view should extend to the top and bottom safe area layout guides.
// Camera controls should be sibling views to `cameraPreviewView`.
@IBOutlet weak var cameraPreviewView: UIView!
// A button whose action will trigger photo capture.
@IBOutlet weak var captureButton: UIButton!
// A label which shows how long the current video has been recording.
@IBOutlet weak var durationLabel: UILabel!
// A reference to the Lens camera object.
private var camera: LensCamera?
// A custom UIView which acts as a visual aid to focus operations.
// FocusReticleView is a custom view, and is not part of the Lens SDK.
private let focusReticle = FocusReticleView()
private let reticleSize: CGFloat = 132
// DispatchSourceTimer is preferred over Timer because Timer requires a
// run loop and delivers its events with some latency.
private var timer: DispatchSourceTimer?
private let timerInterval = DispatchTimeInterval.seconds(1)
private let timerQueue = DispatchQueue(label: "com.yourcompany.videotimer")
private var durationInSeconds: Int = 0 {
didSet {
updateDurationLabel()
}
}
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.isNavigationBarHidden = true
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
guard let camera = self.camera else { return }
// pass the previewView and delegate to LensCamera
let targetSpec: Lens.TargetSpecOptions = .twoPointOne
try? camera.configureSession(previewView: cameraPreviewView, fillView: false, delegate: self, locationMode: .required, allowsImprecise: true, targetSpec: targetSpec)
// tell LensCamera to start the AVCapture session
camera.startCaptureSession()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
camera?.stopCaptureSession()
stopTimer()
}
deinit {
timer?.cancel()
}
// MARK: - IBAction
@IBAction func captureButtonAction(button: UIButton) {
guard let camera = self.camera else {
print("Unable to find LensCamera instance.")
return
}
switch lens.cameraMode {
case .photo: self.takePhoto()
case .video: self.recordVideo()
}
}
@IBAction func toggleMode(button: UIButton) {
guard let camera = self.camera else {
print("Unable to find LensCamera instance.")
return
}
switch camera.cameraMode {
case .photo: camera.cameraMode = .video
case .video: camera.cameraMode = .photo
}
}
@IBAction func switchCamera(button: UIButton) {
guard let camera = self.camera else {
print("Unable to find LensCamera instance.")
return
}
camera.changeCamera()
}
@IBAction func toggleFlash(button: UIButton) {
guard camera = self.camera else {
print("Unable to find LensCamera instance.")
return
}
camera.toggleFlash()
}
// MARK: - UIGestureRecognizer
@objc func handleTapToFocus(_ sender: UITapGestureRecognizer) {
guard let camera = self.camera else {
print("Unable to find LensCamera instance.")
return
}
focusReticle.removeFromSuperview()
let focusPoint = sender.location(in: cameraPreviewView)
self.cameraPreviewView.addSubview(focusReticle)
focusReticle.frame = CGRect(x: focusPoint.x - reticleSize/2,
y: focusPoint.y - reticleSize/2,
width: reticleSize,
height: reticleSize)
focusReticle.animateReticle(at: focusPoint)
camera.focus(with: sender)
}
@objc func handleZoom(_ sender: UIPinchGestureRecognizer) {
guard let camera = self.camera else {
print("Unable to find LensCamera instance.")
return
}
camera.zoom(with: sender)
}
// MARK: - private methods
private func takePhoto() {
takePhotoAnimation()
self.camera?.beginCapture()
}
private func takePhotoAnimation() {
DispatchQueue.main.async { [weak self] in
guard let preview = self?.cameraPreviewView else { return }
preview.layer.opacity = 0
UIView.animate(withDuration: 0.30) {
preview.layer.opacity = 1
}
}
}
private func updateDurationLabel() {
let minutes = durationInSeconds / 60 % 60
let seconds = durationInSeconds % 60
let durationString = String(format: "%02d:%02d", minutes, seconds)
// Updating the UI needs to happen on the main thread
DispatchQueue.main.async {
self.durationLabel.text = durationString
}
}
private func startTimer() {
timer = DispatchSource.makeTimerSource(flags: [], queue: timerQueue)
timer?.setEventHandler(handler: { [weak self] in
self?.durationInSeconds += 1
})
timer?.schedule(deadline: .now() + .seconds(1), repeating: .seconds(1), leeway: .nanoseconds(0))
timer?.resume()
}
private func stopTimer() {
timer?.cancel()
durationInSeconds = 0
}
private func recordVideo() {
guard let camera = self.camera else { return }
is camera.isRecordingVideo {
camera.stopVideoCapture()
} else {
camera.startVideoCapture()
}
}
// MARK: - LensCameraDelegate
func lensCameraDidFailWithError(_ error: LensCameraError) {
// handle the error appropriately
}
// MARK: - LensCameraDelegate - Photo capture
func lensCameraDidGeneratePhotoThumbnail(_ imageName: String, image: UIImage) {
// add thumbnail to a gallery
}
func lensCameraDidGenerateTruepic(_ imageName: String, image: Data) {
// save photo to host app's gallery or upload to host's server.
// Saving to the user's Photo Library will result in a loss of C2PA assertion and claim metadata.
// Converting the returned image data to a `UIImage` will result in a loss of C2PA assertion and claim metadata.
// `UIImage` does not support the features generated during image signing.
}
// MARK: - LensCameraDelegate - Video capture
func lensCameraDidStartRecordingVideo() {
self.startTimer()
// Other UI updates, dispatched to the main thread.
}
func lensCameraDidStopRecordingVideo() {
self.stopTimer()
// Other UI updates, dispatched to the main thread.
}
func lensCameraVideoRecordingFailed(error: Error?) {
// Present a warning to the user.
}
func lensCameraDidGenerateVideoThumbnail(fileName: String, image: UIImage, duration: TimeInterval?) {
// Add the thumbnail to a gallery.
}
func lensCameraDidGenerateTruepicVideo(_ url: URL) {
// Upload the signed video to a host server.
}
}
Updated 16 days ago