Coding Guide: Building An In-App Message Center With Slack (With Video)

Written by: Reinder de Vries, August 30 2015, in App Development, Tutorials

Let’s combine 2 of the hottest things in the app world with each other: an in-app Message Center and Slack. In this guide, I’ll show you how you can code a stand-alone Message Center into your app.

In-App Message Center With Slack
In-App Message Center With Slack

You’ll be using the following techniques:

  • Slack Web & RTM API (Real Time Messaging)
  • Alamofire, Starscream, SwiftyJSON, and JSQMessagesViewController
  • Building up a socket connection
  • Working with CocoaPods
  • Some custom Swift programming, all in Xcode 6

There’s a video below, showing you the end-to-end, narrated, live build of the project.

It’ll explain you how to code the project, how it works, and what happens when the code is executed. Feel free to follow the guide by reading it, or by watching the video – it’s up to you!

You can find all the code for the project on GitHub: reinderdevries/RSSlackCenter.

Wanna know what the cool thing about the RSSlackCenter project is? It’s all stand-alone, client-side code. No need for third-party servers or services, no back-end programming: just straight talk from iPhone to Slack, making the development process considerably easier.

Happy coding!

Learn how to build iOS apps

Get started with iOS 11 and Swift 4

Sign up for my iOS development course Zero to App Store and learn how to build professional iOS 11 apps with Swift 4 and Xcode 9.

Why A Message Center With Slack?

A message center is a chat-like part of your application, used to give users of your apps real-time support and feedback. You can use it for a number of purposes:

  • Get in touch just moments after your app has crashed, to alleviate and solve the issue that arose (and avoid a negative App Store review)
  • Ask for feedback, chat with your customers: a big part of business validation and growth
  • Adding live support and live Q&A, either during office hours or 24/7, with or without automation

An in-app message center is like a future-proof, instant and fine-grained voicemail answering machine.

But… why build one into your app with Slack?

Slack’s getting popular for a number of reasons. For one, the Slack messaging app is used by the team that put a robot on Mars. The company is valued at 2.8 billion dollars, thanks to a whopping 1.1 million daily active users.

Coding clubs around the world shift from Facebook groups to Slack channels, customer support integrates with Slack private convo’s, and countless of creative teams rely on Slack for their day-to-day communications. The seemingly simple chat app transforms how we connect to our peers and customers, which makes it a great fit for in-app messaging.

Why A Stand-Alone Client-Side Implementation?

Slack has a solid API design on multiple levels. There’s the standard REST API for one-off requests to and from data, webhooks for automation, and a reliable web socket API for real-time communication.

Slack’s architecture makes for a good back-end, which could eliminate the use of go-between services. You could see the in-app message center as a client into Slack, simply putting an interface on its messaging capabilities.

First and foremost: a stand-alone implementation makes for an incredibly simple coding job. In turn, that makes for a good topic to write a coding guide about!

A stand-alone implementation has its downsides, too:

  • The message center is hard to integrate with third-party services, because there’s no common editable component except for Slack itself.
  • The security is a nightmare (more on this later), which makes it unadvisable to use the code of RSSlackCenter in a production environment without changes.
  • The current implementation of the bot user doesn’t facilitate filtering of messages when used over RTM (Real Time Messaging), it’s simply a system-wide shout channel everyone can blabber on.

However… it sure makes for a beneficial learning experience to build!

Let’s Code!

Watch the video or continue reading the written coding guide.

These are the rough steps we’ll take:

  1. Setup the Xcode project with CocoaPods and make a Bridging Header
  2. Build a basic implementation of JSQMessageViewController
  3. Get the Slack authentication tokens
  4. Connect to the Slack RTM API
  5. Configure channels and channel invitations
  6. Write routines for sending and receiving messages
  7. Top it off with avatars and a “user typing” indicator

The end result is a Message Center connected to Slack, (almost) ready for you to incorporate it in your own apps, and a ton of learning money in the bank.

Setting Up Xcode And CocoaPods

We’ll start with a fresh new Xcode project. Open Xcode and do the following:

  1. Choose File -> New... -> Project
  2. Pick template Single View Application from the iOS -> Application framework.
  3. Name the project RSSlackCenter, choose a fine Organization Name and Organization Identifier (ideally a reverse URL), pick Swift as the programming language, choose iPhone for devices and make sure Use Core Data isn’t ticked.
  4. Finally, click Next, choose a folder to save the project in, don’t create a Git repository, then click Create.
Xcode: New Project
Xcode: New Project

You’ll end up with an almost empty project. In order to make the project work, you use 4 different libraries:

To be able to use these in the project, we must import them through CocoaPods. Let’s do so:

  1. Right-click on the RSSlackCenter folder in the Project Navigator and choose New File....
  2. Pick the Empty template from iOS -> Other, then click Next.
  3. Name the file Podfile (no point, no extension) and save it in the same directory as the RSSlackCenter.xcodeproj file.

Finally, open the file in Xcode and write the following code in it:

source ''  
platform :ios, '8.0'  

pod 'Starscream', '0.9.4'  
pod 'Alamofire', '1.3'  
pod 'SwiftyJSON', '2.2.1'  
pod 'JSQMessagesViewController', '7.1.0'

This instructs CocoaPods to download the libraries and install them in the project.

Now, let’s first make sure you have CocoaPods installed on your Mac. Start the OS X Terminal app and type this on the command-line:

$ pod --version
Don’t type the dollar sign $, that’s just a universal code-slang prefix for “You should write this on the command-line”.

You’re good if a version number, like 0.38, pops up. If not, then type this to install CocoaPods:

$ sudo gem install cocoapods

Let it run, you’ll need to input your administrator password. If it doesn’t work, consult this guide or ask for help by leaving a comment.

Next, close the project and quit Xcode. This is very important! Once Xcode is closed, use Terminal and cd to go into the directory you saved the project in. Your directory is most likely different from mine, but it’ll look something like this:

$ cd /Users/reinder/Xcode/RSSlackCenter/

Once you’re in the right directory, type this:

$ pod install

A lengthy task starts and CocoaPods installs the right pods for you. You’ll see something like:

Analyzing dependencies  
Downloading dependencies  
Installing Alamofire (1.3.0)  
Installing JSQMessagesViewController (7.1.0)  
Installing JSQSystemSoundPlayer (2.0.1)  
Installing Starscream (0.9.4)  
Installing SwiftyJSON (2.2.1)  
Generating Pods project  
Integrating client project  
[!] Please close any current Xcode sessions and use `RSSlackCenter.xcworkspace` for this project from now on.

Important: from now on, use the RSSlackCenter.xcworkspace file instead of the .xcodeproj file!

Do this:

  1. With Finder, browse to the project directory.
  2. Double-click the RSSlackCenter.xcworkspace file.

Xcode opens. It should look like the image below. Notice the new folders and the Pods project?

Xcode: CocoaPods
Xcode: CocoaPods

Creating A Bridging Header

Of the 4 libraries, 3 are written in Swift and 1 is written in Objective-C: JSQMessagesViewController. Objective-C and Swift code can be mixed in one project, as long as you include an Objective-C Bridging Header. A Bridging Header forms a bridge (o rly?) between Objective-C and Swift, allowing you to use Objective-C methods, classes, etcetera in your Swift projects.

Configuring a Bridging Header has 3 simple steps:

  1. Creating a new file, RSSlackCenter-Bridging-Header.h
  2. Adding Objective-C import statements to the file
  3. Adding the right configuration value to the project’s Build Settings

First, right-click on the RSSlackCenter -> Supporting Files folder in the Project Navigator and choose New File.... Pick Header File from the iOS -> Source, name it RSSlackCenter-Bridging-Header.h, make sure RSSlackCenter is ticked next to Targets, and save the file.

Add the following code to the file:

#import <JSQMessagesViewController/JSQMessages.h>

This instructs the compiler to import the right header file for JSQMessagesViewController.

Finally, click on the project header (the big blue bar up top) in the Project Navigator, then select the RSSlackCenter Target in the list on the left. In the interface that appears, click on the Build Settings tab up top. Then, type bridging in the search box on the right. The list below it filters, and should show a key element called Objective-C Bridging Header.

Double-click in the empty column field next to it. A little input box gizmo appears. Enter the following text in it:


This instructs the compiler that the .h header file is the Bridging Header we want to use for the project. Xcode takes care of the rest!

Xcode: Configuring A Bridging Header
Xcode: Configuring A Bridging Header
The Bridging Header setting in Build Settings is just a path to a file, relative to the project root directory. Does Xcode complain it can’t find the file? Check where you saved RSSlackCenter-Bridging-Header.h, and work out which directory it’s in relative to the project root directory. The root directory is the one that has the .xcworkspace file in it. Then, adjust the Build Settings value accordingly.

Verify that the project is compiling correctly, including CocoaPods and Bridging Header, by pressing Command-B.

Programming The Message View Controller

Now that we’ve imported all the code dependencies into our Xcode project, it’s time to do some actual coding. We’ll implement the foundation of the messages view controller, the main component of the in-app Message Center.

Thanks to Jesse Squires, we’ve got a ready-to-use messages view controller at our disposal. It’s very similar to the Messages iPhone app, text bubbles included.

Creating RSMessageCenterViewController

First, create the new RSMessageCenterViewController class:

  1. Right-click on the folder RSSlackCenter, choose New File....
  2. Choose the template Cocoa Touch Class from category iOS -> Source.
  3. Name the class RSMessageCenterViewController, make it a Subclass of JSQMessagesViewController.
  4. Make sure that Also create XIB file is unticked, and the language is set to Swift.
  5. Click Next, then save the file in the right directory: RSSlackCenter under the project root (in the same directory as AppDelegate.swift). Make sure that RSSlackCenter is ticked, next to Targets.
  6. Finally, click Create.

A new file with some boilerplate code appears. It’ll most likely throw an error on the class definition, because the JSQMessagesViewController isn’t imported yet.

Solve the error by typing this below the first import UIKit statement:

