In this tutorial, I will be explaining how we can go pass data between SwiftUI and UIKit. It’s pretty straightforward to use observable and state objects when passing data between SwiftUI views. When it comes to UIKit, you cannot directly pass these objects. There is a lot more you need to do. To visualize, my UX will be as shown below.
I will declare an observable object in SwiftUI code. It will have a @published variable to show an error. This in turn will pop up an alert showing the error.
enum CameraResult {
case success
case denied
case restricted
case failedToGivePermission
case notDetermined
}
class CameraDetails: ObservableObject {
@Published var result = CameraResult.notDetermined
@Published var showError: Bool = false
}
Next we will write the SwiftUI view. This will have the VStack and the alert. It will also initialize the StateObject. You need to remember that, StateObject needs to be declared and initialized just once. In order to pass the object, you will be using ObservableObject.
struct CameraView: View {
@StateObject var cameraDetails = CameraDetails()
var body: some View {
VStack(alignment: .center) {
PreviewHolderView(cameraDetails: cameraDetails)
}
.alert(isPresented: $cameraDetails.showError) {
Alert(title: Text("Permission issues"), message: Text("We need access to your camera to record videos."), dismissButton: .default(Text("OK")))
}
.edgesIgnoringSafeArea(.all)
}
}
struct PreviewHolderView: View {
@ObservedObject var cameraDetails: CameraDetails
var body: some View {
ZStack{
PreviewUIViewRepresentable(cameraDetails: cameraDetails)
}
}
}
PreviewUIViewRepresentable is the struct used to interact with UIKit views. PreviewUIView is the UIView in UIKit. Its defined as shown below
class PreviewUIView : UIView {
init(delegate: CameraUIViewDelegate) {
super.init(frame: .zero)
self.delegate = delegate // initialize the delegate here
askForCameraPermission()
}
// method to call the delegate
func askForCameraPermission() {
guard self.delegate != nil else {
return
}
// call the delegate here
self.delegate?.cameraOpened(.success)
}
}
In the above class, we are defining a UIView in UIKit, and then setting a delegate when we open the camera. In order to access a UIView in SwiftUI, we need to do this via UIViewRepresentable class. This is as shown below
struct PreviewUIViewRepresentable : UIViewRepresentable {
@ObservedObject var cameraDetails: CameraDetails
func makeUIView(context: Context) -> PreviewUIView {
let previewUIView = PreviewUIView(delegate: context.coordinator)
previewUIView.delegate = context.coordinator
return previewUIView
}
func updateUIView(_ uiView: UIViewType, context: Context) {
print("inside updateuiview")
}
func makeCoordinator() -> Coordinator {
Coordinator(cameraDetails: cameraDetails)
}
typealias UIViewType = PreviewUIView
}
Note we are also declaring the cameraDetails as ObservedObject here. We are going to use it.
extension PreviewUIViewRepresentable {
class Coordinator: NSObject, CameraUIViewDelegate {
func cameraOpened(_ result: CameraResult) {
print("hello \(result)")
cameraDetails.showError = true
}
let cameraDetails: CameraDetails
init(cameraDetails: CameraDetails) {
self.cameraDetails = cameraDetails
}
}
}
SwiftUI and UIKit are two different worlds. If you want to interact between them, you need to use a Coordinator. This class basically acts as a middle man between these two worlds. Lets define it here
extension PreviewUIViewRepresentable {
class Coordinator: NSObject, CameraUIViewDelegate {
func cameraOpened(_ result: CameraResult) {
cameraDetails.showError = true
}
let cameraDetails: CameraDetails
init(cameraDetails: CameraDetails) {
self.cameraDetails = cameraDetails
}
}
}
As you noticed, we are passing the observable object in the init method. We initialize the coordinator in the UIViewRepresentable struct.
func makeCoordinator() -> Coordinator {
Coordinator(cameraDetails: cameraDetails)
}
Also in the PreviewUIViewRepresentable struct, we pass the coordinator as the delegate.
let previewUIView = PreviewUIView(delegate: context.coordinator)
In this way you can interact between both the worlds.
Leave a Reply