4

I am implementing a seemingly trivial and very popular use case where users select a contact, and send them a precomposed SMS.

However, the SMS ViewController dismisses itself automatically. This is easily reproducible.

How do I fix this?

Here's my code:

import UIKit
import MessageUI
import ContactsUI

class ViewController: UIViewController, MFMessageComposeViewControllerDelegate, CNContactPickerDelegate{

    let contactPickerViewController = CNContactPickerViewController()
    let messageVC = MFMessageComposeViewController()

    override func viewDidLoad() {
        super.viewDidLoad()
        contactPickerViewController.delegate = self

        messageVC.messageComposeDelegate = self
    }

    func contactPicker(picker: CNContactPickerViewController, didSelectContact contact: CNContact) {
        if let phoneNumberValue = contact.phoneNumbers.first?.value as? CNPhoneNumber {
            if let phoneNumber = phoneNumberValue.valueForKey("digits") as? String {

                // Configure message ViewController
                messageVC.recipients = [phoneNumber]
                messageVC.body = "Yoyoyo"

                picker.presentViewController(messageVC, animated: true, completion: nil)
            }
        }
    }

    func messageComposeViewController(controller: MFMessageComposeViewController, didFinishWithResult result: MessageComposeResult) {
        self.dismissViewControllerAnimated(true, completion: nil)
    }

    @IBAction func invite(sender: AnyObject) {
        self.presentViewController(self.contactPickerViewController, animated: true, completion: nil)

    }
}
ray
  • 102
  • 1
  • 8

1 Answers1

3

The problem is that you are asking your picker to present the message view controller. When contactPicker:picker:didSelectContact: method is called, the picker view controller is automatically being dismissed by the system. This means that the view controller is going away and you are trying to use that view controller to present your next view controller.

What you need to do is have "ViewController" in this case present the message view controller. Below is an example of the portion of your code i changed. You'll notice i have a timer there. This is because if you try to present the messageVC right away, nothing will happen because the contacts view controller isn't done dismissing itself yet.

func contactPicker(picker: CNContactPickerViewController, didSelectContact contact: CNContact) {
    if let phoneNumberValue = contact.phoneNumbers.first?.value as? CNPhoneNumber {
        if let phoneNumber = phoneNumberValue.valueForKey("digits") as? String {

            // Configure message ViewController
            messageVC.recipients = [phoneNumber]
            messageVC.body = "Yoyoyo"

            dispatch_async(dispatch_get_main_queue(), { () -> Void in
                self.presentViewController(self.messageVC, animated: true, completion: nil)
            })
        }
    }
}
Chris
  • 1,750
  • 2
  • 14
  • 23
  • Awesome Chris, this works. I'm still curious to know if there isn't a less hacky solution (I think we can both agree a timer is some sort of a hack). If `didSelectContact` is dismissed by the system, why is it that it's `messageVC` that gets dismissed, but `contactPicker` remains? Shouldn't `contactPicker` be dismissed as well? – ray Jan 13 '16 at 02:43
  • The system is probably calling dismissViewController: which should be the picker, but you are probably presenting the messageVC fast enough that when dismiss is called, its actually being called on the messageVC. That would be my guess anyways. I agree the timer does seem a bit hacky, but because the dismissal is called by the system and not us, we don't have access to the completion block. Def open to other options than a timer though. – Chris Jan 13 '16 at 02:54
  • Yeah makes sense. I'll give it a wait and accept it tomorrow if no one comes up with official solution – ray Jan 13 '16 at 03:23
  • You could switch from using `NSTimer` to using **Grand Central Dispatch**. The obvious advantage is that, instead of having to define a separate method (`presentMessageVC()` in this case), you can wrap that logic in a block (closure) and have everything nicely contained inside the body of `-contactPicker:didSelectContact:`. – Nicolas Miari Jan 13 '16 at 04:45
  • _(I'm a big fan of GCD, and haven't used `NSTimer` since maybe 2010...)_ – Nicolas Miari Jan 13 '16 at 04:46