import JSQMessagesViewController
Xcode: New JSQMessagesViewController subclass
Xcode: New JSQMessagesViewController subclass

What did we just do? We’ve simply created a JSQMessagesViewController subclass called RSMessageCenterViewController. Subclassing means we inherit all the code and functionality from the JSQMessagesViewController subclass, and can adjust it’s behaviour with our own code – which we’ll do now.

First, add the following code to the viewDidLoad method. Type it directly below the super.viewDidLoad(); line.

self.senderId = "1234";  
self.senderDisplayName = "me";

self.collectionView.collectionViewLayout.outgoingAvatarViewSize = CGSizeZero;

self.inputToolbar.contentView.leftBarButtonItem = nil;

Take a look at the GitHub project files if you’re unsure about where to put this code exactly.

It may have occurred to you that I type a semi-colon ; after each line. This is my personal preference for code formatting, but it isn’t required to do when programming in Swift. You can leave the semi-colons out, if you want to.

OK, what happens in the code?

  1. First, we assign the string 1234 to self.senderId. It’s a property we inherited from JSQMessagesViewController, and it’s required to be set when the class loads. Internally, it’s used to distinguish between the individual senders of messages.
  2. Then, in the same way we set the senderDisplayName property.
  3. Then, we set the size of the avatar image view to CGSizeZero, which makes it present but invisible.
  4. Finally, we disable the leftBarButtonItem of the chat input view. That’s the attachments button, and we won’t be needing it.

Running The App

Now, let’s run the app and see what happens! Do this:

  1. At the top of Xcode, select an iPhone model from the dropdown right next to RSSlackCenter. This sets which Build Target will be built, for which device model and architecture. If you connect your iPhone to your Mac, it’ll most likely show up here too!
  2. Click the Run (or Play) button or press Command-R. This builds (aka “compiles”) the project and runs it on the target device.
Xcode: Run
Xcode: Run

And… nothing happens! Bummer! But, why?

Well, we didn’t set a starting point for our app. By default, it uses a standard Storyboard file called Main.storyboard. In our project, we’re not going to use a Storyboard because we don’t have to: there’s nothing to put in there. All the layout and UI stuff is handled by the JSQMessagesViewController class.

So, let’s quickly adjust the error. First, do this:

  1. Open the Project Settings. You know, that blue bar at the top of the Project Navigator.
  2. Then, switch to the General tab and select the RSSlackCenter target.
  3. Then, remove the text inside the Main Interface field. This removes the default app starting point.

Then, open AppDelegate.swift by clicking on the file in the Project Navigator.

  1. Locate the application:didFinishLaunchingWithOptions: method (it’s the first one).

  2. Insert the following code right after the opening squiggly bracket {, and before the return true line:
    var msgVC:RSMessageCenterViewController = RSMessageCenterViewController();  
    msgVC.title = "Message Center";
    var navigationVC:UINavigationController = UINavigationController(rootViewController: msgVC);
    let frame = UIScreen.mainScreen().bounds;  
    window = UIWindow(frame: frame);
    window!.rootViewController = navigationVC;  

Whoah! What does the code do?

  1. First, it creates a new instance of RSMessageCenterViewController and assigns it to the variable msgVC. Essentially, that line creates a new message center view controller.
  2. Then, the title property on the instance is set to “Message Center”. That title will later show up at the top of the navigation controller.
  3. Then, we create another instance: UINavigationController. The msgVC is assigned as its root view controller. A navigation controller is one of those UI components that has a title at the top, a back button, and allows the user to navigate between different views. A root view controller is simply the first UI view controller it shows.
  4. Then, some magic with the frame happens. The dimension of the iPhone screen is stored in the constant frame (a not-changing variable) and a new window is created with the exact same dimension.
  5. Finally, the navigation controller is assigned to the root view controller property of window (confusing, I know). The method makeKeyAndVisible is required.

About the view controller hierarchy… if the iPhone were a pizza, the view hierarchy would govern the order of pizza layers. Like this:

Pizza Dough (bottom) -> Tomato Sauce -> Salami -> Cheese (top)

For us Swift coders, that’s:

UIWindow -> UINavigationController -> RSMessageCenterViewController


Now, let’s see what happens when we run the app. Are you seeing the Message Center pop up? (It crashes when you press the Send button. That’s OK!)

Xcode: Message Center
Xcode: Message Center

Coding The “Send” Action

Let’s solve that crash and code something functional into the app: the “Send” action. What happens when a user types a message in the app and hits send?

OK, first open the RSMessageCenterViewController.swift file and locate the class definition. It looks like this:

class RSMessageCenterViewController: JSQMessagesViewController  

We’re going to add a property to this class. What’s a property? Think of it as a variable that’s bound to a class, and available to any instance of that class.

Say we define a class Bicycle. A property of that class can be numberOfWheels, or color, or speed. Any instance of that class has these properties, like this:

var fixie:Bicycle = Bicycle();  
fixie.numberOfWheels = 2;

Now, back to the RSMessageCenterViewController class. Add the following on a new line below the first squiggly bracket { of the file.

var messages:[JSQMessage] = [JSQMessage]();

The first couple of lines now look like this:

import UIKit  
import JSQMessagesViewController

class RSMessageCenterViewController: JSQMessagesViewController  
    var messages:[JSQMessage] = [JSQMessage]();  

So, this is what that property line does:

  1. The var keyword “announces” to Xcode that we’re going to declare a variable. It’s a property because it’s declared in the class scope, i.e. in the class and not in a method.
  2. The messages is the name of the property.
  3. It’s followed by a colon, which is followed by the type [JSQMessage]. That means: the type of this property is array of JSQMessage instances.
  4. It’s followed by an assign sign. In math a single = means equals, in programming it means assign that to this. (In programming, “equals” is ==).
  5. Finally, we initialize the new property. We do that by writing the type of the property followed by (). Essentially, we call the constructor method on [JSQMessage]. Note the similarity with: var fixie:Bicycle = Bicycle().

Customizing JSQMessagesViewController

OK, now let’s create the implementation of JSQMessagesViewController. The framework requires us to include several methods in our class. The JSQMessagesViewController itself then calls upon these classes to configure and customize the message view. Internally, the messages view controller uses a collection view controller, a standard iOS UI component.

In total, it’s 6 methods we’ll code. Don’t worry, it looks like more work than it actually is.

Sending Messages

  • Responding to the Send button: override func didPressSendButton(button: UIButton!, withMessageText text: String!, senderId: String!, senderDisplayName: String!, date: NSDate!)

Displaying Message Data

  • Number of messages: override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int
  • Message data for array index: override func collectionView(collectionView: JSQMessagesCollectionView!, messageDataForItemAtIndexPath indexPath: NSIndexPath!) -> JSQMessageData!
  • Message “bubble” data for array index: override func collectionView(collectionView: JSQMessagesCollectionView!, messageBubbleImageDataForItemAtIndexPath indexPath: NSIndexPath!) -> JSQMessageBubbleImageDataSource!
  • Message cell for array index: collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell
  • Message avatar image for array index: override func collectionView(collectionView: JSQMessagesCollectionView!, avatarImageDataForItemAtIndexPath indexPath: NSIndexPath!) -> JSQMessageAvatarImageDataSource!

This “array” is of course the property messages, an array filled with instances of JSQMessage. Each instance stands for one message, and they’re referenced by index. Message 1 is stored at index number 0, message 2 at index 1, 3 at 2, and so on.


Responding To A Send Event

First, add the following method to the class. You can add it on a new line right below the closing squiggly bracket } of the viewDidLoad method.

override func didPressSendButton(button: UIButton!, withMessageText text: String!, senderId: String!, senderDisplayName: String!, date: NSDate!)  
    let message = JSQMessage(senderId: self.senderId, displayName: self.senderDisplayName, text: text);  
    messages += [message];


Whoah? What happens here?

Well, first of all: this method is called upon when the user taps the Send button. The framework executes the method and gives it 5 arguments. You can see these parameters in the method definition, i.e. the stuff after didPressSendingButton: a button, the message text, the ID of the sender, the name of the sender, and the data at which it was sent.

Arguments are the variables you send with a method call. In the method definition, they’re called arguments. Remember it like this: parameter is the parking space, argument is the automobile.

Inside the method, this is done:

  • First, a new constant is declared with name message. The type isn’t specifically declared, instead Xcode uses type inference to determine the type on its own. Saves time for us! After the assign sign, the JSQMessage constructor is called with a couple of arguments.
  • Then, the new message is added to the messages array. It’s done with the compound assignment operator “add and assign”; it basically inserts a new item into the array.
  • Finally, the finishSendingMessageAnimated method is called on self. That self is the current class, so finishSendingMessageAnimated is called on the current class RSMessageCenterViewController. The finishSendingMessageAnimated method is inherited from the JSQMessagesViewController superclass, and it takes one argument: should I animate? We provide true, so yes.

Number Of Messages And Message Data

OK, add the following 2 methods after the closing squiggly bracket of } of method didPressSendButton:

override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int  
    return count(messages);  


override func collectionView(collectionView: JSQMessagesCollectionView!, messageDataForItemAtIndexPath indexPath: NSIndexPath!) -> JSQMessageData!  
    return self.messages[indexPath.item];  

What these methods do is quite clear:

  1. Provide the number of total messages in the controller (required). The return keyword returns (o rly?) the result of the method.
  2. Provide the data for the message at index number X (required). That index number is indexPath.item, and we’re returning the right element from the messages array. Just like: return alphabet[12] results in m. The square bracket syntax is called subscript.

Customizing Message Bubbles

OK, new method. Do you know where to add it? Exactly, on a new line right after the closing squiggly bracket } of method messageDataForItemAtIndexPath!

Type this:

override func collectionView(collectionView: JSQMessagesCollectionView!, messageBubbleImageDataForItemAtIndexPath indexPath: NSIndexPath!) -> JSQMessageBubbleImageDataSource!  
    let factory = JSQMessagesBubbleImageFactory();

    return factory.outgoingMessagesBubbleImageWithColor(UIColor.lightGrayColor());  

Easy, right? This method creates a factory variable of type JSQMessagesBubbleImageFactory. That method is part of the JSQMessagesViewController framework, and we use it for preparing the graphic layout of the message bubbles.

For now it’s pretty simple: a gray bubble, but later we’ll change that.

Customizing Bubble Collection View Cells

OK, pre-final method. Add this one, you’ll know where:

override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell  
    let cell = super.collectionView(collectionView, cellForItemAtIndexPath: indexPath);

    // This doesn't really do anything, but it's a good point for customization  
    let message = self.messages[indexPath.item];

    return cell;  

Just like the code comment reads (the part after //): this method doesn’t do anything. It’s required by the framework, but all it does is create an instance of a collection view cell and return that. Still, would we want to customize these cells (the message cells/bubbles), here’s where we could do that.

Customizing Avatar Image

OK, last method. Add this on a new line after the closing squiggly bracket } of cellForItemAtIndexPath.

override func collectionView(collectionView: JSQMessagesCollectionView!, avatarImageDataForItemAtIndexPath indexPath: NSIndexPath!) -> JSQMessageAvatarImageDataSource!  
    return nil;  

That line, return nil simply means “return nothing”. We’ll customize this method at a later point in this guide, for now we won’t use user avatars.

Alright, let’s run that app! Do this:

  1. Press Command-R or click the Run button. Make sure you’ve selected an iPhone Simulator.
  2. Type something in the input field, then click Send.
  3. Did it work?
Xcode: Run The App
Xcode: Run The App

Note: check out the screenshot above. You can actually see the code we wrote behind it. Is yours as tidy as mine?

Code Structure And Formatting

When teaching, I often get the question: “Where do I put this code?” I’ve tried to be as clear as possible about code placement, but if you have any questions do not hesitate to leave a comment on this page.

This the code structure and formatting (as pseudo-code) you’ve been following so far:


    method(parameter_1, parameter_2)  
        return expression;  

    advanced_method(parameter_1, parameter_2)  
        self.method(argument_1, argument_2);  

Got all the terminology and formatting right? You’re seeing:

  1. A class definition
  2. A property definition
  3. A method definition, with 2 parameters, the method returns “expression”
  4. Another method that calls the first 2 method, with 2 arguments

As for the formatting, you’re doing this:

  1. Write each opening squiggly bracket on a new line, and indent 4 spaces (or 1 tab) on the next line
  2. Write each closing squiggly bracket on a new line, and unindent 4 spaces on the next line.
  3. Group methods and properties sensibly, creating empty lines between grouped code to improve readability.

Getting The Slack Authentication Tokens

OK, let’s talk about Slack for a second. Slack’s a powerful messaging platform, and it is highly customizable. We’ll make some sort of an extension to Slack: we’re adding functionality to the platform.

In short, it’s this:

  • Our code will create a Slack channel for an individual app user, to which it will add a bot and a customer support user (yourself, in this case).
  • The bot functions as an anonymous user, it’s not part of your Slack environment, but only temporarily chats with you and your team.
  • In our app, the Slack channel name will be something like z4ab-zaphod. The first 4 characters are random alphanumerics, then a dash, and then a random character name from the Hitchhikers Guide to the Galaxy stories (Arthur, Zaphod, Ford, Trillian, etc.). Each channel name must be unique, and this random naming convention avoids conflicts between channel names (to an extent).

Now, Slack has different methods of integrating with it. We’ll use these:

  1. The Slack RTM, which stands for “Realtime Messaging”. It’s a socket connection we can send messages to and receive messages from, in a realtime manner. Think of it as a dinner conversation: the moment someone speaks, all the other receive the spoken message.
  2. The Slack REST API, which is one of the most common integration systems on the web. It can only be used to send messages to Slack, or to actively retrieve messages from Slack. A socket connection pushes messages to us, with a REST API we can pull messages from Slack. Think of it as sending letters with the mail: you write a letter to your friend asking how his day went, and he writes back: “Fabulous!”

OK, here’s an important part: authentication. How does Slack know the message you’re sending comes from you?

For that we use 2 authentication tokens. They look like this:


We add those tokens to the messages we’re sending, and Slack reads them, and knows it’s us. At the same time, the authentication tokens pose as a password.

It gives anyone who has these tokens, access to:

  • Send and receive messages on Slack
  • Create, invite and close channels
  • Read messages history
  • Etcetera…

Security Considerations

Important. Take note of the following:

  • Any password must be kept secret. In our project, we’ll store the passwords in plain-text in a code file. It’s very easy to steal that password! That’s why the code in this guide is for educational purposes only. Do not use it in production, unless you’re taking measures to protect the authentication tokens.
  • Using the tokens in the project, while you’re programming it, is safe (unless your WiFi is compromised). Just don’t share your code without removing the tokens, and do not publish your app build (.IPA file) publicly. Always use HTTPS/SSL. If you share your authentication tokens publicly (like I did, on GitHub) make sure you revoke the tokens from Slack!

I’ll share some potential scenarios for overcoming these security issues by the end of the guide.

Just to make sure you understand, here’s a screenshot of a hex editor with the RSSlackCenter executable opened. That file is stored in iPhone Simulator, on your iPhone when you install an app, and even in iTunes when you sync your app. In other words: it’s public.

Searching through the file, I can find the plain-text authentication tokens without problems.

Hex Editor: Plain-text authentication tokens
Hex Editor: Plain-text authentication tokens

Alright, now onto getting those authentication tokens! We need 2 of them:

  • One for the bot, this token starts with xoxb
  • One for the admin user, this token starts with xoxp

A bot has significantly less administrative rights than a normal user (the “admin user” in our project), e.g. for creating and inviting users to a channel we need the admin user, but for talking over RTM we can use the bot.

The Bot Token

First, make sure you do have a Slack set up. Mine’s called “LearnAppMaking” and it looks like this:


If you don’t have a Slack yet, sign up over at

Now, with Slack open:

  1. Click on the team button in the top-left corner.
  2. A menu pops out, choose Configure Integrations
  3. On the page that appears, type “bot” in the search box or scroll down until you see an integration item called Bots.
  4. Click on the View button next to Bots
Slack: Adding A Bot Integration
Slack: Adding A Bot Integration

On the next page, do this:

  1. Choose a username for your bot. I’ve used i-am-bot. If you choose another, note the name somewhere, you’ll need it later.
  2. Then, click Add Bot Integration.
  3. You’ll now see a page similar to the one below. Locate the input field next called API Token, and copy its text. It starts with xoxb. This is the bot token. Don’t share it anyone! (If you accidentally do, click Regenerate below the box.)
  4. Paste the token somewhere private where you can reach it later, we’ll need it when we code. I suggest you use a small text file, e.g. TextEdit (or Sublime Text).
Slack: Bot Token
Slack: Bot Token

The User Token

OK, onto the next token! Do this:

  1. While being logged in to Slack, go to this page:
  2. Click Create New Application
  3. Fill out all the fields:
    • Name: RSSlackCenter
    • Team: Your Slack Team Name
    • Description: In-App Message Center for Slack
    • Link:
    • Redirect URI:
  4. Click Create Application

Note that these URLs are bogus, they don’t lead anywhere. We don’t really need them, but they need to be filled out anyway. If you were to create a true Slack application (other than for the purpose of obtaining a user token), you’d need to put your own server’s (or service’s) authentication URL in here.

OK, you’ll now see a page like this one:

Slack: My App RSSlackCenter
Slack: My App RSSlackCenter

Switch to that temporary file you added your bot authentication token to, and copy these values from the Application Page to that file:

  • Client ID
  • Client Secret (click Show first)

Now, normally you’d use these Application pages to create an entry with Slack for your client-side application. You integrate the app with Slack, but the user will have to log in in the app to be able to use your application.

For us, that’s impossible: the user who needs customer support, who wants to use your Message Center, can’t possibly log into your Slack. But, the app itself needs that Slack authentication access!

Fortunately, we can impersonate the app and obtain a user token ourselves. It would be a static user token, hard-coded into the app (see security concerns above), but nevertheless the only viable way to go.

So, let’s rig the system for a bit :-).

Do this:

  1. Get the Client ID from your text file, and go to this URL: Make sure you replace 1234 with your Client ID.
  2. Follow the pages, i.e. Authorize access to your account by clicking on your team name, then click Authorize.
  3. You’ll be redirected to as you can see in the screenshot below. Look at the URL, though. See that part after code? Copy everything between code=, and the next & sign. You’ll end up with a text string like this: 920837207.9e7c6aa888e. Copy this to your textfile.
Slack: Obtain auth code
Slack: Obtain auth code

Next, do this:

  1. Get your Client ID, Client Secret and Code ready.
  2. Go to Make sure you replace the following items in the URL:
    • abcd with Client ID
    • wxyz with Client Secret
    • 1234 with Code.

You’ll end up with something like this:

Slack: User Authentication Token
Slack: User Authentication Token

Copy the access_token and store it in your text file. It’s the text string between the quotes, after the access_token item. It’ll start with xoxp. Don’t share this token, it’s of equal value as an administrator password.

In case you accidentally throw it on the internet unprotected, go to and click the Trashbin (Delete) icon next to the application authorization item.

If all is well, you now have 2 text strings:

  • The Bot Authentication Token, starting with xoxb
  • The User Authentication Token, starting with xoxp

We’ll now use those in the Xcode project. In case you missed a step, or can’t figure the URLs out, watch the video – it’ll show you how in detail.

Overview: Technical Design

Before we continue to code, let’s look at an overview of the task ahead. When you’re coding, it’s often a good idea to make a technical design of the stuff you’re about to do.

In a technical design, you outline:

  • Frameworks and libraries you’re going to use
  • Elements of the entire process and system, like “the iPhone” and “the cloud”, or “REST API” and “Storage Server”
  • Names of important methods, variables, classes, etc.
  • “Flow” of the app: which action follows which event?

This is a simple technical design of our app, focused on the APIs:

Slack Message Center: Technical Design
Slack Message Center: Technical Design

Let’s look at the 3 different event paths in the diagram. See if you can follow along with the text, and do grab back upon the diagram when you’re coding the remainder of this guide.

Flow Of All Methods

App Start

When the user starts the app, this happens:

  1. A call to method configureChat(), in turn this method makes a call to Slack API method rtm_start(_:). The RTM Start API request returns information about the Slack environment, including a wss:// socket URL we use to open the socket communication.
  2. In turn 3 methods are called: connect(_:), getAvatars() and setupChannel(). The first one opens the realtime socket to Slack, the second downloads the avatar images for all the users, and the third sets up the Slack channel.
  3. The method setupChannel executes an if-statement: if the iPhone user’s preferences already contains a Channel ID, it’ll just invite the bot over to the channel. If it doesn’t contain the Channel ID, it’ll first generate a channel name, then creates and joins the new channel.

Sending A Message

It’s not about Slack, it’s about sending a message. This happens:

  1. The user presses the Send button in the message view, which calls method didPressSendButton(...).
  2. That method calls sendMessage(_:) of the RSMessageCenterAPI singleton class (we’ll code that later).
  3. That method then calls sendMessage(...) of the RSSocketAPI singleton class. In the code, we’ll separate each of the responsibilities in separate classes: the message center class, the Slack API class, and the socket API class.
  4. That method then calls send(_:), which will send the raw JSON data over the socket to Slack.

Receiving A Message

The socket can also receive messages, as long as it is open. The Starscream library will automatically listen to messages for us, and calls method websocketDidReceiveMessage(_:text:) for us when it picks up a new message.

This will result in a notification, an asynchronous way to send events from one part of an app to another. It’ll be picked up by the RSMessageCenterViewController class (the one with the message bubbles), and that will put a message on screen. The user is typing notification works in a similar fashion.

Now, as you can see in the diagram there’s a couple of different lines between all of the elements:

  • The solid arrow indicates an immediate connection, i.e. one method directly calls another method.
  • The dashed arrow indicates an asynchronous operation, i.e. the calling method will execute the next code block when a lengthy task is completed. A REST API request to Slack, over HTTPS, for instance: the app has to wait before the response comes back. These code blocks can be written as closures or notification handlers.
  • The dotted arrow indicate a time constraint. In the diagram, this means that sending and receiving of messages won’t work before the app is connected to the Slack RTM socket. We won’t code a safeguard against this, but it is unlikely a user types so fast it attempts to send before the app is connected.

Connect To The Slack RTM API

OK, let’s get back to coding with Swift!

Throughout the remainder of this guide you’ll need a bunch of settings, URLs, keys, etc. All of these resources are static, so they can be hard-coded into the app. To make the your code easier to read, code, and maintain, we’ll add these constants to a separate file.

Working With Static Constants

So, do this:

  • First, right-click on the RSSlackCenter folder in the Project Navigator, and choose New File.... (Open our project in Xcode, if you haven’t already.)
  • Then, from the iOS -> Source category, choose Swift File and click Next.
  • For Save As, choose filename Constant.swift, then make sure the file saves in the RSSlackCenter directory (rightalongside AppDelegate.swift).
  • Make sure the checkbox for RSSlackCenter, next to Targets, is ticked.
  • Click Create.

Next, copy-paste the following block of code right under the import Foundation line:

struct Slack {  
    /// Slack HTTPS API URLs  
    struct URL {  
        struct rtm {  
            static let start = "";

        struct channels {  
            static let create = "";  
            static let join = "";  
            static let invite = "";  
    /// Parameter constants as found in Slack data  
    struct param {  
        static let token = "token";  
        static let ok = "ok";  
        static let url = "url";  
        static let channels = "channels";  
        static let name = "name";  
        static let channel = "channel";  
        static let id = "id";  
        static let type = "type";  
        static let text = "text";  
        static let users = "users";  
        static let user = "user";  
        static let profile = "profile";  
        static let image_32 = "image_32";  
        static let color = "color";

        static let image = "image";  
        static let image_data = "image_data";  
    // Message types as found in Slack data  
    struct type {  
        static let message = "message";  
        static let user_typing = "user_typing";  
    // User tokens for the Slack bot and user. Note: storing these in a production app is very unsafe and not secure !!!  
    struct token {  
        static let bot = "...";  
        static let admin = "...";  
    // Misc. constants, the username of the Slack bot, and don't forget your towel.  
    struct misc {  
        static let usernames = ["arthur", "ford", "trillian", "zaphod", "marvin", "eddie", "hamma-kavula", "slartibartfast", "deep-thought", "agrajag", "vogon-jeltz"];  
        static let bot_name = "i-am-user";  

struct MessageCenter {  
    // Dictionary keys for NSUserDefaults  
    struct prefs {  
        static let channelID = "channel_id";  
    // Notification types as used in the Message Center  
    struct notification {  
        static let newMessage = "new_message";  
        static let userTyping = "user_typing";  

OK, that code is actually simple to explain: it’s a structure of structs. The structs above are nested, and ultimately they store static constants: immutable variables. We can use them to store app-wide settings, and use these settings like this:

// Output:


var user = json[Slack.param.user];  
// Outputs: nothing, but "user" contains now most likely the value for key "user" from "json"

Next, replace the bot and admin constants with the text strings you got from Slack earlier. Simply remove the ... and paste in your own values between quotes. Like this:

static let bot = "xoxb-8919087376-GbHm7Imq4Wv7shtJHbTFleV6";  
static let admin = "xoxp-4867574370-4867574392-9008552038-36b468";

3 API Classes

OK, now let’s get things going. You’re going to create 3 new classes, in a new folder. Do this:

  • First, right-click the RSSlackCenter folder in Project Navigator and click New Group.
  • Then, name the group APIs.

Then, right-click that new folder and choose New File.... Pick the iOS -> Source -> Cocoa Touch Class template, make sure you subclass NSObject and name the class RSMessageCenterAPI, then create the file in the RSSlackCenter directory right next to AppDelegate.swift. Repeat these steps for 2 more classes:

  1. RSSlackAPI
  2. RSSocketAPI

Each of these 3 classes will have its own responsibility, and interface directly with an API.

Message Center API

Let’s start with RSMessageCenterAPI. Add the following line right below the first import:

import Alamofire

Then, add the following static constant to the class itself:

static let sharedInstance = RSMessageCenterAPI();

This is a static class constant, which essentially turns our class into a singleton. I can talk long and short about singletons, but the essence of it boils down to this: a singleton is a single instance of a class. It ensures a single entry point of data for that class, and makes sure no copies of that class exist in the runtime of the app.

We simply use it to make coding easier, and to avoid copies of stuff we only want 1 instance of.

Next, below the line with the static constant, add this:

var botID:String?;

It’s a simple instance property called botID of type String?. That question mark means it’s an optional. More on that later.

Next, add the following empty method to the class:

func configureChat()  


Your class now looks like this:

import UIKit  
import Alamofire

class RSMessageCenterAPI: NSObject  
    static let sharedInstance = RSMessageCenterAPI();

    func configureChat()  


Slack API

OK, switch to the RSSlackAPI class. Add the following line just below import UIKit:

import Alamofire  
import SwiftyJSON

Then, add the following line to the beginning of the class:

static let sharedInstance = RSSlackAPI();

Finally, add the following method to the class:

func rtm_start(completion: (String) -> Void)  


The entire file now looks like this:

import UIKit  
import Alamofire  
import SwiftyJSON

class RSSlackAPI: NSObject  
    static let sharedInstance = RSSlackAPI();

    func rtm_start(completion: (String) -> Void)  



Important: Make sure you “get” the whole formatting thing. Where to put methods, where to put properties, and where to put imports. It’s fairly important that you understand this before you continue. If you don’t, backtrack on the code you’ve written so far and ascertain the structure of the code: where are the properties? Where are the methods? What are their parameters? What are their types? How are they structured with squiggly brackets and parentheses?

Using Closures

OK, let’s take a close look at the rtm_start(_:) method. It has just one parameter: a closure. A closure is simply a block of code (you could call it a “function” or “method”) you can pass around in the code, as if it were a variable. It makes your coding more fluid, and greatly improves readability.

The first and only parameter of method rtm_start(_:) has completion as it’s name, and (String) -> Void as it’s type. This type simply indicates that the closure takes one parameter of type String and returns Void, which means nothing.

If we were to use the closure, we could write:


How cool is that? It’s as if the variable is actually a function…

OK, to continue, add the following code in the rtm_start(_:) method between the squiggly brackets. It’s the code of the method.

Alamofire.request(.GET, Slack.URL.rtm.start, parameters: [Slack.param.token:]).responseJSON {  
        request, response, data, error in

        if error != nil  

        let json = JSON(data!);


        if let users = json[Slack.param.users].array  
            for user in users  
                if user[].string != nil && user[].stringValue == Slack.misc.bot_name  
                    RSMessageCenterAPI.sharedInstance.botID = user[].string;  

        if let url = json[Slack.param.url].string  

Whoah BATMAN!? What is that? Don’t worry, it’s easy. This is what the code does, line by line:

  • First, we’re making a call to method request of Alamofire.
    • That’s a framework method, which takes 3 parameters: the type of request (GET, POST, etc.), the URL of the request, and the body data that needs to be sent with the request.
    • Check that we’re using Slack.URL.rtm.start as the URL, which is a string from Constant.swift.
    • We’re sending one HTTP GET parameter with the URL: {"token": "<the token you put in>"}, all static constants. Still with me?
    • OK, then the return value of method request is chained to another method named responseJSON, which takes one parameter: a closure.
    • To put it simply: we make a request to a URL, give it 1 parameter, then tell it to execute a closure when the request finishes.
  • Then, the closure is coded between the squiggly brackets. Remember that a closure is a parameter, a variable, but it’s written as if it were a function (or method). The closure takes 4 parameters, as you can see from this line: request, response, data, error in. (If you hold key Command and then click on responseJSON, you can actually see the types of the closure parameters.)
  • OK, then onto the error checking. See the if-statement? That checks whether error is not nil. When it isn’t nil, it must contain an error. If so, we print out the error with println and then return. This halts execution of the closure.
  • Then, using object type JSON from framework SwiftyJSON we convert data to constant json. JSON stands for “JavaScript Object Notation” and it’s a data format that is returned by the Slack API. SwiftyJSON forms a wrapper around this object, making it easier for us to work with the data.
  • Then, to be able to debug the data that comes back from Slack, we println the value of json.
  • OK, then an if let line follows. This is called optional binding: the if-statement checks if a variable is not nil, and if it isn’t it’s bound to a constant. If it is nil, the if-statement clause isn’t executed. In this code block, the "users" key of json is attempted to be read, and converted to an array. The user data comes all the way from the data parameter, to the json variable.
  • Then, we loop over the users: for user in users. For each of the user items in users, this is done:
    • Is the user name not nil?
    • Is the user name equal to the bot_name? (Hard-coded in Constants.swift)
    • If that’s both true, the bot ID is saved in the botID property of the RSMessageCenter singleton. If it’s not true, it just continues to the next user in the loop.
    • Why do we search for the bot ID? These IDs are dynamic, and there’s no good way of finding it. The ID is needed later on, when we’re sending messages to Slack: those need to be identified with an ID, the bot ID.
  • Finally, we get what we came for: the code checks if the json response contains an URL, and if so, it calls the completion(_:) closure with 1 parameter: the url.

OK, got it? You can verify that your code compiles properly by pressing the Command-B keys. Xcode then builds your app, but doesn’t start it. A modal status message appears telling you whether the build failed or succeeded.

Socket API

Now, open the RSSocketAPI.swift file. Add the following imports to the top of the file:

import Starscream  
import SwiftyJSON

Starscream is the library we’re using to set up and manage the socket connection to Slack. You can see a socket as a continuously open line of communication between 2 peers. As opposed to to an ordinary internet connection, which sends and receives data in an open-then-close manner. Socket connections are generally used for push notifications, a type of server-to-client communication that does not require a client to poll for data first. To achieve that, the server and client set up a socket connection before sending data, keep it open all the time, and then send data when it becomes available.

Next, add the singleton functionality to this class too. On the first line of the class (after the opening squiggly bracket), write this:

static let sharedInstance = RSSocketAPI();

Next, add another instance property, right below it:

var socket:WebSocket?;

This is a property with name socket of type WebSocket (optional), part of the Starscream library. Note that this socket property is empty by default, it is only declared, but not initialized. It’s a reference to the websocket we’re about to open.

Next, let’s add a third property. Add this below the socket declaration:

var isConnected:Bool {  
    return self.socket?.isConnected ?? false;  

This is what’s known as a computed property. You can access it just like a normal property, but instead of simply returning a value it returns the result of a function. In our case, it’s the result of this expression:

self.socket?.isConnected ?? false;

The expression does this:

  • It reads the value of self.socket?.isConnected.
    • It finds out whether the socket has a currently active connection.
    • The socket can be nil, because it’s an optional. All optionals must be checked whether they are not nil, before they can be used.
    • We avoid checking by writing a question mark ? after the variable name, before accessing property isConnected on socket. This is called optional chaining.
    • By doing this, if socket is nil, the expression halts and returns false. We can safely assume there’s no connection when socket is nil.
  • The nil-coalescing operator ?? is used to set a default return value, namely false. You can use the ?? operator to return a default value in case an expression returns nil. It’s the short-hand form of this:
    if(variable == nil)  
        return "default";  
    } else {  
        return variable;  
  • In other words, if the socket is nil, it returns false, otherwise it returns the value of isConnected, which is true or false. OK?
Using Optionals

So, about optionals. You’ve used them a couple of times now, and I promised to explain what they are. It’s not complicated: an optional is a data type that can be nil. You use it in a scenario where the value of a variable, parameter or property may be nil.

Think about it like Schrödinger’s Cat: he’s either dead or alive, but you don’t know what is true until you look in the box (leaving aside quantum mechanics in this analogy).

For Swift code, this introduces a kind of type safety. Accessing properties that are nil results in unpredictable code, so forcing the programmer to check wheter a variable is nil or not makes the code stronger. Because the optionals can be checked at compile-time (i.e. when the app is built by the compiler), this results in fewer bugs at run-time.

The makers of Swift left you with a big box of tools to use when working with optionals, including optional chaining, optional binding, and force-unwrapping. Respectively: = "this silently fails when one of those ? is nil"

if let definitiveValue = self.optionalValue  

}!.textLabel!.text = "this can crash, unless you're certain that all of these aren't nil"
Socket Connection And Disconnection

OK, now let’s add the methods to connect and disconnect the socket. Add these 2 methods to the RSSocketAPI

func connect(url:NSURL)  
    self.socket = WebSocket(url: url);  
    socket?.delegate = self;  


func disconnect()  
    socket = nil;  

It’s fairly clear what happens here:

  1. The socket object is assigned to property socket, and is provided the parameter url of type NSURL when it initializes. It assigns self to the delegate property, which means the RSSocketAPI will serve as the delegate of certain WebSocket events. In reality, this means that the RSSocketAPI can customize functionality of WebSocket by implementing methods of an agreed-upon protocol. Finally, the socket connects.
  2. When disconnecting, the method simply calls upon the method disconnect of socket and then resets it back to nil.

The first new method most likely throws an error. You can check the error by clicking on the red dot in the gutter (the left part of the editor with the line numbers). Clicking it reveals:

Type RSSocketAPI does not conform to protocol WebSocketDelegate

In other words: we said that self (i.e., RSSocketAPI) would be the delegate of socket, except that we forgot to implement the actual protocol of the delegate!

Let’s fix that. Change the class definition of RSSocketAPI to this:

class RSSocketAPI: NSObject, WebSocketDelegate  

This tells the compiler that we’ll conform to protocol WebSocketDelegate. Changing this line will mostly cause another error, because we need to add some methods. We’ll do that in a minute!

Sending A Message

But first, let’s add 2 more methods:

func sendMessage(id:Int, type:String, channelID:String, text:String)  
    var json:JSON = [ id,  
        Slack.param.type: type,  channelID,  
        Slack.param.text: text];

    if let string = json.rawString()  


func send(message:String)  
    if let socket = self.socket  



Do you understand what happens here?

  1. The first method is a convenience function, it wraps another method with more descriptive parameters. It’s easier to use, and more convenient. In essence, it forms JSON object out of a dictionary with 4 key-value pairs: ID, type of message, channel ID and the actual message text. It then converts that JSON object to a “raw string”, and calls the send method with that string as its only argument.
  2. The second method is the one that does the actual writing over the socket. First, it checks whether self.socket isn’t nil, and check if it’s connected (if not, execution of the method halts). Then it outputs the raw JSON message string to the debugger with println, and finally writes the message string over the socket (to Slack).

You can use either of these 2 methods without problems, but it’s easier to use the sendMessage(...). It does some heavy lifting for you: turning the method parameters in a JSON data object.

Conforming To The Delegate Protocol

OK, now we’re going to make sure the RSSocketAPI conforms to the new WebSocketDelegate protocol.

Add these 4 methods to the RSSocketAPI class:

func websocketDidReceiveMessage(socket: WebSocket, text: String)  
    println("websocketDidReceiveMessage:: \(text)");  

func websocketDidConnect(socket: WebSocket) {  
    println("websocket is connected")  

func websocketDidDisconnect(socket: WebSocket, error: NSError?) {  
    println("websocket is disconnected: \(error?.localizedDescription)")  

func websocketDidReceiveData(socket: WebSocket, data: NSData) {  
    println("got some data: \(data.length)")  

These 4 methods won’t be called by us, but instead they’re called by the Starscream library when something happens! That’s what that delegate thing does: allowing us to customize responses to particular events.

All 4 methods respond to an event, and this is what happens:

  1. websocketDidReceiveMessage responds to a new incoming message. It’ll print it to the debugger (for now).
  2. websocketDidConnect responds to a connection event, it’s called when the websocket is connected.
  3. websocketDidDisconnect responds to a disconnection event, it’s called when the websocket is disconnected. A disconnection can be intentional, or in error. When there’s an error, we print that.
  4. websocketDidReceiveData is also called when there’s an incoming message. A socket can receive all kinds of data, and when it’s not a text string this method could come in handy for deciphering the data. We don’t need it, because all communication with Slack is in a text format.

Quick Recap

OK, quick recap:

  • We’ve set up 3 API singletons with distinct responsibilities.
  • We’ve written the first rtm_start call to be sent to Slack
  • We’ve set up pretty much all of the socket API

In other words: we’re ready to connect to the socket, and send a message!

Opening The Socket And Saying HI!

Opening The Socket

OK, switch over to file RSMessageCenterAPI.swift and locate the configureChat method.

Paste the following inside the method:

RSSlackAPI.sharedInstance.rtm_start {  
    (url:String) -> Void in

    RSSocketAPI.sharedInstance.connect(NSURL(string: url)!);


This is a call to the rtm_start method we programmed! As you see, it first accesses the sharedInstance class property on RSSlackAPI (the singleton), and on that instance calls the method rtm_start.

Remember that closure we talked about? We coded the rtm_start method to only have one parameter: a closure with name completion. The closure itself had 1 parameter too: a String. In the rtm_start method, we call the closure by writing:


We do that when the request to Slack finishes. In other words:

  • The rtm_start method contacts Slack, gets a bunch of data (including the socket URL)
  • When that finishes, we call the completion closure

The code of the closure is written above, in the squiggly brackets. It only does one thing, this:

RSSocketAPI.sharedInstance.connect(NSURL(string: url)!);

It calls the connect method we just coded, inside RSSocketAPI. It turns the socket URL string into NSURL, and gives that to the method as an argument. The code in RSSocketAPI in turn makes sure that the socket is actually connected.

Now’s a good time to look at that technical design diagram again. Can you figure out the flow of the methods in the code that you just wrote? What happens when?

OK, 2 tiny things remain. Open up file RSMessageCenterViewController.swift and locate method viewDidLoad.

Just before the end of the method, i.e. on an empty line right above the closing squiggly bracket }, add this:


This sets in motion the entire connect-to-Slack method chain.

Getting The Right Channel ID

Now, we haven’t coded the right channel into the app. But, it’d be cool if we could see that the code we did write actually works. To see that, we’re going to hard-code a small trick into the code we have.

At this point, run the app. Press Command-R and see the app come to life. Now, look at the screenshot below.

Slack: Hacking The Channel ID
Slack: Hacking The Channel ID

Then, do this:

  1. Make sure you’re seeing the Debug Window in Xcode. If not, use the middle button in the top-right corner to make it visible (see image). Use the 2 buttons in the bottom-right corner, and the top of the Debug Window, to make it bigger.
  2. In the debug output, you can see some of the data communication between Slack and the app. Search for a text that starts with "channels": [.
  3. Under that text, search for an entry that has "is_general": true set.
  4. Look for the ID of that entry, and copy it’s text.

Check the image if you have trouble figuring it out. The key is that you find the internal ID of the #general channel.

Saying HI!

Then, locate method didPressSendButton and add this code on a new line before self.finishSendingMessageAnimated(true):

RSSocketAPI.sharedInstance.sendMessage(0, type: "message", channelID: "...", text: text);

Paste your #general channel ID in place of the ....

The entire method now looks like this:

let message = JSQMessage(senderId: self.senderId, displayName: self.senderDisplayName, text: text);  
messages += [message];

RSSocketAPI.sharedInstance.sendMessage(0, type: "message", channelID: "...", text: text);


As you’ve guessed correctly, this new line sends a message we type to Slack.

Then, finally, run the app with Command-R. Type something into the text field and tap Send. Open up your Slack, #general channel, and if all wen’t OK, you’ll see your message!

It works!
It works!

Getting On The Right Channel

OK, it works! Awesome :-).

Now, let’s make sure that we’re talking on the right channel. See, now everything happens on the #general channel and we don’t want that. Every user that talks to customer support should have their own channel!

Let’s make that happen.

Generating The New Channel Name

First we’re going to create 2 utility methods that will generate a new channel name for us. As I explained earlier, the channel names all contain 4 random characters, a dash, and a predefined character name from The Hitchhikers Guide to the Galaxy.

Open RSMessageCenterAPI.swift and add these 2 methods to the end of the class.

func randomStringWithLength(length: Int) -> String  
    let alphabet = "1234567890abcdefghijklmnopqrstuvwxyz";  
    let upperBound = UInt32(count(alphabet));

    return String((0..<length).map { _ -> Character in  
        return alphabet[advance(alphabet.startIndex, Int(arc4random_uniform(upperBound)))]  


func getRandomChannelName() -> String  
    let prefix = self.randomStringWithLength(4);  
    let username = Slack.misc.usernames[Int(arc4random()) % Int(count(Slack.misc.usernames))];

    return "\(prefix)-\(username)";  

With a big thanks to MattDiPasquale.

What happens in these 2 methods?

  1. The first method outputs a random text string with a length of parameter length.
    • It uses 2 constants: alphabet, the possible text characters to choose from, and upperBound, the string length of alphabet.
    • It then executes a very dense function. Let’s start at inside of the code:
      • Function advance increments the start index of alphabet by a random amount. It’s used as a subscript for alphabet, so essentially it returns a random character from alphabet.
      • The map method is executed on a range of 0 to length, which in turn calls the advance function. The result is a string of Character instances, that is turned into a String.
    • In other words: it generates a random string of length, based on random characters from alphabet.
  2. The second method outputs the actual channel name, based on the random string and a random item from Slack.misc.usernames. Using string interpolation the right channel name string is returned.

Setting Up The Slack Channel

First, add the following property to the top of the RSMessageCenterAPI:

var channelID:String?;

Then, add the following method to the class:

func setupChannel()  
    if let channelID = NSUserDefaults.standardUserDefaults().stringForKey(MessageCenter.prefs.channelID)  
        self.channelID = channelID;

        let channel_name = self.getRandomChannelName();

        RSSlackAPI.sharedInstance.channels_join(channel_name) {  
            (channelID:String) -> Void in

            NSUserDefaults.standardUserDefaults().setValue(channelID, forKey: MessageCenter.prefs.channelID);

            self.channelID = channelID;


This will mostly cause a few errors, which we’ll solve in a minute. First, what does this method do?

  • First, it attempts to look for a string with key MessageCenter.prefs.channelID in the user defaults. That’s a standardized in-app container for storing simple user preferences. The code looks if a previous channel ID is already stored in the app, because then we don’t have to create a new channel (it already exists). Using optional binding, it’ll look for the value and if it’s not nil the first clause of the if-statement is executed. If it is nil, the else clause will be executed.
    • When the channel exists, the found channelID constant is assigned to self.channelID for future reference, and a call to method inviteBotToChannel is made.
    • When the channel doesn’t exist, a new channel_name is generated. A Slack API call called channels_join is made, and when it completes (see the closure?) the channel_name is stored in the user defaults. Then, just like the other clause, the channelID is saved and method inviteBotToChannel is called.

Creating And Joining The New Channel

Let’s first code that missing channels_join method. Switch over to file RSSlackAPI.swift and add the following method to the class:

func channels_join(channel_name:String, completion: (channelID: String) -> Void)  
    Alamofire.request(.GET, Slack.URL.channels.join, parameters: [Slack.param.token: Slack.token.admin, channel_name]).responseJSON {  
        request, response, data, error in

        if error != nil  

        let json = JSON(data!);

        if  let channel = json[].dictionary,  
            let channelID = channel[]?.string  
            completion(channelID: channelID);  

This method has 2 parameters:

  • channel_name of type String
  • completion, a closure of type (channelID: String) -> Void. The closure takes 1 parameter, channelID.

Just like before, we use Alamofire to make a request to the Slack Web API. The constant Slack.URL.channels.join is used, which contains the URL for this API request.

We send 2 GET parameters with the call:

  • The admin token, in Slack.token.admin
  • The channel name, parameter channel_name

Note that the keys of these 2 parameters are also constants from Constant.swift. Also, this Slack Web API creates and joins the new channel. It creates if it doesn’t exist, and joins after that. When the user has already joined the channel, no error is returned.

When the request finishes and the response is returned, a closure is executed. It checks for errors, and extracts the channelID (not to confuse with channel name) from the returned data. Thanks to SwiftyJSON we can cleverly extract the ID from the "channel" dictionary, and then find the "id" property in it.

Ultimately, our own completion closure is called with 1 argument: channelID. Now, backtrack your code and realize that the code in this closure is what we just wrote in method setupChannel:

NSUserDefaults.standardUserDefaults().setValue(channelID, forKey: MessageCenter.prefs.channelID);

self.channelID = channelID;



Inviting The Bot To The New Channel

OK, now switch back to RSMessageCenterAPI.swift and add this method to the class:

func inviteBotToChannel()  
    if(self.channelID == nil || self.botID == nil)  

    RSSlackAPI.sharedInstance.channels_invite(self.channelID!, userID: self.botID!, completion: nil);  

What happens here? Quite easy:

  • If either channelID or botID is nil, the execution of the method halts. We can’t request the bot to be invited to a channel if one of them is unknown…
  • Then, a call to channels_invite is made.

Let’s code that method channels_invite! Add the following method to class RSSlackAPI:

func channels_invite(channelID:String, userID:String, completion: (() -> Void)?)  
    Alamofire.request(.GET, Slack.URL.channels.invite, parameters: [Slack.param.token: Slack.token.admin, channelID, Slack.param.user: userID]).responseJSON {  
        request, response, data, error in

        if error != nil  

        let json = JSON(data!);


        if(completion != nil)  

Aah, again one of those Slack Web API request. Can you find out what’s happening?

The key is the data we send to Slack: invite userID to channel with channelID. In this case, the userID is actually the ID of the bot user. When we created the channel, only the admin user was invited over to the channel. Slack requires you to join a channel before you can post messages to it. The bot user wasn’t invited yet, so that’s what we did here.

Now, this method does have a completion closure that’s executed once the request finishes. As you can see in inviteBotToChannel, we’re not using it: it’s nil.

Finishing Touch: Adjusting The Message Sending

Are we done yet? Almost! Add the following code to the closure in configureChat:


Right under the connect call, so the entire method looks like this:

func configureChat()  
    RSSlackAPI.sharedInstance.rtm_start {  
        (url:String) -> Void in

        RSSocketAPI.sharedInstance.connect(NSURL(string: url)!);



Next, change the method that sends the messages to Slack. Add the following method to the RSMessageCenterAPI class:

func sendMessage(text:String)  
    if RSSocketAPI.sharedInstance.isConnected && channelID != nil  

        RSSocketAPI.sharedInstance.sendMessage(messageID, type: "message", channelID: channelID!, text: text);  

Then, add this property to the top of the RSMessageCenterAPI class:

var messageID:Int = 0;

We could add this code right into RSMessageCenterViewController, but that wouldn’t be good coding. We set up 3 API’s and 1 view controller to each handle their own responsibilities:

  • Manage the socket (RSSocketAPI)
  • Manage the Slack Web API (RSSlackAPI)
  • Manage the message center calls and data (RSMessageCenterAPI)
  • Show messages on screen and initiate message sending (RSMessageCenterViewController)


Now, switch to the RSMessageCenterViewController.swift file and locate the didPressSendButton method. Replace the line with sendMessage with this:


Much cleaner, right? Then, run the app with Command-R! Open your Slack, click on the new channel. In the app, type something… does it show up in the new channel?

(For fun: type something back. If you look in the Debug Window, you can see that the data actually comes in!)

Receiving Messages

Alright, it’s time for the final keystrokes of this coding guide: receiving messages from Slack. This is where it all comes together!

It’s fairly easy. Switch over to RSSocketAPI.swift and replace the websocketDidReceiveMessage method with this:

func websocketDidReceiveMessage(socket: WebSocket, text: String)  
    println("websocketDidReceiveMessage:: \(text)");

    if let dataFromString = text.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)  
        let json = JSON(data: dataFromString);

        let type = json[Slack.param.type].string;  
        let channel = json[].string;  
        let user = json[Slack.param.user].string;  
        let text = json[Slack.param.text].string;


        if(channel != RSMessageCenterAPI.sharedInstance.channelID)  

        if(type == Slack.type.message)  
            var info:[String: String] = [Slack.param.text: text!, Slack.param.user: user!];  

            NSNotificationCenter.defaultCenter().postNotificationName(MessageCenter.notification.newMessage, object: nil, userInfo: info);  

        if(type == Slack.type.user_typing)  
            NSNotificationCenter.defaultCenter().postNotificationName(MessageCenter.notification.userTyping, object: nil, userInfo: nil);  

Remember that the websocketDidReceiveMessage method responds to a new message being sent from Slack, over the socket, to the app?

This is what happens then:

  • First, we attempt to convert the string into NSData and send that into a JSON object from SwiftyJSON. At first we had a raw JSON string from the socket, but now we have a usable JSON object.
  • Then, we extract the type of the message, the channel it was sent on, the user that sent it, and the text of the message from the JSON object. It’s also output to the Debug Window.
  • Then, pretty important: if the message we received isn’t sent on our channel, we ignore it. (This also raises a security concern.)
  • Then, we respond to 2 types of messages: the message type, and the user_typing type. For both, we send out a notification. Notifications are an easy way to distribute information across your app, particularly if there’s multiple parts of the code that need to respond to such information. Using a listener, you can respond to posted notifications. Here, we’re just sending the notification. Note that for the message, we also give the notification some extra information: what’s the text of it, and who sent it.

Listening For Notifications

Let’s listen in for those notifications. Switch over to the RSMessageCenterViewController class and add these 2 lines to the viewDidLoad method. You can add them on a new line right above configureChat.

NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("onNewMessageReceived:"), name: MessageCenter.notification.newMessage, object: nil);  
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("onUserTypingReceived:"), name: MessageCenter.notification.userTyping, object: nil);

These 2 lines of code add 2 observers for notifications. The type of notification is specified, as well as the selector that needs to be called when the notification is received.

In other words: call method onNewMessageReceived when a notification with name MessageCenter.notification.newMessage is posted. That posting happens in RSSocketAPI, when a new message is received via the socket.

Responding To Notifications

First, let’s code the onNewMessageReceived. Add the following method to class RSMessageCenterViewController:

func onNewMessageReceived(notification:NSNotification)  
    if  let info = notification.userInfo as? [String: String],  
        let text = info[Slack.param.text],  
        let user = info[Slack.param.user]  
        let message = JSQMessage(senderId: user, displayName: user, text: text);  
        messages += [message];

        self.showTypingIndicator = false;


This method does this:

  • First, extract the right data from notification.userInfo. That’s the stuff we put in earlier, when posting the notification (the message text and sender).
  • Then, create a new JSQMessage instance and provide it the sender ID, display name of the user (identical to the sender ID), and the actual message text.
  • Then, add that instance message to the local messages array. This is the same array we earlier input our own messages in.
  • Then, make sure that the typing indicator is removed.
  • Finally, reload the collection view to make sure the new data is shown.

Showing The User Typing Indicator

All good messaging apps show you when the person you’re talking to is typing a message. It’s a requirement for any customer support center, so you know you’re being heard.

Let’s code it!

First, add this property to the RSMessageCenterViewController class:

var showTypingIndicatorTimer:NSTimer?;

It’s a timer, we’ll need it later. Then, add this method:

func onUserTypingReceived(notification:NSNotification)  
    self.showTypingIndicator = true;

    showTypingIndicatorTimer = nil;

    showTypingIndicatorTimer = NSTimer.scheduledTimerWithTimeInterval(4.0, target: self, selector: Selector("onTypingIndicatorTimerFire"), userInfo: nil, repeats: false);  

This method does this:

  • First, show the typing indicator by setting the inherited property to true.
  • Then, invalidate and erase the timer (more on this later).
  • Finally, post a new timer action. In 4 seconds after the timer instance is created, method onTypingIndicatorTimerFire is called.

What for?

Well, we’re essentially debouncing the typing indicator. See, the socket receives a number of user typing events. These events are highly irregular, so if we would switch the typing indicator on and off according to these events, it would flicker a lot.

To counter the flickering, we introduce a grace period. Every time a user typing event occurs, the timer is reset, and the indicator is shown for at least 4 seconds. If more than one events occur in this period, the indicator doesn’t flicker because it’s already showing. When there’s no events for longer than 4 seconds, the indicator is removed.

The result? No flickering, and a reliable indicator. Complete the code by adding this method to the class:

func onTypingIndicatorTimerFire()  
    self.showTypingIndicator = false;

    showTypingIndicatorTimer = nil;  

This is what happens when the timer fires: it removes itself. When a new event comes in, a new timer is created. When another event comes in, the timer resets. When the timer runs out, it doesn’t fire again until a new event comes in. It’s essentially a countdown timer ;-).

Showing User Avatars And Colors

Ready to see your progress? If your code’s all fine, press Command-R and try out typing. Send a message from Slack to the app, and you’ll see the typing indicator and your messages coming in!

Now, the message bubbles aren’t right yet, so let’s finalize the app by making it a little bit prettier.

OK, we’re going to show the Slack users’ avatars and colors. It’ll personify the customer support user in the app, and make it way cooler.

First, open RSMessageCenterAPI and add these 2 properties:

var users:[String: [String: AnyObject]] = [String: [String: AnyObject]]();  
var users_avatar:[String: NSData] = [String:NSData]();

They’re tracking the user data we receive from Slack. Their variable types look a bit scary, but they’re respectively a dictionary of dictionaries, and a dictionary of key-value String and NSData. The former will store user data like name and ID, and the latter will store user avatar image data.

Next, add this method to the same class:

func getAvatars()  
    for (id, user) in self.users  
        if let url = user[Slack.param.image] as? String  
            Alamofire.request(.GET, url, parameters: nil).response {  
                request, response, data, error in

                if error != nil  

                if data != nil  
                    self.users_avatar[id] = data!;  

We’ll call it in a bit, but for now, this is what it does:

  • Iterate over self.users, with key id and value user.
  • If user contains a key called "image" (stored as Slack.param.image) that can be casted to String, continue with:
  • Getting that URL (with Alamofire), and when it completes, store it in self.users_avatar. It’s keyed with the user id.

In other words: it downloads all the user avatars!

Finally, add the following line to configureChat, a new line right above self.setupChannel();:


The entire method now looks like this:

func configureChat()  
    RSSlackAPI.sharedInstance.rtm_start {  
        (url:String) -> Void in

        RSSocketAPI.sharedInstance.connect(NSURL(string: url)!);




Then, switch over to RSSlackAPI.swift and locate the rtm_start(_:) method. Inside it is a for-loop, and inside that for-loop is an if-statement. Below that if-statement, type this:

var user_data = [String: AnyObject]();

if  let id = user[].string,  
    let profile = user[Slack.param.profile].dictionary,  
    let color = user[Slack.param.color].string,  
    let image = profile[Slack.param.image_32]?.string  
    user_data[Slack.param.color] = color;  
    user_data[Slack.param.image] = image;

    RSMessageCenterAPI.sharedInstance.users[id] = user_data;  

That code will extract the right user data from the rtm_start response, and store it in the users property of RSMessageCenterAPI. The entire for-loop now looks like this:

for user in users  
    // Figure out user ID of bot  
    if user[].string != nil && user[].stringValue == Slack.misc.bot_name  
        RSMessageCenterAPI.sharedInstance.botID = user[].string;  

    // Store user data in RSMessageCenterAPI for later reference  
    var user_data = [String: AnyObject]();

    if  let id = user[].string,  
        let profile = user[Slack.param.profile].dictionary,  
        let color = user[Slack.param.color].string,  
        let image = profile[Slack.param.image_32]?.string  
        user_data[Slack.param.color] = color;  
        user_data[Slack.param.image] = image;

        RSMessageCenterAPI.sharedInstance.users[id] = user_data;  

Next, add this method to RSMessageCenterAPI. It’s a utility function, thanks to arshad, that converts a hex-color like #00ff00 to an instance of UIColor.

func colorWithHexString (hex:String) -> UIColor {  
    var cString:String = hex.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()).uppercaseString

    if (cString.hasPrefix("#")) {  
        cString = (cString as NSString).substringFromIndex(1)  

    if (count(cString) != 6) {  
        return UIColor.grayColor()  

    let rString = (cString as NSString).substringToIndex(2)  
    let gString = ((cString as NSString).substringFromIndex(2) as NSString).substringToIndex(2)  
    let bString = ((cString as NSString).substringFromIndex(4) as NSString).substringToIndex(2)

    var r:CUnsignedInt = 0, g:CUnsignedInt = 0, b:CUnsignedInt = 0;  
    NSScanner(string: rString).scanHexInt(&r)  
    NSScanner(string: gString).scanHexInt(&g)  
    NSScanner(string: bString).scanHexInt(&b)

    return UIColor(red: CGFloat(r) / 255.0, green: CGFloat(g) / 255.0, blue: CGFloat(b) / 255.0, alpha: CGFloat(1))  

We’re almost there!

Switch over to RSMessageCenterViewController.swift and locate the messageBubbleImageDataForItemAtIndexPath method. Replace it contents with this:

let message = self.messages[indexPath.item];  
let factory = JSQMessagesBubbleImageFactory();

if(message.senderId == self.senderId)  
    return factory.outgoingMessagesBubbleImageWithColor(UIColor.lightGrayColor());  

if  let user = RSMessageCenterAPI.sharedInstance.users[message.senderId],  
    let color = user[Slack.param.color] as? String  
    // This is the Slack user color  
    return factory.incomingMessagesBubbleImageWithColor(RSMessageCenterAPI.sharedInstance.colorWithHexString(color));  

return factory.outgoingMessagesBubbleImageWithColor(UIColor.lightGrayColor());

Instead of showing a light gray color for all messages, we now show the color of the Slack user. This code also aligns the message bubbles on the left and right, respectively for incoming and outgoing messages.

Then, find the method avatarImageDataForItemAtIndexPath and replace its contents with this:

let message = self.messages[indexPath.item];

if let data = RSMessageCenterAPI.sharedInstance.users_avatar[message.senderId]  
    return JSQMessagesAvatarImageFactory.avatarImageWithImage(UIImage(data: data)!, diameter: UInt(kJSQMessagesCollectionViewAvatarSizeDefault));  

return nil;

This will retrieve the user avatar data from RSMessageCenterAPI, and use that to generate an avatar image (from a factory). That’s returned to the message view controller, displaying the user avatar image.

Aw yiss! You did it.
Aw yiss! You did it.


AWESOME! It worked! You just built a Message Center in your app, connected to Slack, without use of a third-party back-end service. Well done!


Alright, there are ways to make this app better and production-ready. Right now it’s great for learning, but what if you actually wanted to integrate this into your own app?

These are areas that can be improved:

  1. Unsafe storage of priviliged data. These authentication tokens in Constant.swift are essentially admin passwords, and give access to many privileged actions in Slack. It’s near impossible to avoid including auth keys in your client-side app altogether, but there are options. Chris Hulbert mentions a viable security-through-obscurity method on his blog and plenty of StackOverflow topics include pointers towards making your authentication safer. It’s key that you remove the single point of entry from the app. The auth token inside the app code is just too big, and revoking it means you need to send in another update of your app. It’s better to store those authentication tokens on a separate server (a 3rd peer in the entire system, unfortunately) and manage OAuth2 authentication between this server and the app on a per-client basis. That way you know exactly who’s doing what, and you can revoke access without compromising your main security key. The downside of this, is that you need a separate server with it’s own REST API.
  2. Websocket contains all data. Right now, the websocket contains all the data sent to all clients. It’s essentially a communication channel the size of a soccer field, and all listening to it can hear all messages. Filtering happens in the app, but that doesn’t keep a user with malicious intent and physical access to a subscribed device to eavesdrop on all conversations. Ideally, you’d channel all data through a non-clientside filter and only send the appropriate parts to each relevant app. Given that you’ve implemented a REST API that manages authentication per-client, you can easily connect that to the Slack socket, and distribute messaging to the client apps from there.
  3. Ping and pong. Slack requires you to adopt a “ping and pong” mechanism, that essentially sends messages over the socket to indicate it’s still alive. We currently haven’t added that to the project, but it’s a requirement for reliably coding a socket connection. At this time, the code doesn’t disconnect from Slack. Ideally, you’d do that when the app closes or leaves the Message Center (for a production app).
  4. Promises. Although the code base is currently too small to effectively use promises, it is a recommendation for bigger projects. Promises untangle asynchronous code, especially their callbacks, by distributing future results of asynchronous tasks.
  5. Singletons. Unless you’re a pro in maintaining the responsibilities of code classes, don’t use singletons. Before you know it, you’re creating a God class with state information for the entire app, which is hell to debug and document. Instead, use singletons for connectors that need to have a single point of communication: web APIs, sockets, database access, etc.
  6. Data caching. It’s just an optimization: we don’t cache user data from Slack, but there’s a small chance that data changes in an instant. It’s better to cache it for a sensible period, as opposed to downloading it every time the app starts.

Learn how to build iOS apps

Get started with iOS 11 and Swift 4

Sign up for my iOS development course Zero to App Store and learn how to build professional iOS 11 apps with Swift 4 and Xcode 9.


Do you have a question about this coding guide, or do you need help with solving a coding problem? Feel free to ask a question in the comments section on this page. I’d be happy to help you out!

Further Reading

Throughout the coding guide we’ve used a number of resources. Sharpen your knowledge about programming by learning about these several principles, products, toolsets and libraries.

Reinder de Vries

Reinder de Vries is a professional iOS developer. He teaches app developers how to build their own apps at Since 2009 he has developed a few dozen apps for iOS, worked for global brands and lead development at several startups. When he’s not coding, he enjoys strong espresso and traveling.

Leave a Reply

Required to post: Your real name and email address, and a pleasant demeanor. Your email address will not be published. Markdown is supported.

  • I have a problem with typing indicator. I am doing like this but it doesn't appear. How can I fix it? Is there a spesific way using typing indicator? Please help me. Ty

    1. Reinder de Vries March 23, 2017 21:30 in reply to Ali

      @Ali: Okay! First, try if the typing indicator actually works by just using "self.showTypingIndicator = true" without using the timer. Then, make sure that the timer function actually fires by coding "print(#function)" on the first line of the "onTypingIndicatorTimerFire()" function, and then running the code. You should see the function name in the debug window, so then you know it actually fired. If that doesn't work, I'd check whether the typing events come in properly via the socket. Keep in mind that this guide is slightly outdated, so the library implementations (with Starscream, or JSQ) might have changed.

  • Hi Reinder I'm looking to start this project but wanted to know how would this project be different now using iMessage and any updates from Alamofire and Slack? Thanks so much!

    1. Reinder de Vries December 4, 2016 22:02 in reply to Anne

      Anne, good question. I imagine part of the project is outdated, because it was initially written for Swift 2.2. With a little effort you can update the project for Swift 3. Likewise, most of the CocoaPods libraries mechanics are the same (such as websockets) but their methods and classes may have changed. iMessage was never part of the project – if you're looking to build an iMessage app I recommend checking out the iMessage extension kit at

      If you're looking for an easier project to start with, try this one:

  • It's seems all went well for rest of the folks by seeing below discussions , But i am just trying to create the project by following the steps mentioned but after the step :
    "Verify that the project is compiling correctly, including CocoaPods and Bridging Header, by pressing Command-B"

    When I build I am getting multiple syntax errors I am using XCODE 7.3.1, I have attached the screen shot as well.

    1. Reinder de Vries August 26, 2016 10:28 in reply to Pavani

      Try removing the version numbers from the Podfile. I imagine part of it is outdated and not compatible with Swift 2.2.

      pod 'Starscream'
      pod 'Alamofire'
      pod 'SwiftyJSON'
      pod 'JSQMessagesViewController'

  • Xuetao Hong July 20, 2016 05:14

    Hi Great tutorial! But just one question. in the middle of this tutorial, After you find the #General channel ID, how did you add the bot to your #General channel? Because my bot is not in my general channel. Do we need use chanels_join method in RSSlackAPI?

  • Peterdevries October 29, 2015 19:27

    great tutorial, just what I was looking for.

    got a question though and I hope you might have an answer.

    with swift 2, the code has changed for Alamofire.request and I am getting an "unknown" within RSSlackAPI.swift for the beforehand:

    let json = JSON(data!)

    this within :

    func rtm_start(completion: (String) -> Void)

    the Alamofire.request(.GET, Slack.URL.rtm.start, parameters: [Slack.param.token:]).responseJSON {

    response in

    // debugPrint(response) // prints detailed description of all response properties


    // print(response.request) // original URL request

    // print(response.response) // URL response

    // print( // server data

    // print(response.result) // result of response serialisation

    if response.result.error != nil



    var json = JSON(!)



    so I'm not getting anything from the data.
    I was hoping you might have some thoughts on that.

    many thanks,


    1. Peterdevries October 30, 2015 15:10 in reply to Peterdevries

      actually worked it out in the end, has to be:

      let json = JSON(response.result.value!)

      1. Reinder de Vries October 30, 2015 18:19 in reply to Peterdevries

        Glad to hear it worked out, Peter! We still should update this guide to Swift 2.1, thanks for bringing it up.

        Quick tip: it's safer to use optional binding instead of force unwrapping, especially with async / web content. Instead of writing the ! after response.result.value, do:

        if let value = response.result.value,
        let json = JSON(value)

        That way, if result or value are nil accidentally, the code will still execute but not crash. If you use force unwrapping and that variable is nil, the app throws an exception and crashes.

        1. Peterdevries October 30, 2015 20:59 in reply to Reinder

          Thank you for your reply. In the end I did do that:

          switch response.result
          case .Success(let data):
          let json = JSON(data)
          case .Failure(let error):
          print("Request failed with error: (error)")

          somehow I had to invite bots (in slack) onto the channel before I see the messages on the simulator and somehow I do not see any incoming messages. but I guess I'll figure it out in the end.

          Thank you for the writeup, much appreciated.

          1. Reinder de Vries October 31, 2015 09:21 in reply to Peterdevries

            Happy you like it!

            Yes, if you follow the guide to the end it'll show you how to work with bots and creating channels and listening in to the data.

  • Michael Pchelnikov October 10, 2015 10:26

    Awesome, thank you!

    1. Reinder de Vries October 10, 2015 10:31 in reply to Michael

      Good to hear Michael, thank you!

  • Hi!

    Great walkthrough, very extensive!

    I was wondering, what is the advantage of using Slack compared to Layer? Layer seems more customisable and seems to offer more independent in-app functionalities. It also integrates with Parse notification systems.


    1. Reinder de Vries September 1, 2015 10:36 in reply to Filitech

      Good question!

      Layer isn't free, and it's aimed only towards apps. Slack integrates with many more services, extending customer support, and it's free for small teams.

      Layer has a clause in their pricing that will charge a premium when you have more monthly users than your plan allows, which can result in unexpected billing.

      But... one isn't exactly better than another, they're different products. If you're just looking for push notifications, I recommend Urban Airship. And keep in mind that this is a coding guide, not a finished product :-)


      1. Filitech September 1, 2015 11:16 in reply to Reinder

        Awesome, thanks for this elaborative explanation :)