Send Text Message In-App – Using MFMessageComposeViewController with Swift

In a previous walkthrough on sending e-mails in-app, I explored how to use MFMailComposeViewController to allow a user to compose an e-mail without ever leaving your app. I then followed up with a proposal for better-segregating the responsibilities of composing an e-mail and responding to the delegate callbacks out of the View Controller.

I say all this because today, I’d like to walk you through sending a text message in-app using the same pattern as the second article. This walkthrough should be fairly straight-forward on its own, but if you’d like to reference my previous post on clean view controllers, it could be helpful in understanding why I’m not just shoving everything into the View Controller and calling it good.

The Gist

Here’s the gist of the components we’ll need in order to accomplish the task of using MFMessageComposeViewController to send text messages in-app. Presumably, you’d like to allow your users to send a text message to a pre-defined recipient, or maybe you want to pre-populate a message and allow your users to text it to whomever they desire. Any and all of this can be accomplished by implementing this general outline:

  • Create a class (I’ll call it MessageComposer) that will handle the responsibility of creating/configuring a text message composer.
  • MessageComposer will also handle the MFMessageComposeViewControllerDelegate callback method (
    messageComposeViewController:didFinishWithResult:).
  • Finally, I’ll program a View Controller to create an instance of this MessageComposer class and present it based upon some user action, such as tapping a button.
  • Note that to see the example in action, you’ll have to run it on an actual device, because the Simulator is unable to send text messages…

For those who just like to dive in and explore, feel free to head over to GitHub and grab the example project now!

The Details

With the general idea in mind, let’s jump in to discover how to implement the solution…

MessageComposer

While it’s possible to simply put all of this code inside the View Controller, I’d recommend doing your best to avoid it. While this simple example doesn’t add a ton of complexity if you just write it all into your View Controller, a real-world app will undoubtedly be more complex.

In my experience, it’s best to try and segregate out as many responsibilities of functionality from the View Controller where it’s possible. Thankfully, simple examples like this highlight the ease of segregating these responsibilities so that you can begin to employ the pattern for other components that you use within your app.

So in keeping with this idea of nurturing a clean View Controller, I’ve decided to create a new custom class called MessageComposer. Take a look:

 1import Foundation
 2import MessageUI
 3
 4let textMessageRecipients = ["1-800-867-5309"] // for pre-populating the recipients list (optional, depending on your needs)
 5
 6class MessageComposer: NSObject, MFMessageComposeViewControllerDelegate {
 7    
 8    // A wrapper function to indicate whether or not a text message can be sent from the user's device
 9    func canSendText() -> Bool {
10        return MFMessageComposeViewController.canSendText()
11    }
12    
13    // Configures and returns a MFMessageComposeViewController instance
14    func configuredMessageComposeViewController() -> MFMessageComposeViewController {
15        let messageComposeVC = MFMessageComposeViewController()
16        messageComposeVC.messageComposeDelegate = self  //  Make sure to set this property to self, so that the controller can be dismissed!
17        messageComposeVC.recipients = textMessageRecipients
18        messageComposeVC.body = "Hey friend - Just sending a text message in-app using Swift!"
19        return messageComposeVC
20    }
21    
22    // MFMessageComposeViewControllerDelegate callback - dismisses the view controller when the user is finished with it
23    func messageComposeViewController(controller: MFMessageComposeViewController!, didFinishWithResult result: MessageComposeResult) {
24        controller.dismissViewControllerAnimated(true, completion: nil)
25    }
26}

Taking a close look through the comments, you’ll notice the following:

  • You need to import the MessageUI module.
  • Rather than hard-code the recipients list, I’ve declared a constant at a global scope for easy access/changeability in the future. It’s actually optional to even supply a recipients list to your composer… it’s just dependent on how you want to use it. Notice that it’s an array of Strings.
  • MessageComposer inherits from NSObject. This is a requirement of the MFMessageComposeViewControllerDelegate protocol, which MessageComposer conforms to.
  • There’s a function in there called canSendText that becomes important later on for testing whether or not sending a text message is even possible at the moment on the user’s device. It wraps MFMessageComposeViewController‘s canSendText method to avoid needing to import the MessageUI module in other places (like the View Controller that uses instances of this class).
  • configuredMessageComposeViewController does what it says it does – it returns an instance of a MFMessageComposeViewController that’s been configured with a list of recipients and a body.
  • messageComposeViewController:didFinishWithResult: is what gets called with the user either sends the text, or cancels sending a text from the message composer. I’ve written code in that method’s body to simply dismiss the instance of the view controller that called the method.

We’re now ready to head to the View Controller and wire up the components allowing a user to display a MessageComposer!

View Controller

The View Controller is very simple for this example:

 1import UIKit
 2
 3class ViewController: UIViewController {
 4    // Create a MessageComposer
 5    let messageComposer = MessageComposer()
 6    
 7    @IBAction func sendTextMessageButtonTapped(sender: UIButton) {
 8        // Make sure the device can send text messages
 9        if (messageComposer.canSendText()) {
10            // Obtain a configured MFMessageComposeViewController
11            let messageComposeVC = messageComposer.configuredMessageComposeViewController()
12            
13            // Present the configured MFMessageComposeViewController instance
14            // Note that the dismissal of the VC will be handled by the messageComposer instance,
15            // since it implements the appropriate delegate call-back
16            presentViewController(messageComposeVC, animated: true, completion: nil)
17        } else {
18            // Let the user know if his/her device isn't able to send text messages
19            let errorAlert = UIAlertView(title: "Cannot Send Text Message", message: "Your device is not able to send text messages.", delegate: self, cancelButtonTitle: "OK")
20            errorAlert.show()
21        }
22    }
23}
  • You’ll notice that I create an instance of MessageComposer (the custom class we just defined in previous steps). It’s declared at a scope that can be seen throughout the lifetime of the View Controller (so that the delegate callback can be invoked when appropriate).
  • sendTextMessageButtonTapped is wired up to a button on my storyboard, and will be executed when the user taps the button.
  • Checking to make sure the device can send a text message is critical – Note that running the example in the Simulator will execute the else block, because the Simulator cannot send text messages.
  • If the device can send texts, a configured MFMessageComposeViewController is obtained from the MessageComposer instance. It’s then displayed.
  • If the device can’t send texts, it’s probably a good idea to alert the user somehow… I’ve chosen a simple UIAlertView.

Summary

In this walkthrough, I’ve demonstrated the mechanics of configuring, displaying, and dismissing a MFMessageComposeViewController, which enables your users to send a text message in-app. Additionally, I’ve attempted to show how to keep the View Controller from handling more than it really should, by segregating out the MFMessageComposeViewController configuration and delegate protocol conformance to another class.

Happy texting!

comments powered by Disqus