Heads up! We're sunsetting this forum to streamline our community space.

join us on Discord for support, updates, and all things Snap AR! See you there!
Sign up for our live Camera Kit office hour on July 17 @ 9:30am (PT) / 4:30pm (GMT)

How To Prefetch Lenses

Hey guys, we would like to know the best way to prefetch lenses before the camera screen is opened.

Our current problem: it takes 3-7 sec for lenses to load so it is hurting the user experience. Users have to wait on the camera screen with the loading animation and nothing happening.

What API can we use to make this happen? What documentation can help us? We tried reading through everything that's available but haven't been able to find a solution. Please help. Thank you.

Best Answer

  • arashp
    arashp Posts: 52 🔥🔥
    edited September 2022 #2 Answer ✓

    Thank you for posting this question in the forum @Hangoo!

    Our API does let you listen for which lenses have been updated and then prefetch them. For future reference, here are the steps to do that on both platforms, starting with iOS.

    iOS

    To listen for changes to lenses that have been updated, conform the class that manages your camera and lens stack to LensRepositoryGroupObserver. An example implementation is provided in the sample app, CameraController.repository(_:didUpdateLenses:forGroupID:)

    open func repository(_ repository: LensRepository, didUpdateLenses lenses: [Lens], forGroupID groupID: String) {
    // prefetch lens content (don't prefetch bundled since content is local already)
        if !groupID.contains(SCCameraKitLensRepositoryBundledGroup) {
            // the object returned here can be used to cancel the ongoing prefetch operation if need be
            _ = cameraKit.lenses.prefetcher.prefetch(lenses: lenses, completion: nil)
            for lens in lenses {
                cameraKit.lenses.prefetcher.addStatusObserver(self, lens: lens)
            }
        }
    
        DispatchQueue.main.async { [weak self] in
            guard let self = self else { return }
            let lenses = self.groupIDs.flatMap {
                self.cameraKit.lenses.repository.lenses(groupID: $0)
            }
    
            self.uiDelegate?.cameraController(self, updatedLenses: lenses)
        }
    }
    

    You may also want the UI to reflect the fact that a lens is being prefetched, for example, by showing an activity indicator. This can be done by conforming the camera controller to SCCameraKitLensPrefetcherObserver. See CameraController.prefetcher(_:didUpdate:status:) for an example implementation.

    Android

    The same thing can be achieved on Android. Here is a code block that demonstrates how the Prefetcher exposed from the LensesComponent can be used to prefetch lenses

    // content of select list of lenses on demand.
    var lensesPrefetch = Closeable {}
    Closeable { lensesPrefetch.close() }.addTo(closeOnDestroy)
    rootLayout.findViewById<Button>
    
    (R.id.lenses_prefetch_button).setOnClickListener {
        session.lenses.repository.observe(Available(*lensGroups)) {available ->
            available.whenHasSome { lenses ->
            // Cancel any running prefetch operation before submitting new one
            lensesPrefetch.close()
            // Prefetch available lenses content async
            lensesPrefetch = session.lenses.prefetcher.run(lenses) { success ->
                Log.d(TAG, "Finished prefetch of [${lenses.size}] lenses with   
                    success: $success")
                }
            }
        }.addTo(closeOnDestroy)
    }
    

    If you are using CameraActivity then you can use this:

    CameraActivity.Configuration.WithLenses(
        lensGroupIds = LENS_GROUP_IDS,
        prefetchLensByIdPattern = "\\S+"
    )
    

    Please let us know if you have any follow up questions!

Answers

Categories