Create iMessage Stickers (Remote Images + Analytics)
Starting in WWDC 2016 with the release of iOS 10, Apple allowed iOS developers to create their own iMessage sticker packs for the App Store. So, last week, I started to look into it; a little late to the game, I know.
Initially, I wanted to create a sticker pack whose contents I could remotely update. Moreover, I wanted to be able to track which sticker was the most popular and how many times a particular sticker was sent. Unfortunately, Apple doesn’t provide this functionality out of the box and makes it pretty tricky to implement it on your own.
Since the MSStickerBrowserView
doesn’t provide any callbacks when a sticker is selected, it ruled out using that for tracking what stickers were being sent.
After doing a bit more research, I saw a few posts on StackOverflow where people were trying to add UITapGestureRecognizer
’s to the MSStickerBrowserView
, but even then they were having limited results. This approach would only tell you that a sticker was selected, but was unable to tell you which one.
My next attempt was to use a UICollectionView
to display remotely loaded images, but it didn’t have the same look and feel as an iMessage Sticker Pack. Namely, you couldn’t click and drag the images into the iMessage transcript and the “shimmer” effect when you long pressed on a sticker was missing. However, it seemed to be the only way to incorporate analytics to see what stickers were being sent.
Playing around with this approach a little more, I was able to not only fully recreate the traditional sticker experience, but also allow the stickers to be created from remote images.
Tutorial
Let’s start off by creating a new iMessage App.
We can’t choose a “Sticker Pack App” as it offers no opportunity to write any code to fetch the images or add analytics. Instead, we’ll create an iMessage App that manages and presents a UICollectionView
of MSStickerViews
.
MessagesViewController Setup
First, we need to create a UICollectionView
to hold the MSStickerViews
.
@IBOutlet weak var stickerCollectionView: UICollectionView!
Next, we need to create an array to hold the MSSticker
objects which will function as the datasource of the UICollectionView
. Additionally, we’ll create an array to hold the names corresponding to the MSSticker
objects displayed in the UICollectionView
.
Having this list of filenames makes it easy to track and report what stickers are being sent.
//The name of the file that the image should be saved as. var fileName = [String]()var stickers = [MSSticker]()
Stickers From Remote Images
MSStickerView
’s require the image they display to be locally stored. So, first we’ll write code to fetch a list of remote URLs and save them locally on the device.
Next, we’ll load the images from their URLs and save the images to the DocumentsDirectory
.
for image in imageURLs { let lastPathComponent = (image as NSString).lastPathComponent self.fileName.append(lastPathComponent) self.createStickerFromRemoteImage(imageURL: image, fileName: lastPathComponent) }
We need to handle the local store of .gifs and .pngs differently. The full code is available on GitHub.
Displaying And Tracking Stickers
Now we have the ability to properly fetch both .gifs and .pngs from a URL and save them to the DocumentsDirectory.
Once the image is locally saved, we use the file path to the image to create an MSSticker
object. We maintain a collection, stickers
, which serves as the datasource to the collection view we made earlier.
Now, we get to the fun part. My final solution was to create a UICollectionView
where the UICollectionViewCell
’s contained an instance of a MSStickerView
.
First, let’s create our new custom UICollectionViewCell
class StickerCell: UICollectionViewCell { @IBOutlet var stickerView: MSStickerView! }
The first problem I ran into with my UICollectionView
approach was that the UICollectionViewCell
would “steal” the touch event from the underlying MSStickerView
. To resolve this, simply add the following code to the project
func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool { return true }
Now, multiple gesture recognizer events can happen simultaneously without cancelling out the other.
Next, we use the stickers
array we created earlier to populate the UICollectionView. For each cell, we populate the stickerView
with the corresponding sticker from the datasource.
The call to startAnimating()
starts the animation of the .gif, if applicable.
For each cell, we add a UITapGestureRecognizer
with the name of the sticker it is attached to. Whenever the cell is pressed, it’s easy to track and report the sticker that was selected.
Now, you have the ability to remotely load images, present them as stickers, and track their respective popularity.
Conclusion
Eventually, you could easily create an endpoint that returns a list of image URLs. So, as seasons change, new designs are generated, etc, you can easily update your sticker collection in real-time without submitting a new release.
For the full source code, check out the GitHub repo.
2020 Update:
I created this service to help non-technical designs create and profit from their own stickers packs: https://stickerspot.digitalbunker.dev/ If you’re a designer that wants to maintain rights to their art, but profit from iMessage Sticker Packs, it’s perfect for you!