Bird
Raised Fist0
iOS Swiftmobile~20 mins

Camera and photo library in iOS Swift - Mini App: Build & Ship

Choose your learning style10 modes available
Build: PhotoPickerScreen
This screen lets users take a photo with the camera or pick one from the photo library. The chosen photo is shown on the screen.
Target UI
-------------------------
| Photo Picker          |
|-----------------------|
| [Take Photo] [Gallery]|
|                       |
|                       |
|       [ImageView]     |
|                       |
|                       |
-------------------------
Add two buttons side by side: 'Take Photo' and 'Gallery'.
When 'Take Photo' is tapped, open the camera to take a new photo.
When 'Gallery' is tapped, open the photo library to pick an existing photo.
Show the selected or taken photo below the buttons.
Handle permissions for camera and photo library access.
If the device has no camera, disable the 'Take Photo' button.
Starter Code
iOS Swift
import SwiftUI
import PhotosUI
import UIKit

struct PhotoPickerScreen: View {
    @State private var image: Image? = nil
    @State private var showCamera = false
    @State private var showPhotoLibrary = false

    var body: some View {
        VStack {
            HStack {
                Button("Take Photo") {
                    // TODO: Show camera
                }
                .disabled(!UIImagePickerController.isSourceTypeAvailable(.camera))

                Button("Gallery") {
                    // TODO: Show photo library
                }
            }
            .padding()

            Spacer()

            if let image = image {
                image
                    .resizable()
                    .scaledToFit()
                    .frame(maxWidth: 300, maxHeight: 300)
            } else {
                Text("No photo selected")
                    .foregroundColor(.gray)
            }

            Spacer()
        }
        .sheet(isPresented: $showCamera) {
            // TODO: Camera picker
        }
        .sheet(isPresented: $showPhotoLibrary) {
            // TODO: Photo library picker
        }
    }
}

struct PhotoPickerScreen_Previews: PreviewProvider {
    static var previews: some View {
        PhotoPickerScreen()
    }
}
Task 1
Task 2
Task 3
Task 4
Task 5
Solution
iOS Swift
import SwiftUI
import UIKit

struct PhotoPickerScreen: View {
    @State private var image: Image? = nil
    @State private var showCamera = false
    @State private var showPhotoLibrary = false
    @State private var inputImage: UIImage? = nil

    var body: some View {
        VStack {
            HStack {
                Button("Take Photo") {
                    showCamera = true
                }
                .disabled(!UIImagePickerController.isSourceTypeAvailable(.camera))
                .padding()

                Button("Gallery") {
                    showPhotoLibrary = true
                }
                .padding()
            }

            Spacer()

            if let image = image {
                image
                    .resizable()
                    .scaledToFit()
                    .frame(maxWidth: 300, maxHeight: 300)
            } else {
                Text("No photo selected")
                    .foregroundColor(.gray)
            }

            Spacer()
        }
        .sheet(isPresented: $showCamera) {
            ImagePicker(sourceType: .camera, selectedImage: $inputImage)
        }
        .sheet(isPresented: $showPhotoLibrary) {
            ImagePicker(sourceType: .photoLibrary, selectedImage: $inputImage)
        }
        .onChange(of: inputImage) { _ in loadImage() }
    }

    func loadImage() {
        guard let inputImage = inputImage else { return }
        image = Image(uiImage: inputImage)
    }
}

struct ImagePicker: UIViewControllerRepresentable {
    class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
        let parent: ImagePicker

        init(_ parent: ImagePicker) {
            self.parent = parent
        }

        func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
            if let uiImage = info[.originalImage] as? UIImage {
                parent.selectedImage = uiImage
            }
            picker.dismiss(animated: true)
        }

        func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
            picker.dismiss(animated: true)
        }
    }

    var sourceType: UIImagePickerController.SourceType
    @Binding var selectedImage: UIImage?

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    func makeUIViewController(context: Context) -> UIImagePickerController {
        let picker = UIImagePickerController()
        picker.delegate = context.coordinator
        picker.sourceType = sourceType
        return picker
    }

    func updateUIViewController(_ uiViewController: UIImagePickerController, context: Context) {}
}

struct PhotoPickerScreen_Previews: PreviewProvider {
    static var previews: some View {
        PhotoPickerScreen()
    }
}

This solution uses SwiftUI with a UIViewControllerRepresentable wrapper called ImagePicker to access the camera and photo library.

Two buttons let the user choose between taking a photo or picking from the gallery. The 'Take Photo' button is disabled if the device has no camera.

When a photo is selected or taken, it updates the inputImage state, which triggers loadImage() to convert it to a SwiftUI Image and display it.

The ImagePicker uses UIKit's UIImagePickerController to handle the camera and photo library, with a coordinator to manage delegate callbacks.

This approach handles permissions automatically by the system when accessing camera or photo library.

Final Result
Completed Screen
-------------------------
| Photo Picker          |
|-----------------------|
| [Take Photo] [Gallery]|
|                       |
|                       |
|     [Selected Image]  |
|                       |
|                       |
-------------------------
Tapping 'Take Photo' opens the camera to take a new picture.
Tapping 'Gallery' opens the photo library to pick an existing photo.
After selecting or taking a photo, the image appears below the buttons.
If the device has no camera, 'Take Photo' button is disabled.
Stretch Goal
Add a button to save the selected photo to the user's photo library.
💡 Hint
Use UIImageWriteToSavedPhotosAlbum to save the UIImage and show a confirmation alert.

Practice

(1/5)
1. What is the purpose of setting sourceType to .camera in UIImagePickerController?
easy
A. To select an existing photo from the photo library
B. To record a video using the device camera
C. To allow the user to take a new photo using the device camera
D. To open the device settings

Solution

  1. Step 1: Understand sourceType usage

    The sourceType property of UIImagePickerController defines where the image will come from.
  2. Step 2: Identify the effect of .camera

    Setting sourceType to .camera opens the device camera to take a new photo.
  3. Final Answer:

    To allow the user to take a new photo using the device camera -> Option C
  4. Quick Check:

    sourceType = .camera means take new photo [OK]
Hint: Camera sourceType means take a new photo [OK]
Common Mistakes:
  • Confusing .camera with .photoLibrary
  • Thinking .camera records video by default
  • Assuming .camera opens settings
2. Which of the following is the correct way to declare a UIImagePickerController and set its delegate in Swift?
easy
A. var picker = UIImagePickerController() picker.delegate = self()
B. let picker = UIImagePickerController() picker.delegate = self
C. let picker = UIImagePickerController picker.delegate = self()
D. var picker = UIImagePickerController picker.delegate = self

Solution

  1. Step 1: Correct initialization syntax

    In Swift, UIImagePickerController() requires parentheses to create an instance.
  2. Step 2: Assign delegate properly

    The delegate is assigned as self without parentheses because it is a property, not a function call.
  3. Final Answer:

    let picker = UIImagePickerController() picker.delegate = self -> Option B
  4. Quick Check:

    Initialize with () and assign delegate without () [OK]
Hint: Use parentheses for init, no parentheses for delegate [OK]
Common Mistakes:
  • Forgetting parentheses when creating UIImagePickerController
  • Using parentheses when assigning delegate
  • Declaring picker without parentheses
3. Given the following code snippet, what will be printed when a user selects a photo from the library?
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
    if let image = info[.originalImage] as? UIImage {
        print("Image selected")
    } else {
        print("No image found")
    }
}
medium
A. Nothing is printed
B. No image found
C. Compilation error
D. Image selected

Solution

  1. Step 1: Check the delegate method behavior

    The method imagePickerController(_:didFinishPickingMediaWithInfo:) is called after user picks a photo.
  2. Step 2: Understand the info dictionary

    The info dictionary contains the key .originalImage with the selected UIImage if successful.
  3. Final Answer:

    Image selected -> Option D
  4. Quick Check:

    Image exists in info dictionary after selection [OK]
Hint: If image exists in info, print "Image selected" [OK]
Common Mistakes:
  • Assuming image is nil after selection
  • Confusing delegate method names
  • Not casting info dictionary value properly
4. What is the error in the following Swift code that prevents the image picker from working correctly?
let picker = UIImagePickerController()
picker.sourceType = .camera
picker.delegate = self
present(picker, animated: true)
medium
A. Missing UIImagePickerControllerDelegate and UINavigationControllerDelegate conformance
B. Incorrect sourceType value
C. Delegate should be assigned after presenting picker
D. Cannot present picker without a navigation controller

Solution

  1. Step 1: Check delegate conformance requirements

    To use UIImagePickerController delegate methods, the class must conform to UIImagePickerControllerDelegate and UINavigationControllerDelegate.
  2. Step 2: Identify missing conformance

    If the class does not conform, delegate methods won't be called and picker won't work properly.
  3. Final Answer:

    Missing UIImagePickerControllerDelegate and UINavigationControllerDelegate conformance -> Option A
  4. Quick Check:

    Delegate protocols must be adopted for picker to work [OK]
Hint: Always conform to both delegate protocols [OK]
Common Mistakes:
  • Forgetting to add delegate protocols to class
  • Assigning delegate after presenting picker
  • Using wrong sourceType value
5. You want to allow users to either take a new photo or pick one from their library. Which approach correctly handles both options in Swift?
hard
A. Set sourceType dynamically based on user choice, then present a single UIImagePickerController
B. Use sourceType = .savedPhotosAlbum to cover both camera and library
C. Create two UIImagePickerController instances, one with .camera and one with .photoLibrary, and present both simultaneously
D. Use UIImagePickerController only for camera and a custom UI for photo library

Solution

  1. Step 1: Understand sourceType flexibility

    A single UIImagePickerController can have its sourceType set to either .camera or .photoLibrary before presenting.
  2. Step 2: Handle user choice before presenting picker

    Based on user selection, set the sourceType dynamically and present the picker once.
  3. Final Answer:

    Set sourceType dynamically based on user choice, then present a single UIImagePickerController -> Option A
  4. Quick Check:

    One picker, sourceType changes per user choice [OK]
Hint: Use one picker, change sourceType before presenting [OK]
Common Mistakes:
  • Trying to present two pickers at once
  • Using savedPhotosAlbum to cover camera
  • Mixing custom UI unnecessarily
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard