I have a SwiftUI view embedded inside Controller A using UIHostingController. Controller A is launched from Controller B. The SwiftUI view uses a ViewModel class. In the ViewModel, I have subscribed to some publishers and I want to send data to Controller A.
//This is the ViewModel file that contains a publisher to receive the video URL upon capture, and I am sending this URL to a controller using PassthroughSubject.
class GenericCameraViewViewModel: ObservableObject {
let xCamSession: XCamSession
var preview: some View {
xCamSession.interactivePreview()
}
private var subscription = Set<AnyCancellable>()
public var cameraEventSubject = PassthroughSubject<GenericCameraResult, Never>()
init(genericCameraConfiguration: GenericCameraConfiguation) {
// Prepare video album cover
xCamSession.videoFilePublisher
.receive(on: DispatchQueue.main)
.map {result -> URL? in
if case .success(let file) = result {
return file.path
} else {
return nil
}
}
.sink(receiveValue: { fileURL in
debugPrint("Receive file url")
self.cameraEventSubject.send(GenericCameraResult(event: .capturedVideo, images: nil, videoFile: fileURL))
// if self.shouldSendData {
// self.xCamSession.terminateSession({ result in
// self.cameraEventSubject.send(GenericCameraResult(event: .capturedVideo, images: nil, videoFile: fileURL))
// debugPrint("Receive file url")
//
// })
// }
})
.store(in: &subscription)
}
}
// a function in the controller to add SwiftUI view and subscribe the subject
// display the measure view as a view controller child
private func addAsAChild(){
//let config = GenericCameraConfiguation(captureMode: .photo,canCaputreMultiplePhotos: true, cameraPosition: .back,cameraPhotoFlash: .off,cameraVideoTorch: .auto)
let viewModel = GenericCameraViewViewModel(genericCameraConfiguration: configuration)
let videoContentView = GenericCameraView(viewModel: viewModel)
viewModel.cameraEventSubject
.receive(on: DispatchQueue.main) // Ensure sink runs on main thread
.sink(
receiveCompletion: { completion in
print("-- completion", completion)
self.cancellables.first?.cancel()
self.dismiss(animated: true)
},
receiveValue: {[weak self] result in
viewModel.shouldSendData = false
switch(result.event){
case .closePressed:
viewModel.cameraEventSubject.send(completion: .finished)
debugPrint("Close Button Pressed")
break
case .donePressed:
debugPrint("Done Button Pressed (result.images ?? [])")
viewModel.cameraEventSubject.send(completion: .finished)
let capturedImages = result.images ?? []
let arrImagePaths = self?.saveImages(images: capturedImages)
self?.mDelegate?.capturedImagesPath(capturedImages: arrImagePaths ?? [])
break;
case .capturedPhoto:
debugPrint("capturedPhoto Button Pressed (result.images ?? [])")
let capturedImages = result.images ?? []
let arrImagePaths = self?.saveImages(images: capturedImages)
self?.mDelegate?.capturedImagesPath(capturedImages: arrImagePaths ?? [])
case .capturedVideo:
debugPrint("capturedVideo Button Pressed (result.videoFile ?? URL.init(string: "https://cameraxaapp.com")!)")
self?.mDelegate?.capturedVideoUrl(videoUrl: result.videoFile)
//viewModel.cameraEventSubject.send(completion: .finished)
}
}
).store(in: &cancellables)
let hostingViewController = HostingController(rootView: videoContentView)
addChildWithView(hostingViewController)
}
To achieve this, I added a PassthroughSubject inside the ViewModel and subscribed to this subject inside Controller A. I am sending data using this subject within the publisher’s .sink method, and it works fine. However, when I open Controller A again, the .sink method of the publisher is called automatically.
Vishal Bhargava is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.