Apple introduced iMessages in June 2011 as the default messaging platform for iOS users; it's served well for Apple since then and now has millions of users. Unfortunately, iOS developers were never allowed to plug into the iMessages system and customize it according to their needs. This all changed in 2016 when Apple introduced the Messages framework in iOS 10, allowing developers to customize the look and feel of iMessages in the form of stickers and custom iMessages extensions using Swift 3.0.

Today, there are thousands of stickers and iMessages apps in the App Store and some even made it to the top ten paid apps. In this article, you're going to build couple of iMessages apps and look at the different methods and purposes for each approach.

Sticker Pack Apps

Sticker Pack Apps can be created in less than 30 seconds because they're code free, meaning that they can be developed and published without writing a single line of code. Xcode 8 provides a new template for creating Sticker Pack Applications, as shown in Figure 1.

Figure 1: Sticker Pack Application Template in Xcode 8
Figure 1: Sticker Pack Application Template in Xcode 8

Once you select the Sticker Pack Application template, Xcode 8 automatically creates the default starter project. An important thing to notice is that the Sticker Pack Application project doesn't contain storyboard, view controllers, or even application delegate files. That's because Sticker Pack Applications are 100% code-free.

Select the Stickers.xcstickers assets and in the middle pane, select the Sticker Pack folder. This updates the right pane, which displays the number of stickers in the application. Because you haven't added any stickers to the application yet, it says No Stickers - Drag and drop images to add new stickers.

Figure 2: Xcode 8 Stickers Pane Area for Sticker Pack applications
Figure 2: Xcode 8 Stickers Pane Area for Sticker Pack applications

At this point, all you have to do is drag and drop the images into the designated area and you're done. I told you: This is going to take less than 30 seconds!

Figure 3 shows the Sticker Pack Application after I've dropped several awesome smiley face emojis into the sticker assets folder.

Figure 3: Displaying a list of emojis as part of the Sticker Pack App
Figure 3: Displaying a list of emojis as part of the Sticker Pack App

At this point, you're done with the app! You can run the app and witness your awesome work.

Figure 4: Sticker Pack App running in the iPhone simulator
Figure 4: Sticker Pack App running in the iPhone simulator

By default, the Sticker Pack Applications displays the stickers in medium sticker-size format. You can adjust the size using the attributes inspector, as shown in Figure 5.

Figure 5: Adjusting the size of the stickers icons
Figure 5: Adjusting the size of the stickers icons

If you run the app again, you'll notice that the stickers are much bigger in size. For the larger size stickers, make sure that your stickers' dimensions are greater than 500 x 500 points. The stickers with large sizes create a very distinctive and unique effect, as shown in Figure 6.

Figure 6: Emoji King App with enormously huge Emojis
Figure 6: Emoji King App with enormously huge Emojis

Even though sticker applications are quick and easy to create and are great for sharing funny emojis, pictures, GIF images, etc., they're quite limited in nature. The limitation comes from the fact that sticker applications don't have any code. This means that sticker pack applications can't integrate with ad networks, in-app purchases or even transition to different screens.

Sticker Pack applications allow the designers to use their creative skills to step into the arena of app development without having to write a single line of code.

Don't feel upset that you can create the same sticker application using code. Instead of choosing the sticker pack application project template, you use the iMessages application project template. In the next section, I'll demonstrate how you can write a complete iMessages application that allows the user to keep track of RSVP confirmations.

Creating the RSVP iMessages Application

The real power of the new iOS 10 Messages framework comes from the iMessages applications. Just like sticker pack applications, iMessages applications run under the umbrella of the Messages framework app but provide a lot more flexibility for customization.

In this section, I'm going to create a very practical iMessages application for managing RSVPs. Consider a scenario where you're throwing a birthday party and you want an RSVP count for the number of guests. Instead of sending and tracking hundreds of RSVPs on your Messages app, wouldn't it be great if you could simply get the count of people who're interested in attending the event? This saves you from the headache of counting the confirmations yourself and also affects the number of pizzas you order for the party. Nobody likes to waste pizza and cake, am I right?

Messages App Presentation Styles

Before jumping into the technical details of the RSVP iMessages app, I'll talk about the different presentation styles of the iMessages application. The iMessages app comes with two presentation styles: compact and expanded.

  • Compact: The user selects the extension in the app drawer: The Messages app launches the extension using the compact style.
  • Expanded: The user selects a message in the transcript that represents one of the extension's MSMessage objects: The Messages app launches the extension using the expanded style.

Adding Child View Controllers

When creating iMessages applications, Xcode automatically adds a default view controller on the storyboard. This view controller inherits from MSMessagesAppViewController and serves as the root controller for the iMessages applications. In order to display a custom view controller, you need to inject the controller inside the MSMessagesAppViewController using view controller containment.

