Video hosted by Apple at devstreaming-cdn.apple.com

Configure player

Close

WWDC Index does not host video files

If you have access to video files, you can configure a URL pattern to be used in a video player.

URL pattern

preview

Use any of these variables in your URL pattern, the pattern is stored in your browsers' local storage.

$id
ID of session: wwdc2016-224
$eventId
ID of event: wwdc2016
$eventContentId
ID of session without event part: 224
$eventShortId
Shortened ID of event: wwdc16
$year
Year of session: 2016
$extension
Extension of original filename: mp4
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: [2016] [Session 224] iMessage Ap...

WWDC16 • Session 224

iMessage Apps and Stickers, Part 2

App Frameworks • iOS • 45:27

Messages apps allow people to collaborate by using your app. Learn how people can work together to accomplish a task within the conversation. Customize the appearance of the messages your app can send. Learn about how to manage your app state in one-to-one and group conversations to provide an engaging and collaborative experience.

Speakers: Alex Carter, Stephen Lottermoser

Unlisted on Apple Developer site

Transcript

This transcript has potential transcription errors. We are working on an improved version.

Good afternoon. Welcome to "iMessage Apps and Stickers, Part 2". My name's Alex and with me today helping present is Stephen. He's going to do the demos. So, in the first part of this talk, Bhaskar and Lily introduced stickers, sticker packs, and simple iMessage apps focused on providing sticker data to messages.

Today we're going to talk about interactive messages and we're going to dive deeper into the iMessage apps and the Messages framework. So, what are interactive messages? Well, you can see one here behind me highlighted in the Messages transcript. This is a special type of message bubble provided by a Messages extension and you can see that's shown here in the screenshot highlighted.

Messages extensions allow your users to compose interactive messages and then they allow them to insert them on the input field, as shown here. They also allow a user to reply to an interactive message by tapping one in the transcript and in this point an extension will be launched full screen, as shown here.

The user can interact with this extension to compose a reply. Your extension can then insert this new content into the messages and it will show here on the input field and the user can send it. So, now we're going to welcome Stephen to the stage and he's going to show you our sample app running in the simulator.

[ Applause ]

I'm going to show you a completed iMessage application that I've written to demo all of the APIs that we're going to learn in today's session. If you look here in the simulator, you'll see the icon from my iMessage app called Ice Cream. When I tap this, my iMessage app is revealed here running within the context of Messages. All of the UI with this pink background is code-- is UI provided by my extension. Now, my iMessage app is a collaborative ice cream sticker-building application and here you see a history of ice cream stickers that I've already built.

When I tap this Add button here, my iMessage app will expand to take the full screen and I'll have this nice sticker-building experience here. If I choose an ice cream cone, my iMessage app dismisses and stages an interactive message in the input field for the user to send.

Now, on iOS 10, we've added support for you to-- for you to develop and test your iMessage apps right within the simulator. If you go back to the conversation list here, you'll see two ends of a simulated conversation. Tapping the second one shows the simulated received message. When I tap this, my iMessage app is launched with the next step in the ice cream building process. I can select my favorite ice cream, mint chip, stage that message, and send it.

Now, if I go back to that first conversation or the first end of the conversation and tap this message and select the final part of the ice cream, my topping, and send it, I now have a completed ice cream that I've built. In fact, now when I go back into my iMessage app, you'll see that it appears here in my ice cream history as a sticker that I can then apply anywhere I want to. And that's a quick sample of what you can do with interactive messages. And now back to Alex.

[ Applause ]

Thanks, Stephen. That was just a great quick demo of some of the features of iMessage apps. So, let's talk about how we can accomplish this with our APIs. I'm going to go through four things. One of them-- so I'm going to give you an overview of the API followed by number two, a little look into the extension lifecycle. Number three, we're going to talk about how to compose a message using our APIs. Number four, we're going to talk a little bit about how the message is sent.

