UIViewControllerRepresentable and CNContactPickerViewController

I've succesfully created a number of UIViewControllerRepresentable using various UIViewController, with one exception: CNContactPickerViewController. Everything I've tried has given me a blank white screen. Is this UIViewController special in some way that makes it currently incompatible?


import SwiftUI
import ContactsUI

// Minimal version
struct LookupContactViewController : UIViewControllerRepresentable {
    
    func makeUIViewController(context: Context) -> CNContactPickerViewController {
        let contactPickerVC = CNContactPickerViewController()
        contactPickerVC.delegate = context.coordinator
        return contactPickerVC
    }
    
    func makeCoordinator() -> Coordinator {
        return Coordinator()
    }

    func updateUIViewController(_ uiViewController: CNContactPickerViewController, context: Context) {
    }
    
    class Coordinator: NSObject {
    }
}

extension LookupContactViewController.Coordinator : CNContactPickerDelegate {

    func contactPickerDidCancel(_ picker: CNContactPickerViewController) {
        print("Cancelled!")
    }
    
    func contactPicker(_ picker: CNContactPickerViewController, didSelect contact: CNContact) {
        print("Chose: \(contact.givenName)")
    }
}

#if DEBUG
struct LookupContact_Previews : PreviewProvider {
    static var previews: some View {
        LookupContactViewController()
    }
}
#endif

Replies

Did you try an EKEventEditViewController with delegate?

Not the controller I want, but it works fine with UIViewControllerRepresentable.


import SwiftUI
import EventKitUI

struct EKEventEditVCR: UIViewControllerRepresentable {
    
    typealias UIViewControllerType = EKEventEditViewController
    
    func makeUIViewController(context: UIViewControllerRepresentableContext) -> EKEventEditVCR.UIViewControllerType {
        let controller = EKEventEditViewController()
        
        controller.delegate = context.coordinator
        return controller
    }
    
    func updateUIViewController(_ uiViewController: Self.UIViewControllerType, context: Self.Context) {
        
    }
    
    func makeCoordinator() -> EKEventEditVCR.Coordinator {
        return Coordinator()
    }    
    
    class Coordinator : NSObject, UINavigationControllerDelegate {
        
    }
        
}

#if DEBUG
struct EKEventEditVCR_Previews: PreviewProvider {
    static var previews: some View {
        EKEventEditVCR()
    }
}
#endif

I will definitely check this out. I did sometihng similar, but could not dismiss the view controlller.

Ah... that may be. But, EKEventEditViewController at least rendered.

And it appeared in my implementation, but I could not dismiss the view controller nor did the delegate work.

With a big thank you to you, I can say that with the help from your example and a bit more digging, I was able to get mine to work. Here's the code below.


import Foundation
import SwiftUI
import EventKitUI

let eventStore = EKEventStore()
struct EKEventWrapper: UIViewControllerRepresentable {

    typealias UIViewControllerType = EKEventEditViewController
    var theEvent = EKEvent.init(eventStore: eventStore)
    var coordinator = Coordinator()
    
    func makeUIViewController(context: UIViewControllerRepresentableContext<EKEventWrapper>) -> EKEventWrapper.UIViewControllerType {
        theEvent.startDate = Date()
        theEvent.endDate = Date()
        theEvent.title = "The Main Event!"
        let calendar = EKCalendar.init(for: .event, eventStore: eventStore)
        theEvent.calendar = calendar
        let controller = EKEventEditViewController()
        controller.event = theEvent
        controller.editViewDelegate = coordinator as EKEventEditViewDelegate
        return controller
    }

    func updateUIViewController(_ uiViewController: EKEventWrapper.UIViewControllerType, context: UIViewControllerRepresentableContext<EKEventWrapper>) {
        //
    }
    
    func makeCoordinator() -> EKEventWrapper.Coordinator {
        return Coordinator()
    }
      
    class Coordinator : NSObject, EKEventEditViewDelegate {
        func eventEditViewController(_ controller: EKEventEditViewController, didCompleteWith action: EKEventEditViewAction) {
            switch action {
            case .canceled:
                print("Canceled")
                controller.dismiss(animated: true, completion: nil)
            case .saved:
                print("Saved")
                controller.dismiss(animated: true, completion: nil)
            case .deleted:
                print("Deleted")
                controller.dismiss(animated: true, completion: nil)
            @unknown default:
                print("I shouldn't be here")
                controller.dismiss(animated: true, completion: nil)
            }
        }
    }
}

Well, I can dismiss it once, but have problems dismissing it the second time. ****.

I've fixed some things, but I still cannot dismiss it twice in my overall code.


import Foundation
import SwiftUI
import EventKitUI

let eventStore = EKEventStore()
struct EKEventWrapper: UIViewControllerRepresentable {

