Building an interactive iMessage application for iOS 10 in Swift

Bartłomiej Kozal
Sep 16, 2016 · 8 min read
Image for post
Image for post

Update: the app is open source now under the GNU GPL-3.0 license:

When iMessage applications were announced during the WWDC 2016 keynote, I didn’t expect too much. Stickers, message effects, and maybe access to the most common APIs. Generally, features that only big players will make good use of. Thankfully, I was wrong because the reality is completely different.

According to messages, there are no restrictions at all. Sure, your application can still be rejected during the App Store review. But, if you wish to, technically you can implement any regular iOS application as an iMessage application. Even things like in-app purchases. I think it’s amazing because it opens up a whole new market for developers.

I spent some time exploring this topic and built a Checkers board game which uses the new messages API to send moves between players. It was featured by Apple on release day of iOS 10! Then I asked on /r/iosprogramming if anyone is interested in a tutorial about the new iMessage and there it is.

Before you start

iMessage applications can be developed and distributed in two ways:

  • As an extension to an existing application
  • As an extension which is an independent application

This is significant because you can create iMessage only, standalone apps. It’s not necessary to have a parent application like for other types of extensions.

New messages can be received on:

  • iPhone and iPad with iOS 10
  • Mac with macOS Sierra
  • Apple Watch with watchOS 3

Each previous version of these operating systems has fallback support — the message is delivered as a static image. Additionally, you can provide a URL to your back-end service and allow users to interact with the message from the web browser.

Image for post
Image for post

The important thing is: as a developer you don’t have access to the content of the conversation. So forget about bots that reply to commands.

In terms of testing the application. There is no option to send a message from the iOS simulator to physical devices, neither in the opposite way.

First extension

Image for post
Image for post

Similar to a single view iOS application, you will see: default view controller, main interface storyboard, assets directory, information property list, linked Messages framework, and product binary. Nothing too unusual, so let’s look into the details.

Image for post
Image for post

Messages view controller

The first characteristic is that the view of messages view controller can be presented in two styles: compact and expanded.

Image for post
Image for post

You can get the active presentation style by checking the presentationStyle property. To request a new presentation style, call the requestPresentationStyle function. Furthermore, there are also two functions invoked at the beginning and end of style transition that can be overridden: willTransition and didTransition.

The activeConversation property of the messages view controller gives you access to the active conversation. As I said before, you can’t access the content of messages other than those created by your extension.

Image for post
Image for post

Messages extension has its lifecycle and you are able to override the callback functions like didBecomeActive in the messages view controller. It works very similar to the well-known viewDidLoad from UIViewController. A full list of lifecycle functions possible to override is available under the “Managing the Extension’s State” section in the MSMessagesAppViewController documentation.

There is another set of functions to track when a user taps on the message bubble, e.g. didSelect, didReceive. Again the full list is in the MSMessagesAppViewController documentation under the “Tracking Messages” section.

One thing which is not clearly enough explained in the documentation, in my opinion, is information that tracking message functions are not called if the extension is inactive and the user taps on the message bubble.

Image for post
Image for post

Please pay special attention to this! As you can see, the visual difference isn’t obvious at first sight. It’s very easy to miss when the extension process was terminated. It’s enough if the user switched between extensions or went back to the conversations screen!

Image for post
Image for post

Because in the expanded view a user doesn’t see the message bubble, you may want to request the compact view or dismiss the view to show the bubble. After dismissing, you can still insert a new message without an error but the process will be terminated so the extension will change the state to inactive!

Be careful about view constraints too. Even if you center an element in the middle of the screen in your storyboard it doesn’t mean it will be centered in the extension view. The reason behind this is that the extension view has a top bar with conversation participants and a bottom bar with the text field for the text message. And both have different height.

Image for post
Image for post

As you probably know, each view controller has the superview which is a parent for other views. There is no difference here. Two presentation styles don’t mean that you have two separate views. It’s still the same view.

I recommend using the setup similar to the example provided by Apple. Use two UIViewControllers for each presentation style and instantiate them in your MSMessagesAppViewController. The biggest advantage of this solution is that you don’t have one huge class responsible for everything and you can easily pass data to your messages using the delegate pattern and protocols.

One more thing: There is another new view controller called MSStickerBrowserViewController. I didn’t play with it so I won’t describe how it works. It is used to group sticker objects (MSSticker) in a similar way to UICollectionViewController. Each sticker is initialized with an image and can be dragged to the active conversation from the view controller.


Image for post
Image for post

The image property can be replaced by mediaFileUrl because message bubbles don’t have to be static. Additionally, for PNG, JPEG, GIF you can use videos.

There is another useful property: shouldExpire. If it is enabled, a message will disappear after being read.


When someone replies to the message within a session, its bubble is replaced by a summary text and a new bubble is placed as the newest thing in the conversation.

Without a session, each message will be sent as a separate bubble.

Image for post
Image for post


From the conversation, you can get selectedMessage which references to the message tapped by the user, or you can insert a new message by using the insert function.

What is very important when you compose and insert the message is that the user still has to confirm it before sending by tapping on the button. There is no workaround for this and you can’t send a message programmatically from the app.

Sending custom data and web back-end

Each message has its URL property and iMessage extensions use the format of URL (key=value&key=value) to attach data to the message. Additionally, there are new generic classes in iOS 10 to make parsing URLs easier: URLComponents and URLQueryItem.

Having a back-end is only necessary if you want to give macOS users the possibility to click on the message and interact with it from the browser. Without the back-end, a message is still properly shown as image/video with captions and icon.

Group conversations


Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch

Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore

Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store