So, starting with the API overview. We have our iMessage app running here, shown in compact presentation mode and this is an extension. Extensions have a principal class and for iMessage apps this will be a subclass of the MSMessagesAppViewController. Now, ultimately, the MSMessagesAppViewController is a subclass of UIViewController, so it's providing your extension's UI. Above the extension, we have the conversation and specifically the conversation transcript. This is represented in our API by the MSConversation object.

And finally we have a message itself. We have this bubble here in the transcript and this is represented by the MSMessage class. Also, you'll note in this slide we have another two classes shown, the MSSession and MSMessageTemplateLayout. These provide some control over how the message is sent and how the message appears in the transcript. So, let's move on and talk about iMessage extension lifecycle. Here we have a timeline and when an extension is launched, perhaps from the app drawer, we're going to launch a process.

Shortly afterwards, we'll call didBecomeActive with conversation and we're going to pass in a conversation object that represents the conversation your app is running within. And since this is a UIViewController subclass, we'll also call will-- viewWillAppear and viewDidAppear as your extension appears onscreen. Now, what happens when your app remove-- is removed from the screen? We're going to call this resigning active and what happens is first you will receive viewWillDisappear, viewDidDisappear calls on your principal class.

This is followed by a viewWill-- sorry-- this is followed by a willResignActive call with conversation. At this point, we're about to teardown the connection between Messages and your extension. So, this is the last time you get to do anything with the conversation. And then at some point later your process will be terminated and this-- just to stress-- is because your process is built on extensions and extensions are short-lived processes and they're terminated aggressively.

So, let's see now that we have a process running and showing UI within Messages, your user is going to interact with this UI and build a message and let's see how we're going to deal with that in our API. Well, the first thing you're going to do is create an MSMessage object and we're going to set some properties on that object.

In this case, I'm setting an https URL so the message is URL property and I'm encoding some detail about the ice cream that we're building in the query string of that URL. The only platform that is able to generate iMessage-- these messages is iOS, but these messages will be sent to macOS and to watchOS.

When they're delivered on macOS, they'll be shown in full fidelity in the conversation transcript and clicking on a message in the transcript will try and get this URL and open it in your web browser. So, if you provide an https URL here, then your website is able to show-- should be able to show some representation of the message for users on macOS.

Moving on, we have an accessibility label property here. This is used by screen readers when reading the transcript. So, I'm setting this to a description of the message bubble so that our users who use screen readers get a great rich description of the bubble as it is displayed in the transcript. And finally I'm going to talk about-- we've got layout. And I'm going to talk about this in a little bit more detail now.

The layout object is how we specify the appearance of a message in the transcript. Right now we only have one layout and that's the MSMessageTemplateLayout. We can set some properties on this and this affects how the bubble is built. So, right here we have an image property and this takes a UI image and it provides content for the area highlighted onscreen.

We also have a mediaFileURL property. The mediaFileURL also provides content for the same area as the image, but here you can provide content that's not supported by UI image. For instance, you could provide a URL to a local video file. A short video would be then looped in the background of the message bubble in the transcript. Let's take a second to focus a little bit more on these two properties.

We should note first that if the image and the URL are both provided, then the image is going to take precedence over the URL. We had some recommended-- recommendations for the size of the assets provided in these properties. We're recommending that you provide your assets at roughly 300 point by 300 point.

You can provide slightly larger assets and slightly smaller assets and I really encourage you to experiment with asset sizes here in order to find what best suits your app and your needs. All of these assets, as with stickers, are provided at 3x and Messages will downsample and downscale these images when they arrive on other devices as appropriate for the screen resolution on that-- on the receiving device.

In the mediaFileURL, we support PNG, JPEG, GIF, and video. When I say video, what I mean here is that we support any videos that can be played with our media player frameworks and I'd like to refer you to the media player framework documentation to find out more about this.

The other thing that we're going to do with this media is we're going to transcode the assets when they're sent. This is in order to optimize them for transfer over our networks. And finally I'd like to encourage you to avoid rendering text into this image. As I said, these images are scaled down for display on devices with 1x and 2x screens, yet we're asking you to provide them at 3x.

