[BUG] ImagePicker renders a blank camera preview on iOS 27
Nobody has claimed this yet.
- Dominant language
- Swift
- Stars
- 8.2k
- Forks
- 495
- PR merge metrics
- No merged PRs in 30d
Description
ImagePicker renders a blank camera preview on iOS 27 — sourceType is applied too late
Summary
ImagePicker(...).sourceType(.camera) presents the camera UI (shutter button, flash and cancel
controls are all laid out correctly) over a completely black preview area. No frames are ever
delivered. This reproduces on every launch on iOS 27 beta.
The cause is that sourceType is never set on the controller that is actually presented — it is
only assigned afterwards, in updateUIViewController.
Cause
Sources/SwiftUIX/Intramodular/Images/ImagePicker.swift:
public func makeUIViewController(context: Context) -> UIViewControllerType {
UIImagePickerController().then {
$0.delegate = context.coordinator
}
}
public func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
context.coordinator.base = self
uiViewController.allowsEditing = allowsEditing
uiViewController.sourceType = sourceType // too late
...
}
UIImagePickerController() is created with the default sourceType of .photoLibrary, which on
iOS 14+ is backed by an out-of-process picker. sourceType is then mutated to .camera in
updateUIViewController, i.e. after the controller has already configured itself for the photo
library.
UIImagePickerController builds its capture session as part of being configured for .camera, so
a controller that was born as .photoLibrary never starts one. Apple's documentation has always
required the source type to be set before the picker is presented; earlier iOS releases happened to
tolerate the late switch, iOS 27 does not.
Steps to reproduce
Requires a real device — the simulator has no camera.
struct ContentView: View {
@State private var isShowingCamera = false
@State private var image: UIImage?
var body: some View {
Button("Take photo") { isShowingCamera = true }
.fullScreenCover(isPresented: $isShowingCamera) {
ImagePicker(image: $image, onCancel: { isShowingCamera = false })
.sourceType(.camera)
.edgesIgnoringSafeArea(.all)
}
}
}
Grant camera access, then tap "Take photo".
Expected: a live camera preview.
Actual: camera chrome over a black preview; no frames.
Notes
- The camera hardware and the permission grant are fine: an
AVCaptureSession-based view in the
same app on the same device shows a live preview normally. - The photo library path (
sourceTypeleft at its.photoLibrarydefault) is unaffected, since
no transition happens in that case. ImagePicker.swiftis unchanged between 0.2.3 and 0.3.1, so this is not a recent regression in
SwiftUIX — it is long-standing code that iOS 27 stopped tolerating.
Suggested fix
Set the source type (and the camera-only properties that depend on it) while creating the
controller, and stop mutating sourceType in updateUIViewController:
public func makeUIViewController(context: Context) -> UIViewControllerType {
- UIImagePickerController().then {
- $0.delegate = context.coordinator
- }
+ UIImagePickerController().then {
+ $0.delegate = context.coordinator
+
+ if UIImagePickerController.isSourceTypeAvailable(sourceType) {
+ $0.sourceType = sourceType
+ }
+
+ if $0.sourceType == .camera {
+ $0.cameraDevice = cameraDevice ?? .rear
+ }
+ }
}
public func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
context.coordinator.base = self
uiViewController.allowsEditing = allowsEditing
- uiViewController.sourceType = sourceType
if let mediaTypes = mediaTypes, uiViewController.mediaTypes != mediaTypes {
uiViewController.mediaTypes = mediaTypes
}
-
- if uiViewController.sourceType == .camera {
- uiViewController.cameraDevice = cameraDevice ?? .rear
- }
}
Because sourceType and cameraDevice are only meaningful before presentation, moving them into
makeUIViewController is also closer to what UIImagePickerController documents. If keeping the
modifiers reactive matters, the alternative is to key the representable on sourceType so SwiftUI
rebuilds the controller when it changes, rather than mutating a live one.
I am carrying this as an app-side replacement for now. Happy to open a PR if the approach looks
right.
Environment
- SwiftUIX 0.3.1 (
e1754664a6105529b7978489164720185317c29e) - Xcode 26.4.1 (17E202), Swift 6.3.1
- Deployment target iOS 16.0
- Reproduced on a device running iOS 27 beta
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with Sources/SwiftUIX/Intramodular/Images/ImagePicker.swift, comparing makeUIViewController with updateUIViewController and the reported sourceType and cameraDevice assignments. Apply the lifecycle correction described in the issue, then verify the provided camera reproduction on a real iOS 27 device; done means a live camera preview while the photo-library path remains unaffected.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- swift
- Domain
- mobile-dev
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 78/100