Home > Software design >  How to call a function on state change
How to call a function on state change

Time:09-03

I want to update the image in firebase storage when my State changes. Currently I call my function to upload the picked image to storage in the onAppear:

    @State var shouldShowImagePicker: Bool = false
    @State var image: UIImage?


Button(action: {
                                shouldShowImagePicker.toggle()
                            }) {
                            if let image = self.image {
                                Image(uiImage: image)
                                    .resizable()
                                    .frame(maxWidth: 300, maxHeight: 300)
                                    .scaledToFit()
                                    .onAppear() {
                                        persistImageToStorage()
                                    }
                                } else {
                                Text("Select Image")
                                    .frame(width: 300, height: 300)
                                }
                            }
.fullScreenCover(isPresented: $shouldShowImagePicker, onDismiss: nil) {
                            ImagePicker(image: $image)
                        }

This is the function to upload the image to storage:

private func persistImageToStorage() {
        let ref = FirebaseManager.shared.storage.reference(withPath: "image")
        guard let imageData = self.image?.jpegData(compressionQuality: 0.5)
            else { return }
        ref.putData(imageData, metadata: nil) { metadata, err in
            if let err = err {
                print(err)
                return
            }
            ref.downloadURL { url, err in
                if let err = err {
                        print(err)
                        return
                }
            }
        }
    }

This is my ImagePicker component:

import SwiftUI

struct ImagePicker: UIViewControllerRepresentable {

    @Binding var image: UIImage?

    private let controller = UIImagePickerController()

    func makeCoordinator() -> Coordinator {
        return Coordinator(parent: self)
    }

    class Coordinator: NSObject, UIImagePickerControllerDelegate, UINavigationControllerDelegate {

        let parent: ImagePicker

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

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

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

    }

    func makeUIViewController(context: Context) -> some UIViewController {
        controller.delegate = context.coordinator
        return controller
    }

    func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {

    }

}

Doing it this way works fine the first time I open the ImagePickerbut once an image is selected it does not call the function again since the onAppear does not call again. I tried setting onChange on the binding of the image but it seemed to not work for me.

CodePudding user response:

You could create a Binding that explicitly persists the image when the value is set:

.fullScreenCover(isPresented: $shouldShowImagePicker, onDismiss: nil) {
    let imageBinding = Binding(
        get: { image },
        set: { value in
            image = value
            persistImageToStorage()
        }
    )
    ImagePicker(image: imageBinding)
}

Note: Since imageBinding is already a Binding, you do not use a $ here.

Tested in Xcode 13.4.1


You said using onChange with image did not work for you, but I also found this to work:

ImagePicker(image: $image)
    .onChange(of: image) { _ in
        persistImageToStorage()
    }
  • Related