If you were to render text into this image, particularly small text, when these images are scaled down, the text could be rendered illegible, but you'll notice that we actually do show in this example some text drawn on top of the image, so let's talk about how this is done without degrading quality.

The layout has some additional properties over image-- the two that provide images-- and these are text properties. They're transformatted with the message and drawn natively on the receiving devices so we can draw the text on the receiving devices as crisply and cleanly as possible. We have here shown the image title and this is text that's drawn on top of the image in the lower left hand corner.

We have an image subtitle that's drawn again on top of the image, right below the image title as shown. And we have these caption properties and these are drawn below the image in the caption bar. That's this gray area onscreen. So, we have caption, trailing caption, subcaption, and trailing subcaption.

Now, if you look at the caption properties, it's also possible to omit these altogether or set them all to nil. If you do that, then you'll get a bubble that doesn't have the caption bar at the bottom and it'll look something more like this. And one last thing to note on this slide.

We have an icon in the top left-hand corner of a bubble. This icon is provided and drawn by Messages on top of your content and this is always going to be the iMessage app icon of the app that sent the message. So, let's move on now and talk about how we send this message that we've constructed.

To do that, we're going to need to get an MSConversation instance. You can get this from your principal class's active conversation. Once you have a conversation, you can call the insert(message), pass the message you constructed, and make sure that you handle any errors that are returned. Doing this will cause the message to appear in the input field.

Of course, as mentioned in the first talk, you can also send some other types of data. We support sending text, attachments, and of course stickers. Once you've got your content on the input field, the user is able to send the message by tapping the blue Send button and I should note here that there's no way for your extension to actually automatically send a message and we always want the user to have the final say as to what gets sent to their friends. So, now I'd like to hand you back over to Stephen who is going to show you how we implement this in our sample project.

[ Applause ]

Thanks again, Alex. OK. I've taken the sample iMessage app that I showed earlier in the session and stripped it down to a very basic minimum for our starting point. Here we still have our history of ice cream stickers that have already been built, but you'll notice that if I tap the Add button, nothing happens.

Here in Xcode, I have my MSMessagesAppViewController subclass shown. I override the lifecycle method did become active with conversation, call super, and then call present child view controller which is a helper method I've written which will instantiate an ice cream history controller and add it as a child view controller. And that's all I have so far.

Now, what I'd like to do is when I tap this Add button is start the sticker-building process. Now, this ice cream history view controller has a delegate protocol and I have an extension on my messages view controller which conforms to this delegate by implementing the method historyViewController AddButtonTapped.

This is where I'm going to trigger my functionality and I'm going to also start by adding a helper method called composeMessage for iceCream, which will take an ice cream model object and will turn it into an MSMessage for me to stage in my conversation. To do so, I'm going to create an instance of URL components and set the query items property of that URL components to be a query items representation of my model. Next thing I'm going to do is create an instance of MSMessageTemplateLayout and set the image of that layout to be a rendered version of my model. I also want to set a caption for my message and I'll declare my caption up here.

Now I'm ready to create my MSMessage object. I'll do so by declaring an instance of MSMessage and set the URL property to be the URL representation of my components. Then I'll set the layout and the accessibility label. For brevity in the demo, I'm just going to reuse my message caption as my accessibility label.

Now that I have my message, I'm ready to stage it in the conversation. I'll get a reference to my active conversation and call conversation.insert to insert the message so it is staged. Now that I've written my composeMessage function, here I'm ready to call it from my historyViewController AddButtonTapped method.

And I'll start off by always adding or passing a new ice cream model instance. Now when I run this in the iOS simulator and launch my iMessage application, wait for that debugger to attach and tap the Plus (+) button. My message is staged for the user to send. Now, because I haven't actually selected any ice cream parts, I just have a placeholder image for the ice cream and you see my caption below the message.

Now, this is a great start, but I'd like to get closer to that final demo that I showed you earlier. When I tap that Plus (+) button, I want to go into my sticker-building UI experience. To do so, I need to introduce a new concept to you. Here my iMessage app is presented in a compact presentation style. I can choose to have my iMessage app presented in an expanded presentation style by requesting it.

