0

I'm building an app that prompts the users for their contacts. I'm brand new to Swift but making decent progress.

I'm invoking this open function from a parent call site and want to receive the response value back from the contactPicker function. I'm a bit unfamiliar with the implied nature of the contactPicker invocation, and was expecting to have to invoke it directly from within the presentContactPicker function, but the documentation seemed to suggest this approach is preferable.

As it stands now, this implementation correctly prompts the Contact Picker UI, and prints the selected contacts to the console. But I need assistance actually passing the contacts to the callback function.

The issue that I have is that I'm not able to access the callback function from inside the contactPicker. I have an intuition that maybe I could attach the callback to the parent class with a technique like self.callback = callback in the open function and then call self.callback() in the contactPicker itself, but it didn't work.

import Foundation
import UIKit
import ContactsUI

@objc(ContactsPicker)
class ContactsPicker : NSObject, CNContactPickerDelegate {
  
  @objc static func requiresMainQueueSetup() -> Bool {
    return false
  }
  
  @objc func open(_ options: NSDictionary, callback: RCTResponseSenderBlock) -> Void {
    DispatchQueue.main.async {
      self._presentContactPicker(options: options)
    }
  }
  
  func _presentContactPicker(options: NSDictionary) -> Void {
      let contactPickerVC = CNContactPickerViewController()
      contactPickerVC.delegate = self
      let controller = RCTPresentedViewController()
      controller?.present(contactPickerVC, animated: true)
  }
  
  func contactPicker(_ picker: CNContactPickerViewController, didSelect contacts: [CNContact]) {
    print("+++++++++++++++++++++++++++")
    print(contacts)
    print("+++++++++++++++++++++++++++")

    ### But how can I access the callback from the exposed open method?
  }
}

1
  • @Sweeper hey, so I've tried to do self.callback = callback in the open function and hit the compilation error Value of type ContactsPicker has no member 'callback' so I tried to add a simple func callback () {} on the parent class but I get the Cannot assign to value: 'callback' is a method since I assume it is expecting type of RCTResponseSenderBlock not func - I tried declaring it as such, but was hitting more and more errors, so at that point I realized I was pretty out of my depth and figured I would ask here! 🙏 Commented Nov 27, 2021 at 20:42

1 Answer 1

1

maybe I could attach the callback to the parent class with a technique like self.callback = callback in the open function and then call self.callback() in the contactPicker itself

This is the right idea. You can declare a property to store the callback like this:

private var callback: RCTResponseSenderBlock?

@objc func open(_ options: NSDictionary, callback: @escaping RCTResponseSenderBlock) -> Void {
    self.callback = callback
    DispatchQueue.main.async {
        self._presentContactPicker(options: options)
    }
}

Note that the since the callback "escapes" into the class, you should mark the parameter as @escaping.

func contactPicker(_ picker: CNContactPickerViewController, didSelect contacts: [CNContact]) {
    callback(someParameters)
}
Sign up to request clarification or add additional context in comments.

6 Comments

you're totally right. I was digging around more after your earlier comment and came to a similar answer. This works great! By the way in the contactPicker function, when I invoked self.callback!([contacts][0]) the callsite receives [null] but when I pass self.callback!([contacts][0].givenName) the callsite receives ["Kate"] so I know the data passing is working well, but it seems not to be able to pass the CNContact object itself. Any ideas? Appreciate your help either way 🤝
@Trevor I'm not familiar with react native, but it does not likely support CNContact objects. Keep in mind that the data you are passing to this callback is going to your JS code, so you probably need to transform the data in such a way that the JS code can read. My first guess would be to turn it into a [String: Any]. JS is full of those :)
Your intuition is spot on. I tried the description instance method and it passed all the information I was expecting to see over the bridge as a string. My mental model is that I need to pass some JSON like object probably, and your recommendation or [String: Any] makes total sense. Is it a simple approach or do you need a complex utility for mapping over all the "keys" and converting the values to a String?
@Trevor I think you'll have to do this by hand, unfortunately. Something like this. The answer in the link also turns it into JSON Data using JSONSerialization, which you don't need. (I don't know what would happen if you did...)
Sweeper, you were right again. I'm a bit surprised there is no generic capability for converting the CNContact into a regular Data or [String: Any] object, but if I use a mapper utility for plucking out all the values then it works great. Cheers for all your help, I really appreciate you!
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.