ondelva

← Blog

Opening a Third-Party Camera From the Lock Screen and Control Center

Camera Control, the Lock Screen and Control Center each need a different piece: an App Intent, a capture extension, and a control widget. Notes on which does what, living inside the capture extension, a control extension that grew from 560 KB to 31 MB, and a gallery icon that only updated after deleting the app.

Yunseon Hong4 min read

Part five of notes from building Thirdstop, an iOS camera app. Since iOS 18 a third-party camera can be opened the way the system camera is: from the Camera Control button, from the Lock Screen, from Control Center. Getting there took three separate pieces, and the documentation does not make it obvious which piece covers which entry point.

Measured on an iPhone 17 Pro with iOS 26.6.1. API details were checked against Apple’s documentation on 11 September 2026.

Each entry point needs a different piece

Where the user startsWhat you need
Camera Control button, phone unlockedCameraCaptureIntent
Camera Control button, phone lockeda capture extension (LockedCameraCapture)
Control Center, Lock Screen controls, Action buttona ControlWidget in a widget extension

It is easy to assume the intent alone will put the app in Control Center. It does not. The only place a CameraCaptureIntent shows up by itself is Settings, under Camera, then Camera Control.

The pieces do fit together, though. The same CameraCaptureIntent goes straight into ControlWidgetButton(action:), and the system decides whether to open the app or the locked capture extension depending on whether the phone is locked. You do not write that branch.

A small convenience: ControlWidget has a main(), so a control extension with a single control can put @main on it directly without a WidgetBundle.

Living inside the capture extension

The locked-screen extension runs with far less than the app has. Apple’s overview spells it out: no network, no App Group container, and the extension’s own container is erased when the system suspends it. The app hands it settings through the intent’s app context, which updateAppContext(_:) caps at 4 KB once encoded as JSON.

Some things still work from there. Saving to the photo library through PhotoKit works directly from the extension. Files meant for the app go in the session’s sessionContentURL. For anything the extension cannot finish, it asks the system to unlock the phone and passes the destination along as an NSUserActivity.

Two more things. If there is no camera view with an AVCaptureEventInteraction attached, the extension is terminated shortly after launch. And the extension point identifier, com.apple.securecapture, is not in the documentation. I found it in Xcode’s own template, Capture Extension.xctemplate.

The intent file must depend on nothing

The file that declares the intent has to be compiled into three targets: the app, the capture extension and the control extension. The first time, my intent file referred to one app type. That was enough for the control extension to pull in most of the app, and it went from 560 KB to 31 MB.

The fix is plain and a little tedious: the intent file references only system types and types defined in the same file.

Control glyphs have to be custom SF Symbols

Given a PDF or a PNG image, Control Center showed a question mark instead. The glyph has to be a symbol, and for a custom glyph that means a custom SF Symbol.

The symbol is also flattened to one color on the way in. fill-opacity came out as 1.0 regardless of what the file said. So there is no color and no transparency to design with, only shape.

The fastest way to find out whether a symbol file is acceptable is to let actool tell you. The smallest template that passed had the Baseline and Capline guides and the Regular-S, Regular-M and Regular-L glyphs, drawn as filled shapes with the even-odd rule rather than strokes. The output directory has to exist before you run it:

xcrun actool <name>.xcassets --compile <out> --platform iphoneos \
  --minimum-deployment-target 18.0 --output-partial-info-plist <plist>

For size, I rendered the glyph next to a system symbol, circle.circle, and compared. A glyph 1.13 times the cap height looked puny next to it. 1.45 times matched.

After fixing the glyph I reinstalled the app over the old build. The same control placed on the Lock Screen now drew the new glyph. The Control Center gallery kept drawing the old question mark.

I spent half a day suspecting the asset. The asset was fine. Only deleting the app and installing it fresh made the gallery pick up the new glyph. If you are changing a control’s icon, delete the app before you start doubting your files.

Leave the app name out of the control name

The Control Center gallery and the control tile both show the app name next to the control’s name. Call the control “AppName Camera” and people see “AppName AppName Camera”. The control’s own name only needs to say what it does.

Filed under Build log