Instead of calling composeMessage for iceCream here, I'll call a method on my super class, request presentation style with the expanded enum case. This will trigger a lifecycle callback method called willTransition to presentationStyle. I'll call the super implementation and then I'll call my helper method to present the right child view controller for this presentation style.

Now, my presentChildViewController currently always presents my ice cream history view controller and so I need to change the logic to take into account the presentation style so I present the correct view controller. First, I'm going to add a parameter here so I know which presentation style I'm dealing with.

Then I'm going to change this to only present my history view controller when I'm in the compact presentation style. If I'm in the expanded case, I'm instead going to declare a new instance of my ice cream model and let my controller be an instance of an ice cream builder view controller to which I'll pass that ice cream model.

Now, lastly, before I add this as a child view controller, I need to remove any previous child view controllers to clean up my view hierarchy. Now I need to update the call sites to my helper method. Here I'm going to add that-- pass that pending presentation style and in the didBecomeActive with conversation method, I'm going to pass the current presentation style, which I can get as a property on my parent view controller. Should be right. Lastly-- actually, let's see-- let's see what this looks like if I can get this to work.

And I tap that Plus (+) button. My iMessage application goes to that expanded state and I'm presenting that ice cream builder view-- experience. However, you'll notice that when I tap Select nothing happens yet. Now, this is because my ice cream builder view controller also has a delegate protocol and I have an extension on my view controller here-- sorry to take you through that so fast-- that implements this delegate protocol by implementing the method iceCreamBuilderView controller didSelect iceCreamPart for iceCream. This is called whenever that Select button is tapped.

So, this is the right point for me to call that helper method I wrote, compose message, and I'll pass in the updated ice cream model from this method. The other thing I'm going to do is call dismiss here because my iMessage app is done preparing content and so I'd like my iMessage app to dismiss and display the staged message for the user.

Now when we launch this, tap the Plus (+) button and select an ice cream cone. I dismiss my iMessage app and the correct interactive message is stage ready for the user to send. And that's a quick demo of what it looks like to send an interactive message. Now back to Alex.

[ Applause ]

Thank you, Stephen. Great demo. So, Stephen has introduced to you and shown you how to use our APIs to insert content into the conversation and send it. Stephen also introduced presentation styles, so we're going to have a quick look at that in some more detail. We have two presentation styles, compact and expanded. Compact one is shown here and here we have expanded.

So, there's a couple of differences between these two presentation styles. In compact mode, you don't have access to the keyboard. You also don't have access to horizontal scrolling, swipe gesture recognizes. This is because in the compact mode the user can swipe left and right to switch between iMessage apps quickly.

However, you do have access to the input field, so whenever insert message is called in compact presentation style, the message will be inserted right into the input fields right away and the user can see it immediately. Subsequent calls to insert message will cause the current message to be replaced with the new one, so what you can do here is allow your user to iteratively build a message, see it as it progresses on the input field, and when they're happy with it they can send it. To contrast, in expanded presentation style we obviously don't have access to the input field here, but we do have access to horizontal swipe gesture recognizes and scrolling and you can use the keyboard.

The user can always transition your extension between these two styles by tapping on the top chevron from expanded to collapse to compact presentation style and when in compact they can tap on the chevron at the bottom of the screen to expand your app into expanded presentation style. So, when you're using iMessage apps, you need to be very responsive to the area in which your app is displayed. Whenever these transitions happen, you're going to get some callbacks on your principal class.

The willTransition to presentationStyle will be called as the transition starts and at the end when the transition is complete will call didTransition(to: presentationStyle). As Stephen showed, you can request presentation style expanded or compact by calling the requestPresentationStyle method on your principal class. And again, as was shown in the demo, you can call dismiss and this will dismiss your iMessage app and show the keyboard.

So, let's move on and talk about how we reply to a message. We have two cases here to cover. One case when your extension is inactive and there's a bubble in the transcript that the user taps and we're going to look at that first. We'll come back to the second case.

So, looking at our timeline again, this should be very familiar. The bubble is tapped by the user and your process will be launched. And now we go through exactly the same set of steps that we do when your app is launched from the app drawer. You'll get a didBecomeActive with conversation followed by the viewWillAppear and viewDidAppear.

When this is done, you'll-- your app will be presented in expanded presentation style. We always present apps in expanded presentation style when the user taps a button to launch it-- sorry-- taps a bubble in the transcript to launch it. Now, looking at our second case, the extension is already active. In this case it's showing UI in the compact presentation style.

So, looking again at our timeline, in this case, the bubble is tapped and the app is active. So, we're not going to call willBecomeActive or didBecomeActive. Instead, you'll receive a call to willTransition to presentationStyle expanded. This is followed by a didSelect message and conversation. This is called on your principal class and it lets you know that the user tapped on a bubble in the transcript and selected a message.

Then finally as the transition to expanded presentation style completes you'll get the didTransition to presentationStyle. So, now at the end of both of these timelines, your app is presenting the UI in the expanded presentation style and you'll want to get that message that was tapped, so this is done using the MSConversation's selectedMessage property and you can get this from the current active conversation on your principal class. You're going to want to show the selected message in your UI and allow the user to compose a response. Now I'd like you to hand you back over to Stephen for one final demo showing you how to implement reply in our sample app.

[ Applause ]

OK, so we left off with a staged message we're sending here, which I'll go ahead and send. And now if I go back to the conversation list and go to the other end of that conversation I have that received message. When I tap that message, my iMessage app is correctly launched; however, my ice cream building experience doesn't show the next step in the ice cream building process.

I'm not ready to select ice cream scoops yet and this is because every time that I currently present this view controller, I'm always passing in a new ice cream model object. What I need to do is look at the selected message on the conversation and use an ice cream model object from that selected message if it exists.

So, I'll go back up here to where I'm instantiating that ice cream builder controller and get a reference to my active conversation .Then I will use a failable initializer here that looks at the message on the conversation and if that initializer fails then I know that I don't have anything in progress and I'll just create that new ice cream model, but if it does exist then I can pass in the in progress ice cream. The other thing I want to do is make sure that I am passing the correct caption text depending on the ice cream part that has been selected.

To do so, I'm going to need to pass a new parameter into this method, which is the selectedIceCreamPart. Now, this message caption here I'll change to a declaration of the string type and then I'm going to switch on the selectedIceCreamPart to choose a correct message caption. Oh, and I also need to update the call site to my method here to pass in that new parameter.

Now when I launch my iMessage app, tap the Plus (+) button, send that first message and then go back to the conversation list and to the other end of the conversation and now I'll tap the received message and you'll see that my ice cream builder view controller is correctly set up to pick an ice cream scoop.

Now I can select some mint chip, send it, go back to the other end of the conversation once more, and select a topping. So, now you can see what it looks like to send interactive messages back and forth and accomplish a task collaboratively. However, you'll see that in my conversation UI, my partially built ice creams are starting to pollute my conversation a little bit.

I don't really need to see the partially built ice creams, I only really care about the completed ice cream. What I'd like to do is collapse those previous messages and maybe leave behind a succinct summary of those messages. To do so, I'm going to make use of a new object called MSSession to group messages together.

Here when I'm composing my message, I'm going to move my reference to my active conversation a little bit so that when I declare my session object I can look to see if I have a session already on the selected message and use that if it exists so that it will continue the same grouping. Otherwise, I'll create a new MSSession object. I'll then pass that session here into my initializer for MSMessage.

Now, the other thing I want to do is provide a nice summary of messages when they're collapsed. Here I'm going to declare a method or declare a variable called summary Text and here I'll choose the right summaryText depending upon the ice cream part that was chosen. Now I could take that summaryText variable and set it on my MSMessage object. And that's it. Oh, maybe I should actually assign it.