Because the RSVP app requires several screens, it's a good idea to separate the functionality of each screen into a separate view controller. This technique allows you to follow the single responsibility principles and helps you write more maintainable and readable code. Figure 7 shows the overall user interface architecture of the app.

Figure 7: User Interface Layout of the iMessages RSVP Application
Figure 7: User Interface Layout of the iMessages RSVP Application

You can see that:

  • The CompactViewController is displayed in the compact mode when the application is launched.
  • The CreateRSVPViewController allows the user to create a new RSVP and enter the details of the event.
  • The SelectionViewController allows the user to respond to the RSVP request.

The first step is to add the CompactViewController to the MessagesViewController using the view controller containment techniques. I've created a custom function that's responsible for adding and removing child view controllers. Listing 1 shows the complete implementation of the presentViewController function.

Listing 1: A generic function to inject designated view controller into the MSMessagesAppViewController

private func presentViewController
(with conversation:MSConversation,
 for presentationStyle : MSMessagesAppPresentationStyle) {

    var controller :UIViewController!

        if presentationStyle == .compact {

            controller = instantiateRSVPCompactViewController()

        } else {

            controller = instantiateCreateRSVPViewController()

        }

        //Remove any existing child controllers.
        for child in childViewControllers {
            child.willMove(toParentViewController: nil)
            child.view.removeFromSuperview()
            child.removeFromParentViewController()
        }

        addChildViewController(controller)

        controller.view.frame = self.view.bounds
        controller.view.translatesAutoresizingMaskIntoConstraints = false
            self.view.addSubview(controller.view)

        controller.view.leftAnchor.constraint(equalTo:
            view.leftAnchor).isActive = true
        controller.view.rightAnchor.constraint(equalTo:
            view.rightAnchor).isActive = true
        controller.view.topAnchor.constraint(equalTo:
            view.topAnchor).isActive = true
        controller.view.bottomAnchor.constraint(equalTo:
            view.bottomAnchor).isActive = true

        controller.didMove(toParentViewController: self)
}

Depending on the presentation style, the presentViewController function injects the correct controller into the MessagesViewController. The instantiateRSVPCompactViewController and instantiateCreateRSVPViewController functions are responsible for instantiating the controllers based on their storyboard Identifiers. Listing 2 shows the instantiate functions implementation.

Listing 2: Instantiating the Child Controllers

private func instantiateCreateRSVPViewController()
    -> UIViewController {

    guard let controller = self.storyboard?.instantiateViewController
        (withIdentifier: "CreateRSVPViewController")
        as? CreateRSVPViewController else {
            fatalError("CreateRSVPViewController not found")
        }

controller.delegate = self
return controller
}

private func instantiateRSVPCompactViewController()
    -> UIViewController {

guard let controller = self.storyboard?.instantiateViewController
    (withIdentifier: "RSVPCompactViewController") as?
    RSVPCompactViewController else {
        fatalError("RSVPCompactViewController not found")
    }

controller.delegate = self
return controller
}

Run the app and you'll notice that the RSVPCompactViewController is injected into the MessagesViewController and is displayed in the compact view due to its presentation style. If you press the expand button, the presentViewController is triggered again and CreateRSVPViewController is injected into the MessagesViewController. Figure 8 shows this in action.

Figure 8: iMessages compact and expanded states
Figure 8: iMessages compact and expanded states

The communication between the child view controllers and the parent view controller is performed using protocols and delegates. The CompactRSVPViewController exposes a protocol that can be implemented by the delegate classes. The implementation of the instantiateRSVPCompactViewController function already set its delegates, and are notified when any protocol function is triggered. The protocol CompactRSVPViewController delegate defines only a single function called rsvpCompactViewControllercreateRSVP. The function doesn't take any parameters and it doesn't return any parameters. The only purpose of this delegate method is to transfer the control back to the MessagesViewController, as can be seen in the following snippet.

protocol RSVPCompactViewControllerDelegate : class {

    func rsvpCompactViewControllercreateRSVP()

}

The MessagesViewController conforms to the RSVPCompactViewControllerDelegate and provides a custom implementation for the rsvpCompactViewControllercreateRSVP function, as shown in the next snippet.

func rsvpCompactViewControllercreateRSVP() {

requestPresentationStyle(.expanded)
}

As you can see, the rsvpCompactViewControllercreateRSVP simply requests the expanded presentation style, which launches the CreateRSVPViewController. The requestPresentationStyle function invokes the didTransition function, which in turn calls the presentViewController function and selects the correct controller depending on the passed presentation style.

Customizing Messages

Once the user fills in the RSVP description, you need to communicate that to the other recipient in the form of a message. The Messages Framework provides multiple ways of sending messages. A message can be sent as a plain text message or a customizable MSMessage. The next snippet shows an approach where a plain text message is sent as an RSVP invitation.

func createRSVPViewControllerDidCreatedRSVP(rsvp: RSVP) {
    requestPresentationStyle(.compact)

    self.activeConversation?.insertText(rsvp.title,completionHandler: nil)
}

The result is shown in Figure 9.

Figure 9: Use the iMessages App to send a simple text message.
Figure 9: Use the iMessages App to send a simple text message.

It's pretty simple! But it also doesn't capture any enthusiasm and excitement for the party. Luckily, the MSMessages class allows you to send customized messages. Listing 3 shows the implementation of the composeMessage function, which creates a MSMessage class.

Listing 3: Function to create a message with custom layout

private func composeMessage(rsvp :RSVP) -> MSMessage {

    let message = MSMessage()
    let components = NSURLComponents()
    message.url = components.url!

    let layout = MSMessageTemplateLayout()
    layout.image = UIImage(named: "partyballoons.jpg")
    layout.caption = rsvp.title
    layout.subcaption = "Yes Count: \(rsvp.yes)"
    layout.trailingSubcaption = "No Count:\(rsvp.no)"

    message.layout = layout
    return message
}

The composeMessage function takes in an object of type RSVP class and generates and returns an instance of the MSMessage class. The MSMessageTemplateLayout is responsible for customizing the look and feel of the message. I assigned an image to the layout that serves as the background image of the message. I also changed the caption of the message through the template layout, which allowed me to show the text of the invitation. Figure 10 shows the result in action.

Figure 10: Message custom layout
Figure 10: Message custom layout

This definitely looks much better, and I'd bet that more people are going to RSVP YES for the party due to the appealing appearance of the message.

Maintaining Sessions Between Participants

In order to update the RSVP count, you need to maintain the session between the participants. This can be accomplished by using the URL property of the MSMessage class. The URL property can hold query string data that can represent the information in pairs. The next snippet shows the query string to create for the application.

message=Are%20you%20coming%20to%20my%20party&yesCount=54&noCount=3

The query string contains three pieces of information in pairs. Each pair is identified by a key and a value. Here's the breakdown of the message URL:

  • message: The actual text of the invitation
  • yesCount: The number of people who replied yes
  • noCount: The number of people who replied no

Apple simulator doesn't provide the functionality to send group messages nor does it allow to add custom Messages accounts.

Listing 4 shows the implementation of the composeMessage function, which adds the query parameters to the message.

Listing 4: Compose Function with Sharing Data Through URL

private func composeMessage(rsvp :RSVP) -> MSMessage {

    let session = self.activeConversation?.selectedMessage?.session

    let message = MSMessage(session: session ?? MSSession())

    let components = NSURLComponents()

    components.queryItems = [URLQueryItem(name: "title", value:
        rsvp.title),URLQueryItem
        (name:"yesCount",value:"\(rsvp.yes)"),
        URLQueryItem(name:"noCount",value:"\(rsvp.no)")]

    message.url = components.url!

    let layout = MSMessageTemplateLayout()
    layout.image = UIImage(named: "partyballoons.jpg")
    layout.caption = rsvp.title
    layout.subcaption = "Yes Count: \(rsvp.yes)"
    layout.trailingSubcaption = "No Count:\(rsvp.no)"

    message.layout = layout

    return message

}

The NSURLComponents class is responsible for creating the query string in a designated format. Finally, you assign the newly generated URL to the message's URL property. Now when a message is added to the active conversation, it has the underlying query string part of the URL property. Next, you need to add a view that displays the text of the invitation and also to accept or reject the invitation.

The RSVPSelectionViewController is responsible for providing the user with options to either accept or reject the invitation. The RSVPSelectionViewController consists of two buttons, one for YES and the other one for NO, as shown in Figure 11.

Figure 11: RSVP confirmation screen
Figure 11: RSVP confirmation screen

The RSVP title is passed to the RSVPSelectionViewController using the message's URL property, where it can be decomposed into an object and then displayed in the view controller. Finally, the user is given the opportunity to accept or reject the invitation by pressing a button. The button, when pressed, increments the yes or no count and then appends a new message to the active conversation. The result is shown in Figure 12.

Figure 12: Message updated with RSVP count
Figure 12: Message updated with RSVP count

You can't modify the original message, but you can append to it by using a shared session between participants.

Conclusion

Apple's inclusion of the Messages framework has opened doors for creating a new category of applications. Apart from the stickers apps, iMessages apps have become an integral part of Apple's echo system, allowing you to create a new form of communication medium for your users. The Messages framework has allowed developers to extend the existing iOS app to include the messages capability.