iOS Setup

Learn how to implement Lens in an iOS application

Lens is a camera framework which captures C2PA-compliant verified photos and videos. After successful capture, photos contain assertions (such as location, date and time, used to verify the provenance of the image), and are cryptographically signed.

The camera itself has no user interface elements, allowing you to customize the look and feel to match your app's existing style. The SDK provides API for interacting with the camera's controls; initiating capture, turning flash on and off, etc.

Getting Started

Requirements

To build a project using Lens SDK the minimum requirements are:

  • Lens iOS API key. Truepic requires the following information for production keys:
    • App name - The name of the application as listed in the Apple App Store.
    • App ID Prefix (or Team ID) - To locate, log in to developer.apple.com > Click on Certificates, IDs & Profiles > Identifiers
    • Bundle ID - To locate, click your bundle ID. This opens the Certificates, Identifiers & Profiles page. Look for App ID Prefix.
  • iOS Deployment Target 14.0+
  • Xcode 13.0+
  • Swift 5.0+
  • LensSDK framework library

Xcode

Import the Framework library

  1. Drag the LensSDK.xcframework folder into your Xcode project directory under Frameworks.
  2. When building to a Device, ensure that the Targets > General > Frameworks, Libraries, and Embedded Content setting is set to Embed > Embed & Sign
  3. Set Enable Bitcode to No (only necessary in Xcode 14 and earlier).
    This can be set in Targets > Build Settings > Build Options > Enable Bitcode > No

Application

Permissions

Lens needs user permissions to function: one for the camera, one for the microphone (optional), and another for location when in use. To facilitate this, the following property list keys must be added to your app's Info.plist. Either:

  1. Add the relevant Privacy key from the Information Property List dropdown in Xcode, or
  2. Manually edit the XML by right clicking on Info.plist and selecting Open As > Source Code.

Lens validates it has the necessary permissions and will issue an error to your application if the permissions have not been obtained.

Camera

KeyValue (example)
Privacy - Camera Usage DescriptionAPPNAME requires access to the camera in order to capture verified photos.

or

<key>NSCameraUsageDescription</key>
<string>APPNAME requires access to the camera in order to capture verified photos.</string>

Microphone

KeyValue (example)
Privacy - Microphone Usage DescriptionAPPNAME will use the microphone when recording videos or audio.

or

<key>NSMicrophoneUsageDescription</key>
<string>APPNAME will use the microphone when recording videos or audio.</string>

If microphone permissions are denied, videos will be captured without audio.

Location When In Use

KeyValue (example)
Privacy - Location When In Use Usage DescriptionAPPNAME uses Location Services to validate the accuracy of your image's location.

or

<key>NSLocationWhenInUseUsageDescription</key>
<string>APPNAME requires access to your location in order to validate the accuracy of your image's location.</string>

Recommendation for Initializing Lens

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

Implementation

This section covers basic implementation and another section covers additional features.

Lens Class

The Lens class provides access to the main functions of the SDK.

  • LensCamera: used to capture photos, videos, and audio. Media captured by the camera is automatically cryptographically signed. Access via the camera property.
  • LensSigner: used to sign media brought into your app's environment by other means, for example: importing media from the user's file system. Access via the signer property.
  • LensVerifier: used to verify C2PA content on signed media. Access to the verifier is via the verifier convenience property, but can also be accessed by LensVerifier's public initializer.

Camera Preview & Delegate Setup

  1. Start by importing the SDK in your App Delegate, or other long-lived class, and start enrollment using your API key. In the example below, we use the application delegate and then use dependency injection to pass our reference to lens to the next controller. You can alternatively delay the initialization of Lens until later in the application lifecycle, but make sure the object that owns the Lens class is one that will be long-lived.
import LensSDK

var lens: Lens

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
    do {
        let lens = try Lens(with: "API_KEY")
        self.lens = lens

        // Use dependency injection to pass the camera and signer to the camera controller.
        if let cameraController = window?.rootViewController as? CameraViewController {
            cameraController.camera = lens.camera
            cameraController.signer = lens.signer
        }
    }
    catch {
        print("\(error.localizedDescription)")
    } 

    return true
}
  1. Import the SDK in your view controller and implement the required LensCameraDelegate methods:
import LensSDK

class CameraViewController: UIViewController {
    var camera: LensCamera?
    var signer: LensSigner?

    ...
}

extension CameraViewController: LensCameraDelegate {
    func lensCameraDidFailWithError(_ error: LensCameraError) {
        // Called when an error is encountered during camera setup or configuration.
        // - Parameter error: the error thrown or passed from the internal frameworks.
    }
    
    func lensCameraDidGeneratePhotoThumbnail(_ imageName: String, _ image: UIImage) {
        // Called when the preview thumbnail has been generated.
        // - Parameter imageName: the name is composed of a UUID and a ".jpg" extension.
        // - Parameter image: A `UIImage` object with the smaller preview image.
        // The thumbnail has no C2PA claims or assertions, and is not cryptographically signed.
    }
    
    func lensCameraDidGenerateTruepic(_ imageName: String, _ truepic: Data) {
        // Called when the final signed image has been generated.
        //  - Parameter imageName:the same file name returned by the initial capture.
        //  - Parameter truepic: a block of data containing the signed and C2PA-compliant image data.
    }
    
    func lensCameraDidDisableCapture(for reason: LensCaptureDisabledReason) {
        // Called when a device configuration is encountered which requires that camera capture be disabled.
        // - Parameter reason: an instance of `LensCaptureDisabledReason`.
        // You should update the state of your camera controls to reflect this.
    }
    
    func lensCameraDidEnableCapture() {
        // Called when a previously encountered configuration has been resolved, permitting camera capture.
        // You should update the state of your camera controls to reflect this.
    }
}
  1. Initialize the Lens SDK and configure the camera session:
