Are images stored in the state?

I am trying to implement a feature that allows a user to set their profile picture.

I am wrapping a UIImagePIckerController using a UIViewControllerRepresentable.

My Coordinator class is handling the delegate method didFinishPickingMediaWithInfo which returns the image picked.

Currently, I send this image to the reducer via an action to be stored in the state.

What is the best practice for a situation like this? Is it preferred to instead write the image to a URL and send that back instead? Since now my state is exposed to the UIImage type.

//  ImagePicker.swift

import SwiftUI

import ComposableArchitecture

struct ImagePicker: UIViewControllerRepresentable {
  @Environment(\.presentationMode) var presentationMode
  
  let viewStore: ViewStore<ImagePickerState, ImagePickerAction>
  
  func makeUIViewController(
    context: UIViewControllerRepresentableContext<ImagePicker>
  ) -> UIImagePickerController {
    let picker = UIImagePickerController()
    picker.delegate = context.coordinator
    return picker
  }
  
  func updateUIViewController(
    _ uiViewController: UIImagePickerController,
    context: UIViewControllerRepresentableContext<ImagePicker>
  ) {
    
  }
  
  func makeCoordinator() -> Coordinator {
    Coordinator(self)
  }
  
  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.viewStore.send(.imagePicked(image: uiImage))
      }
      
      parent.presentationMode.wrappedValue.dismiss()
    }
  }
}

public struct ImagePickerState: Equatable {
  var showingImagePicker: Bool
  var image: UIImage?
}

public enum ImagePickerAction: Equatable {
  case setSheet(isPresented: Bool)
  case imagePicked(image: UIImage)
}

public struct ImagePickerEnvironment {
  
}

public let imagePickerReducer = Reducer<ImagePickerState, ImagePickerAction, ImagePickerEnvironment> { state, action, environment in
  switch action {
  case .setSheet(isPresented: true):
    state.showingImagePicker = true
    return .none
    
  case .setSheet(isPresented: false):
    state.showingImagePicker = false
    return .none
    
  case let .imagePicked(image):
    state.image = image
    return .none

I store the UIKit types in State as well. I don't see any wrong doing in this. Your state is backing a view, and if you has to display a user picked image, so be it ¯\_(ツ)_/¯

It may be better to store it as Data so the state is more context-independent? If you wanted to be as “pure” as possible at least, I think state should be completely unaware of how its content is being rendered by any views.

I was asking myself that as well. I build a wrapper around uiimage which has data: Data property for the sake of conforming to Codable. It also has an id which is nice because the app stores multiple images and also has a .remove event.

But images are really big and I thought about storing the URL and maybe a thumbnail version instead since I feel it blocks the app

2 Likes
Terms of Service

Privacy Policy

Cookie Policy