There we go. Now when I launch my iMessage app and go about the process of building an ice cream-- doesn't ice cream sound good right now? I can pick my flavor of ice cream and you'll see that that summary text has already been collapsed and shown for me there when I send my message. Now if I go-- sorry-- I'm going to go back to the first part of the conversation, tap this message once more. I think chocolate sounds like a good topping right now.

Now when I send that I have a completed ice cream message, but I'm only showing the one-- the one image here and I have a nice summary text throughout my transcript there. Also, when I go into my Ice Cream app here, you'll see my newly completed ice cream sticker ready for me to use however I'd like.

And that's it. We have a completed demo application. Now, this was an example of how to build an interactive sticker-building application, but you could take this to do anything. You can build-- you can integrate with wonderful services and build nice awesome collaborative games and I can't wait to see what you build. Now, back to Alex.

[ Applause ]

Thanks, Stephen. That was another awesome demo and Stephen implemented the reply and also introduced something new, the MSSession. So, let's dive into the MSSession. What we saw in the demo was a messy transcript containing lots of half-completed ice creams and we saw how to use MSSession to take that and turn it into something that looks a bit more like this, which is a lot neater and less cluttered.

It's great. So, to implement this, we used an MSSession object. We created an MSSession and then for our first message that was part of the session, we passed that session into its initializer and we have this session message. We can set some summary text and then we send it using the conversation's insert message, as we've seen before.

When we're replying to a session message, we don't want to create a new session; we need to reuse an existing one and we can get the existing session from the current conversation so the activeConversation selectedMessage. The selected message will have a second session property if it's a session message.

Then we take that session, we pass it into the sessions-- the messages initializer, as we saw before. We see here the summaryText is providing text for the message summary in the conversation transcript. You can now also omit this property or set it to nil and if you do that then there will not be a summaryText entry in the transcript, but your message will still partake in the session behavior. So, now we've covered the basics of the API and we've seen how to build a very simple iMessage app that sends interactive messages between your friends. We're going to move on and talk about some of the more advanced features of the API.

We'll start by looking at some more override methods on your principal class. We'll move on and talk about group conversations and finally we'll discuss how to identify the sender of a particular message. First, when the user sends a message, we realize that your app may wan to know that this has happened. For instance, if you're a game and the user plays a move, we need to know when to update our model to record that move and that should really only happen when the message is actually sent.

So, we have a didStartSendingMessage method on the principal class and this is called when the user taps the send button. Now, this is important. That message-- that does not mean that the message has actually been sent or delivered. All it means is the user has hit the Send button and it communicates the intent to send this message.

We also have, similarly, a didCancelSending. This happens when the user taps the cross in the top right hand corner of the message bubble on the shelf. didCancelSending is your opportunity to clean up any resources that have been accumulated during the composition of that message. And lastly we have didRecieveMessage.

didRecieveMessage happens-- is called when your app is running or active and a message has come in from one of the recipient-- one of the participants in the conversation. And it's a-- this is useful, for example, if your app has-- if your user is editing a session message and another message comes in on the same session, it's an updated state and you need to update the UI that represents this state. So, let's move on and talk about group conversations. iMessage apps are going to be used in group conversations, so you're going to have to consider that when you're building them. We have here an example conversation between three friends: Amber, Ben, and Chris.

Ben's getting ice cream for everyone and he sent out a question to Amber and Chris. Which topping do you-- would you like on your ice cream? Chocolate sauce or sprinkles? Amber is going to reply with chocolate sauce and Chris here is going to reply with sprinkles. Both users send these messages at exactly the same time.

Let's focus a little bit now on Ben and see what happens on his device as these messages arrive. First, Amber's reply is going to arrive on Ben's device and this is followed very shortly afterwards by the reply from Chris. So, you note Amber's reply was turned into a summary message and Chris's reply is shown as the last bubble in this conversation.

This could just as easily have happened the other way around and we should note that only the tapped message is available to your iMessage app, so we don't really have access to Amber's reply in our example. How do we deal with this? Well, right now we're recommending that you store state in the cloud and this means that the URL property of the MSMessage that's passed to and from the participants in the conversation only needs to-- only needs to take a token that represents the session and then you receive the current state of the session when the user taps on a message. And when a user sends a message, you should push the current state of the session up to the cloud. Moving on, we're going to talk about sender identifiers.

Here we have a conversation between Amber, Ben, and Chris again. In this scenario, Amber has replied with a preference for chocolate sauce. Chris has not yet replied. Focusing again on Ben's device, we have a message here and we don't know who to attribute it to, but we can find out some information about that.

Now, what we have here are some sender participant identifiers-- sorry, let me rephrase that. What we have here are some participant identifiers. Apple is very, very concerned about user privacy and we really very highly value the privacy of our customers, so we don't expose contact information at all to iMessage apps.

Instead, we provide these participant identifiers. On Ben's device, he has a local participant identifier and this is a UUID that represents Ben on this device. He also has two remote participant identifiers. These represent the other participants in the conversation. In this case, we have two. We have Amber and we have Chris.

Looking at the incoming message, we see that the message has a sender identifier and this will map to one of the local participant identifiers in Ben's list. Moving back to the big picture, looking at Amber and Chris's device, the sending messages have sender identifiers and these map to the local identifier on each device that sent that message.

You can use these identifiers to get a handle on the number of participants in a conversation. You can attribute messages to a sender so once you've received one message with a particular sender identifier and you receive a second with the same identifier, you know they've come from the same user. They can also be used in conjunction with a web service. They can be used to help establish identity. And one more thing.

You can use these identifiers or the string representation of these identifiers prefixed with a dollar sign in any of the text passed to the MessageTemplateLayout's text properties. You can also use-- similarly, you can use these identifiers in the text passed to the Messages sumamaryText. When Messages displays the UI for the bubbles with this type of formatting in their text, it will replace the identifiers with the contact name of the person that their identifier maps to.

These identifiers are unique on each device. If you noticed on the earlier slide, every device for every person the identifier was different. And they're scoped to the install of the iMessage app and what I mean by this is that the identifiers themselves will be stable over time, but if the user removes your iMessage app and then later reinstalls it, you'll get a completely different set of identifiers within the same conversation. You can get the sender participant identifier from an MSMessage using the senderParticipantIdentifier property and you can get the local participant identifier via conversation using the localParticipantIdentifier property. And you can get the list of remote participant identifiers from the remoteParticipantIdentifier property.

So, that wraps our more advanced topics on the API. We're going to talk a little bit more about supported platforms. Interactive messages will be delivered on these platforms: watchOS 3, macOS Sierra, and iOS 10. Of these platforms, only iOS 10 will actually generate messages. On macOS Sierra, the user can click on a bubble in the conversation transcript and the URL passed to the Messages URL property will be opened with Safari. That will only be if it's an https or http URL. On watch, you can hand off interactive messages to a device where you're able to compose a reply.

We also support some fallback for older platforms. These messages will be delivered to watchOS 2, iOS 10-- iOS 9, and OS 10.11, but they'll be delivered in a fallback format. There's two separate messages. The first one will be the image as provided in the template layout. The second one will be a URL as provided in the message. Now, again note if this is an https URL, we'll send it. If it's a data URL, we won't. So, we only-- we'll only send the https or http URLs in the fallback message.

So, that wraps up our talk. In summary, we have-- this week, we've introduced the Messages framework, iMessage apps. In the first part of this talk, Bhaskar and Lily introduced us to sticker packs and showed us how to create simple iMessage apps that create sticker content. Today, we've created iMessage apps that send interactive content and we've talked about how they work in group conversations and a few other things. Now I'm really excited to see what you guys go out and make with this API. I can't wait to see what you produce and that-- that's it.

[ Applause ]

For more information, you can check out our session's website on developer.apple.com. We have a related session. If you haven't already seen "iMessage Apps and Stickers, Part 1", then please go and watch it on the web. And that wraps up our talk. Thanks to Stephen for doing our demos. Thanks to you guys for coming.

[ Applause ]