As a courtesy, this is a full free rendering of my book, Programming iOS 6, by Matt Neuburg. Copyright 2013 Matt Neuburg. Please note that this book has now been completely superseded by two more recent books, iOS 7 Fundamentals and Programming iOS 7. If my work has been of help to you, please consider purchasing one or both of them. Thank you!

Chapter 33. Mail and Messages

Your app can present an interface allowing the user to edit and send a mail message or an SMS message. Two view controller classes are provided by the Message UI framework; your app will link to MessageUI.framework and import <MessageUI/MessageUI.h>. The classes are:

MFMailComposeViewController
Allows composition and sending of a mail message.
MFMessageComposeViewController
Allows composition and sending of an SMS message.

New in iOS 6, the Social framework lets you post a message to Twitter or Facebook (or Weibo) on the user’s behalf. Link to Social.framework and import <Social/Social.h>. You can use an SLComposeViewController to give the user an interface to construct and send a message, or prepare and post a message directly using SLRequest.

Also new iOS 6, UIActivityViewController (Chapter 26) provides a unified interface for letting the user choose any of the built-in messaging milieus (mail, SMS, Facebook, Twitter, and Weibo). To give the user a chance to send a message through one of these milieus, with an initial message body or other data that you supply, you should use UIActivityViewController. However, I have not found any way to make UIActivityViewController fill in the fields of a proposed mail message. For example, for my users to be able to email me from within one of my apps, I must fill in the To field in the mail composition form. MFMailComposeViewController lets me do that; UIActivityViewController doesn’t. Thus, the Message UI framework remains important.

Mail Message

The MFMailComposeViewController class, a UINavigationController, allows the user to edit a mail message. The user can attempt to send the message there and then, or can cancel but save a draft, or can cancel completely. Before using this class to present a view, call canSendMail; if the result is NO, go no further, as a negative result means that the device is not configured for sending mail. A positive result does not mean that the device is connected to the network and can send mail right now, only that sending mail is generally possible with this device; actually sending the mail (or storing it as a draft) will be up to the device’s internal processes.

To use MFMailComposeViewController, instantiate it, provide a mailComposeDelegate (not delegate), and configure the message to any desired extent. The user can later alter your preset configurations, at which time the message details will be out of your hands. Configuration methods are:

  • setSubject:
  • setToRecipients:
  • setCcRecipients:
  • setBccRecipients:
  • setMessageBody:isHTML:
  • addAttachmentData:mimeType:fileName:

Typically, you’ll show the MFMailComposeViewController as a presented view controller. This approach works equally well on the iPad (use UIModalPresentationFormSheet if a full-screen presentation feels too overwhelming).

The delegate (MFMailComposeViewControllerDelegate) will receive the message mailComposeController:didFinishWithResult:error: describing the user’s final action, which might be any of these:

  • MFMailComposeResultCancelled
  • MFMailComposeResultSaved
  • MFMailComposeResultSent
  • MFMailComposeResultFailed

Dismissing the presented view is up to you, in the delegate method.

Here’s a minimal example:

- (IBAction)doMail:(id)sender {
    BOOL ok = [MFMailComposeViewController canSendMail];
    if (!ok) return;
    MFMailComposeViewController* vc = [MFMailComposeViewController new];
    vc.mailComposeDelegate = self;
    [self presentViewController:vc animated:YES completion:nil];
}

-(void)mailComposeController:(MFMailComposeViewController *)controller
        didFinishWithResult:(MFMailComposeResult)result
        error:(NSError *)error {
    // could do something with result/error
    [self dismissViewControllerAnimated:YES completion:nil];
}

Text Message

The MFMessageComposeViewController class is a UINavigationController subclass. Before using this class to present a view, call canSendText; if the result is NO, go no further. The user has no option to save an SMS message as a draft, so even if this device sometimes can send text, there’s no point proceeding if the device can’t send text now. However, you can register for the MFMessageComposeViewControllerTextMessageAvailabilityDidChangeNotification in the hope that the device might later be able to send text; if the notification arrives, check its MFMessageComposeViewControllerTextMessageAvailabilityKey.

To use MFMessageComposeViewController, instantiate the class, give it a messageComposeDelegate, configure it as desired through the recipients (phone number strings) and body properties, and show it as a presented view controller. The user can later alter your preset configurations, at which time the message details will be out of your hands.

The delegate (MFMessageComposeViewControllerDelegate) will receive the message messageComposeViewController:didFinishWithResult: with a description of the user’s final action, which might be any of these:

  • MessageComposeResultCancelled
  • MessageComposeResultSent
  • MessageComposeResultFailed

Dismissing the presented view is up to you, in the delegate method.

Here’s a minimal example:

- (IBAction)doMessage:(id)sender {
    BOOL ok = [MFMessageComposeViewController canSendText];
    if (!ok) return;
    MFMessageComposeViewController* vc = [MFMessageComposeViewController new];
    vc.messageComposeDelegate = self;
    [self presentViewController:vc animated:YES completion:nil];
}
-(void)messageComposeViewController:(MFMessageComposeViewController*)controller
        didFinishWithResult:(MessageComposeResult)result {
    // could do something with result
    [self dismissViewControllerAnimated:YES completion:nil];
}

Twitter Post

In iOS 6, the interface for letting the user construct a Twitter post is SLComposeViewController, part of the Social framework (superseding TWTweetComposeViewController and the Twitter framework). Twitter, together with Facebook and Weibo, are represented by constant strings. You’ll use a class method to learn whether the desired service is available; if it is, you can instantiate SLComposeViewController for that service and present it as a presented view controller.

Instead of a delegate, SLComposeViewController has a completionHandler. Set it to a block taking one parameter, an SLComposeViewControllerResult. In the block, dismiss the view controller.

Here’s a minimal example:

BOOL ok =
    [SLComposeViewController
        isAvailableForServiceType:SLServiceTypeTwitter];
if (!ok) return;
SLComposeViewController* vc =
    [SLComposeViewController
        composeViewControllerForServiceType:SLServiceTypeTwitter];
if (!vc) return;
vc.completionHandler = ^(SLComposeViewControllerResult result) {
    // could do something with result
    [self dismissViewControllerAnimated:YES completion:nil];
};
[self presentViewController:vc animated:YES completion:nil];

You can also, with the user’s permission, gain secure access to the user’s Twitter account information (or Facebook, or Weibo) through the ACAccountStore class (part of the Accounts framework). Using this, along with the SLRequest class, your app can construct and post a message directly, without passing through the message composition interface; see Apple’s Tweeting example. The ACAccountStore class can manipulate accounts in other ways as well.