    typealias UIViewControllerType = EKEventEditViewController
    var theEvent = EKEvent.init(eventStore: eventStore)
    var coordinator = Coordinator()
    
    func makeUIViewController(context: UIViewControllerRepresentableContext<EKEventWrapper>) -> EKEventWrapper.UIViewControllerType {
        theEvent.startDate = Date()
        theEvent.endDate = Date()
        theEvent.title = "The Main Event!"
        let calendar = EKCalendar.init(for: .event, eventStore: eventStore)
        theEvent.calendar = calendar
        let controller = EKEventEditViewController()
        controller.event = theEvent
        controller.eventStore = eventStore
        controller.editViewDelegate = coordinator as EKEventEditViewDelegate
        return controller
    }

    func updateUIViewController(_ uiViewController: EKEventWrapper.UIViewControllerType, context: UIViewControllerRepresentableContext<EKEventWrapper>) {
        //
    }
    
    func makeCoordinator() -> EKEventWrapper.Coordinator {
        return Coordinator()
    }
      
    class Coordinator : NSObject, EKEventEditViewDelegate {
        func eventEditViewController(_ controller: EKEventEditViewController, didCompleteWith action: EKEventEditViewAction) {
            switch action {
            case .canceled:
                print("Canceled")
                controller.dismiss(animated: true, completion: nil)
            case .saved:
                print("Saved")
                do {
                    try controller.eventStore.save(controller.event!, span: .thisEvent, commit: true)
                }
                catch {
                    print("Problem saving event")
                }
                controller.dismiss(animated: true, completion: nil)
            case .deleted:
                print("Deleted")
                controller.dismiss(animated: true, completion: nil)
            @unknown default:
                print("I shouldn't be here")
                controller.dismiss(animated: true, completion: nil)
            }
        }
    }
}

Sorry to have hijacked the thread, but I got some inspiration, and some examples off of the internet. The thing doesn't work when presented modally, but does work in a VStack with a binding.


import Foundation
import SwiftUI
import EventKitUI

let eventStore = EKEventStore()
struct EKEventWrapper: UIViewControllerRepresentable {
    @Binding var isShown: Bool
    typealias UIViewControllerType = EKEventEditViewController
    var theEvent = EKEvent.init(eventStore: eventStore)
    
    func makeUIViewController(context: UIViewControllerRepresentableContext<EKEventWrapper>) -> EKEventEditViewController {
//        func makeUIViewController(context: UIViewControllerRepresentableContext<EKEventWrapper>) -> EKEventWrapper.UIViewControllerType {
        theEvent.startDate = Date()
        theEvent.endDate = Date()
        theEvent.title = "The Main Event!"
        let calendar = EKCalendar.init(for: .event, eventStore: eventStore)
        theEvent.calendar = calendar
        let controller = EKEventEditViewController()
        controller.event = theEvent
        controller.eventStore = eventStore
        controller.editViewDelegate = context.coordinator
        return controller
    }

    func updateUIViewController(_ uiViewController: EKEventWrapper.UIViewControllerType, context: UIViewControllerRepresentableContext<EKEventWrapper>) {
        //
    }
    
    func makeCoordinator() -> EKEventWrapper.Coordinator {
        return Coordinator(isShown: $isShown, event: theEvent)
    }
      
    class Coordinator : NSObject, UINavigationControllerDelegate, EKEventEditViewDelegate {
        @Binding var isShown: Bool
        
        init(isShown: Binding<Bool>, event: EKEvent) {
            _isShown = isShown
        }
        
        func eventEditViewController(_ controller: EKEventEditViewController, didCompleteWith action: EKEventEditViewAction) {
            switch action {
            case .canceled:
                print("Canceled")
                isShown = false
//                controller.dismiss(animated: true, completion: nil)
            case .saved:
                print("Saved")
                do {
                    try controller.eventStore.save(controller.event!, span: .thisEvent, commit: true)
                }
                catch {
                    print("Problem saving event")
                }
                isShown = false
//                controller.dismiss(animated: true, completion: nil)
            case .deleted:
                print("Deleted")
                isShown = false
//                controller.dismiss(animated: true, completion: nil)
            @unknown default:
                print("I shouldn't be here")
                isShown = false
//                controller.dismiss(animated: true, completion: nil)
            }
        }
    }
}

Hey - I'm also having this issue. Were you able to find a solution for CNContactPickerViewController?

I got a very detailed response on StackOverflow that I did not notice until today:


https://stackoverflow.com/questions/57246685/uiviewcontrollerrepresentable-and-cncontactpickerviewcontroller/57621666#57621666

Hi, found your solution very useful since I'm struggling with the same issue.


Just one question: how did you handle the app request to access the calendar, in this new scenario?


Many thanks.