func configureCamera() {
    // Instantiate camera
    guard let camera = self.camera else {
        print("Unable to instantiate Lens Camera.")
        return
    }

    // Pass a UIView object to the to SDK to display a camera preview view. 
    // Setting fillView to true will fill the preview view, false will resize to fit within the preview view.
    do {
        try camera.configureSession(previewView: self.cameraPreviewView, fillView: false, delegate: self, locationMode: LensLocationMode.optional, allowsImprecise: false)
    } catch {
        // handle error
      	print("\(error.localizedDescription)")	
    }
}

The delegate can be any object that conforms to LensCameraDelegate.

Camera Capture Setup

Camera Mode

The default capture mode of Lens is photo. This can be changed by setting the following property:

final public var cameraMode: LensSDK.LensCameraMode

where LensCameraMode is the enum:

public enum LensCameraMode {
    case photo
    case video
    case audio
}

Photo Capture

Capturing photos with Lens camera is triggered by calling the beginCapture() method, typically within an IBAction:

@IBAction func photoCaptureTapped(button: UIButton) {
    guard let camera = self.camera else {
        print("Unable to find Lens Camera instance.")
        return
    }
    camera.beginCapture()
}

After the photo has been taken, but prior to C2PA processing and signing, Lens will return a UIImage containing a thumbnail of the photo in the following delegate method:

func lensCameraDidFinishInitialCapture(_ imageName: String, _ image: UIImage)
  • imageName - a UUID string

Once the capture has been signed, the following delegate method will be called:

func lensCameraDidGenerateTruepic(_ imageName: String, _ truepic: Data)

The final signed image is returned as a Data object to preserve C2PA assertions and metadata.

Saving the signed image to the iOS Photo Library is not recommended since UIKit classes do not support the metadata and features generated during image signing. Saving to your application's Documents directory using the Data.write(to: url) API is ideal.

Video Capture

To record a video, call the startVideoCapture method. To stop an in-progress recording, call stopVideoCapture.

@IBAction func videoCaptureTapped(button: UIButton) {
    guard let camera = self.camera else {
        print("Unable to find Lens Camera instance.")
        return
    }
    if camera.isRecordingVideo {
        camera.stopVieoCapture()
    } else {
        camera.startVideoCapture()
    }
}

When recording and C2PA-signing completes, the following delegate method will be called:

func lensCameraDidGenerateTruepicVideo(_ url: URL)
  • url - the location on disk to which the signed video is written.

If recording completes, but processing and signing fails, the following delegate method will be called:

func lensCameraDidGenerateUnsignedVideo(_ url: URL)
  • url - an unsigned video file path

While there are no restrictions on file size or duration to record verified videos and upload them to your server, there is currently a file size limit of 100 MB per video file uploaded to the Lens API for processing. This may not be necessary for your use case, but if so, we recommend limiting recording to under 45 seconds per video.

Handle errors

Errors will be surfaced to your app via the delegate method:

func lensCameraFailedWithError(_ error: LensCameraError) {}

where enum LensError is defined as:

ErrorDescription
enrollmentErrorAn error occurred within the device enrollment process.
Check that your API key is being passed properly, and that the App Attest service has been configured on your app's web server.
Check attestation logs for any errors.
timestampErrorAn error occurred when requesting a trusted timestamp.
cameraSessionFailedAn error occurred which caused the currently configured camera session to fail.
imageSigningFailedAn error occurred which caused the image signing process to fail.
airplaneModeDevice is in airplane mode. Airplane mode must be disabled to allow capture.
cameraPermissionDeniedPermission to the camera was denied.
internalErrorSome other error occurred. Check the localizedDescription for more information.

Troubleshooting

"Device is not secure" error in Xcode

Good news, it is not expected that the camera will ever allow you to take photos while connected to Xcode, due to our security measures that prevent this. Instead, you'll need to test on a disconnected device:

  1. use Xcode to build the app and deploy it to a device.
  2. Once the app starts, stop it from running in Xcode and disconnect.
  3. Force kill the app and restart it. The camera should work.

Certificate errors, or photos aren't being taken

This error can occur if your App ID does not exactly match what we had on file when we issued your iOS API key. The App ID is a combination of the App ID Prefix (or Team ID) and bundle ID, joined by a period, for example XZBPL6P2LU.com.epic.analysis. We can help you check to make sure the App ID you are working with matches what we have registered for that key. If we need to update it, we can do so right away, without you having to change your key. If you need to manage more than one App ID, we can issue a key for each.

HTTP errors

In your logs, you may see ATS failed system trust and Connection 11: system TLS Trust evaluation errors. Lens makes calls to our time stamping authority (TSA) using HTTP. We must whitelist those host names in the application to allow the network calls to be successfully made. To do this, add these suggested plist values:

<key>NSAppTransportSecurity</key>
<dict>
	<key>NSExceptionDomains</key>
	<dict>
		<key>ra.truepic.com</key>
		<dict>
			<key>NSExceptionAllowsInsecureHTTPLoads</key>
			<true/>
			<key>NSExceptionMinimumTLSVersion</key>
			<string>TLSv1.0</string>
			<key>NSExceptionRequiresForwardSecrecy</key>
			<false/>
			<key>NSIncludesSubdomains</key>
			<true/>
		</dict>
		<key>tsa.truepic.com</key>
		<dict>
			<key>NSExceptionAllowsInsecureHTTPLoads</key>
			<true/>
			<key>NSExceptionMinimumTLSVersion</key>
			<string>TLSv1.0</string>
			<key>NSExceptionRequiresForwardSecrecy</key>
			<false/>
			<key>NSIncludesSubdomains</key>
			<true/>
		</dict>
	</dict>
</dict>

Which should look like this: