How To: Build A Real-Time Chat App With Firebase And Swift

Written by: Reinder de Vries, June 14 2017, in Tutorials

How To: Build A Real-Time iOS Chat App With Firebase And Swift

Let’s build a chat app! In this guide, you’re going to build a chat app for iOS with Xcode 9, Swift 4 and Firebase. When you’re done, you can chat real-time with multiple users!

Firebase is perfect for a chat app. It’s easy to use, can store a ton of data, and you can observe data changes in real-time! This app can serve as the foundation for your own messaging app, like iMessage, or you can follow along as a learning project.

Ready to get started? Here’s a quick overview of the steps:

Last Updated: December 17, 2017. Updated for Swift 4.0 with Xcode 9.

Get the screencasts, and more

Learn how to build this iOS app step-by-step

Enroll in our iOS courses to get the screencasts for this tutorial, and more: app templates, lifetime updates and helpful support.

Getting Started: Creating The Project In Xcode

Alright! Let’s get this party started…

The first thing you’re going to do is create a new Xcode project. That’s what you’d do for any new app.

So, start Xcode and choose File -> New -> Project.... A dialog opens:

This is where you typically choose an app project template, like a game, an augmented reality app, or an iMessage app.

Choose Single View App. This is a simple template with just one view controller, perfect for your chat app.

On the next screen, input the following items:

  • Product Name: Chat
  • Team: Your name or company name1
  • Organization Name: Your company name, like LearnAppMaking
  • Organization Identifier: A reverse-URL, like com.learnappmaking
  • Bundle Identifier: com.learnappmaking.Chat
  • Language: Swift, of course!
  • Leave the checkboxes for Core Data, Unit Tests and UI Tests unchecked

Your screen now looks similar to this:

Finally, click Next. Finally, in the next dialog, choose a convenient location to save your project. (I’m always saving my projects in a catch-all directory called Xcode Projects)

At last, the main interface of Xcode appears…

Look around – here’s what you see:

  • On the left, there’s a bunch of Navigators. They help with organizing your project, and they give access to important project assets and tools.
  • On the far right, there’s a bunch of Inspectors. They give you fine-grained control of aspects of your project, like UI elements in Interface Builder, Quick Help, and file properties.
  • In the middle you see the main editor, that currently shows Project Settings (above). When you’re coding, this area shows a text editor.
  • At the top left you see Play and Stop buttons, and a Target dropdown. With those buttons you can run and build your app, and you can choose on which device you want to run your app, like a physical iPhone, or an iPhone Simulator.
  • In the middle, at the top, you see a status bar. Important messages appear here.

OK, there’s a few things you need to change in your Xcode project.

  • First, open the ViewController.swift file by clicking on it, in the Project Navigator.
  • Then, at the top of the editor, find this code: class ViewController: UIViewController {.
  • Then, click on ViewController while holding the Command-key. A small popout appears – choose Rename...
  • Then, in the screen that appears, rename ViewController to ChatViewController. Make sure that the checkbox for Main.storyboard is ticked.
  • Finally, click Rename in the top-right to submit the changes.

Renaming files is a new feature, called refactoring, in Xcode 9. If you’re on Xcode 8, do this:

  • Rename ViewController to ChatViewController, simply by editing ViewController.swift
  • Open Main.storyboard, select Chat View Controller Scene in the list on the left, then open the Identity Inspector on the right (3rd tab), and make sure to set Class to ChatViewController

Next, you have to embed the chat view controller in a navigation controller. Do this:

  • First, open Main.storyboard by clicking on the file in the Project Navigator. Interface Builder opens…
  • Then, select Chat View Controller Scene in the list on the left. This will activate the main view controller you see in the editor, with a blue outline.
  • Then, choose from the menu Editor -> Embed In -> Navigation Controller. This will “wrap” the view controller in a navigation controller.

You can rearrange both view controllers in the editor, so that the navigation controller is placed on the left, and the chat view controller on the right.

You see an arrow going from the navigation controller to the chat view controller, indicating their connection. You can also spot a faint arrow on the left of the navigation controller, indicating that this is the first view controller of your app.

That’s all there is to it! You can try out your app by clicking the Play button, or by pressing Command-R. This will start your app in the iPhone Simulator. Your app should show up, and have an empty white UI with a navigation bar at the top.

You can also choose a different Simulator with the dropdown in the top-left corner (like iPhone 7).

Installing And Integrating CocoaPods

OK, before you can start coding there’s just one thing we have to do: installing CocoaPods.

Your project relies on third-party libraries:

The easiest way to add third-party libraries to your project is with CocoaPods. CocoaPods is a package manager, and it helps you to manage your libraries, download new versions, and integrate them with Xcode.

If you haven’t installed CocoaPods before, you can do so by typing this on the command-line, in Terminal:

$ sudo gem install cocoapods

Never type in that $ – it’s used to indicate Command Line Interface (CLI)code. You can read more about working with CocoaPods in the Getting Started guide.

  • Next, when you’re in Xcode, right-click on your Chat project in the Project Navigator and choose New File....
  • Then, in the dialog that appears, scroll down, and choose the Empty template from the Other category.
  • Then, in the save dialog that appears, name the file Podfile and make sure to save the file in the same directory as Chat.xcodeproj. (Important!)

With Podfile open, add the following code in it:

platform :ios, '9.0'

target 'Chat' do

  pod 'Firebase/Core'
  pod 'Firebase/Storage'
  pod 'Firebase/Auth'
  pod 'Firebase/Database'
  pod 'JSQMessagesViewController'


Don’t forget to save the file!

With those lines you tell CocoaPods which pods it should install, for which platform and target. Easy-peasy!

Next, make sure to close your Xcode project by clicking the red (x) in the top-left corner of Xcode, and then close Xcode itself with Command-Q or by choosing Xcode -> Quit from the menu. (Important!)

Then, find your project’s folder in Finder. Use the Finder app to browse to the folder you save your project in, like Xcode Projects.

In case you already know how to work with Terminal, simply use cd to navigate to your project directory.

Then, open Terminal and type cd and then a space:

$ cd 

Then, drag the Chat folder from Finder onto the Terminal window. This should paste the path of your project folder on the command-line. It’s a trick, so you don’t have to type in the entire path for your project folder, yourself.

Your Terminal window should now show something like:

$ cd /Users/username/Xcode\ Projects/Chat

Finally, press Enter.

Then, type in:

$ pod install

CocoaPods will now download all the pods for you, and generate a workspace that includes the chat project, and a new CocoaPods project. This CocoaPods project will build frameworks, and integrate those with your chat project – effectively integrating the third-party libraries!

Next up, with Finder still showing your project folder, make sure to open Chat.xcworkspace. Instead of working with the project, you’ll now work with the workspace. After all, your chat app project is now part of a workspace. (Important!)

Quick Tip: If you’ve opened the workspace and your projects appear “collapsed” or empty, then you forgot to close your project before you quit Xcode. Close all open projects and workspaces, and then open the chat workspace again. Solved!

In the screenshot below you can clearly see the work CocoaPods has done. On the left, in the Project Navigator, you see that there’s a new project: CocoaPods, that has added a bunch of code files and framework files.

If you open the Build Phases tab of your chat app’s Project Settings, you can see that CocoaPods has integrated the new pods with your project. CocoaPods essentially instructs Xcode to compile the third-party source code, and add it to your project as a framework.

You can even inspect the third-party code – check it out!

OK, there’s one thing left to integrate: a Bridging Header. One of the third-party libraries, JSQMessagesViewController, is written in Objective-C. Your project is coded in Swift, so you need to make a “connection” between the Objective-C library code and your own Swift code.

You can do so with a Bridging Header. It’s fairly easy to set up:

  • First, right click on your project in the Project Navigator and choose New File...
  • Then, select Header File from Source category and click Next.
  • Finally, in the save dialog, name the file Bridging-Header.h. Make sure to tick the checkbox at the bottom of the dialog, the one for Targets and Chat. Save the file alongside your Podfile and workspace files by clicking Create.

When the file opens, type the following in it:

#import <JSQMessagesViewController/JSQMessages.h>

Then, do this:

  • First, go to your Project Settings by clicking on your project in the Project Navigator, and go to the Build Settings tab for the Chat Target.
  • Then, in the search box in the top-right, start typing bridging header. The editor filters its rows, and now locate the row that says Objective-C Bridging Header.
  • Finally, double-click on the empty column next to Objective-C Bridging Header – and an input box appears. Type in: Bridging-Header.h and click outside the input box to confirm your change.

Now check if everything still works by pressing Command-B. This will compile your project, and tell you if there are any errors. No errors? Great!

In case you get a compiler error like Error opening input file, you’ve most likely misspelled the bridging header file path, or saved the file in a different location. Check the location of your bridging header, and make sure that the Objective-C Bridging Header reflects this path, relative to your project folder. Chances are that Chat/Bridging-Header.h is the location you used…

Done and done! Let’s continue…

Get the screencasts, and more

Learn how to build this iOS app step-by-step

Enroll in our iOS courses to get the screencasts for this tutorial, and more: app templates, lifetime updates and helpful support.

Configuring Your Firebase Database

You’re now going to configure Firebase.

Firebase is a Platform-as-a-Service, providing cloud services for apps. With it, you can create a cloud database your app can connect to. Firebase also has an armada of other services, essentially covering the entire indie app landscape, with analytics tool, crash reporting, file storage and app monetization products.

Firebase is often compared with Parse Server, but it’s an entirely different tool, especially from a database perspective.

Firebase’s Database platform is flat, a “NoSQL”, and that means you can’t easily query for data, and can’t store relational data. Your database is flat, a tree-like structure, of which you can traverse paths.

Platforms like Parse Server, and the late, are relational. You can create connections between objects in the database, called pointers, and you can execute complex queries with joins and where-clauses, similarly to how SQL works.

Firebase is especially suitable for real-time purposes, and for data that doesn’t need to be relational. Firebase is fast, and it has convenient tools for developers. For a chat app, Firebase is a perfect match.

The structure for our database is simple:

  • chatapp/
    • chats/
      • {$key}/
        • name (string)
        • sender_id (string)
        • text (string)

As you can see, the “root” of the tree-structure is chatapp. Chat messages are saved on the chats node. Every chat message has a unique $key, so you can uniquely identify each chat message. This $key is a special property of a Firebase database – you can compare it with a “unique key” in SQL.

Every chat message has three properties: a name, a sender_id and the chat text. The name is needed to show a chat user the sender of an incoming message, and the sender_id is used internally in the app

Sending a chat message is nothing more than appending message data to the chats node, with a unique $key, and receiving chat messages in the app is nothing more than observing data for the same chats node. A path like chatapp/chats/-KmLvuZfWAmvictV04_u/sender_id is called a reference.

This is what the data looks like in Firebase:

OK, now let’s configure Firebase! First, go to and sign up for a free account. Then, log into your new account and go to your Firebase Dashboard.

Next, click Add Project.

In the dialogs that appear, choose the following settings:

  • Project Name: Chat
  • Country/Region: Choose your country or region

Finally, click Create Project. Then, in the screen that appears, choose Add Firebase to your iOS app.

Another dialog appears. Choose the following settings:

  • iOS Bundle ID: Input your app’s Bundle ID. You choose that when creating the Xcode project. It’s something like: com.learnappmaking.Chat.
  • App Nickname: Anything, something like “Chat App”

Finally, click Register App. In the next screen that appears, click the big Download GoogleService-Info.plist button. You’ll now download a .plist file, so save it in a convenient location (like ~/Downloads).

Do as the screen tells you, so add the .plist file to your Xcode project. Drag-and-drop the file from Finder into Xcode, and add it to the Project Navigator. When a dialog appears, make sure to tick the checkbox for Copy items if needed, and tick the checkbox next to Target.

Finally, click Continue. You can skip step 3, Add Firebase SDK, because you’ve already done that in this guide.

Continue to step 4, Add initialization code:

  • First, switch to Xcode and open AppDelegate.swift
  • Then, at the top of the file, right below import UIKit, type: import Firebase.
  • Finally, locate the function application(_:didFinishLaunchingWithOptions:) and add this line to the method: FirebaseApp.configure().

Try your code with Command-B. Are you getting any errors? Great!

In case you’re get an error that says Use of unresolved identifier “FirebaseApp”, change the line to: FIRApp.configure().

OK, now you’re going to do something else in Xcode. In order to work easier with the Firebase paths, and references, you create a Constants.swift file to hard-code the paths for the chat data.

Do this:

  • First, right-click on the project in Project Navigator and choose New File....
  • Then, pick the Swift File template, name the file Constants.swift, add it to the Chat Target, and save the file in your project folder.
  • Finally, click Create.

Then, add the following Swift code to Constants.swift:

import Firebase

struct Constants
    struct refs
        static let databaseRoot = Database.database().reference()
        static let databaseChats = databaseRoot.child("chats")

The code above is a nested struct, a structure to store variables in. Whenever you now need access to the reference for chat data, you can use:


You’re creating two static constants called databaseRoot and databaseChats. The root uses a function to get a reference to the root of the database, and then databaseChats “extends” that with a child node called chats.

OK, there’s one last thing we need to set straight: permissions. Firebase has an elaborate permissions tool, with which you can use a script-like language to grant and deny access to data, files, and so on.

Since your chat app is going to be a public free-for-all, and because no chat user will need to log in, the permissions for the chat app are simple:

  "rules": {
    ".read": true,
    ".write": true

Important: Never use the permission settings above in a production app! These settings effectively give anyone permission to read and edit any data (for this particular app). You’re using it now for education purposes, so remember to use a more sensible setting in your production Firebase apps. You can find more information here: Understand Firebase Realtime Database Rules.

To configure permissions in Firebase, do this:

  • First, go to your Firebase Dashboard and open your app. This will show you your app’s overview page.
  • Then, in the menu on the left, choose Database.
  • Then, top-left, choose the tab Rules.
  • Finally, replace the text in the editor with the new rules (sample above) and click Publish.

Make sure you input the new rules in the right menu – you can reach a similar-looking screen by choosing Storage -> Rules, for instance, but you need Database -> Rules!

Quickly check if your app still compiles and runs by pressing Command-R in Xcode. App shows up fine in iPhone Simulator? Neat! You can also check Firebase’s status messages in the Debug Window, bottom-middle of Xcode.

OK, that’s it. You’ve done enough configuring! It’s time to code Swift for real…

Setting Up The Messages View Controller

Let’s get to it!

You’re now going to create and configure the JSQMessagesViewController (JSQMVC). JSQMVC is a class that puts a list of message bubbles, much like iMessage, and a text input field on the app screen. It’s like a chat-app-UI-in-a-box.

With a little configuring, you’re going to integrate it in your app, and connect it to Firebase.

You’re starting with subclassing JSQMVC. Do this:

  • First, open ChatViewController.swift and locate the class definition at the top. This is the line that starts with class ....
  • Then, replace UIViewController with JSQMessagesViewController. An error should appear: Use of undeclared type.
  • Finally, add import JSQMessagesViewController below import UIKit. The error should now disappear.

Then, add the following two lines to the function viewDidLoad():

senderId = "1234"
senderDisplayName = "..."

You can add them right below super.viewDidLoad(). Make sure to change ... to your own name!

If you then run your app by clicking the Play button or by pressing Command-R, you should see JSQMVC’s chat UI show up in iPhone Simulator, like this:

It’s working! Let’s continue…

Configuring JSQMessagesViewController

OK, now you have to configure JSQMVC for a bit. This is what you’re going to do:

  • Add a local property to store messages in
  • Provide JSQMVC with the message data
  • Create message bubbles in the right colors
  • Hide avatars for message bubbles
  • Set the name label for message bubbles

Storing messages in messages
First, the local property to store messages. It’s an array, and you need it to display chat messages. In JSQMVC, those messages are shown with “bubbles”, much like iMessage. JSQMVC uses a collection view to display those chat bubbles, and it needs to be provided with the right data.

An array called messages is going to help with that. Add the following line to the top of the class:

var messages = [JSQMessage]()

You add this line after the opening squiggly bracket {, but before the function viewDidLoad(). That part of the code now looks like this:

class ChatViewController: JSQMessagesViewController
    var messages = [JSQMessage]()

    override func viewDidLoad()

That line declares and initializes a property – a variable that’s accessible throughout the entire class scope. It’s type is array of JSQMessage items, so it can store multiple objects of type JSQMessage.

You can access each item by an index number, from 0 to the end of the array. Arrays are a fundamental data structure in programming!

Providing JSQMVC with the message data
OK, next up: JSQMVC needs access to the data from messages in order to show those message bubbles.

Add the following two methods to the class:

override func collectionView(_ collectionView: JSQMessagesCollectionView!, messageDataForItemAt indexPath: IndexPath!) -> JSQMessageData!
    return messages[indexPath.item]

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

You can add them below the function viewDidLoad(), after the closing squiggly bracket } of that method, but before the closing squiggly bracket } of the class…

Quick Tip: See if you can spot all the opening and closing brackets {} in the above screenshot. It’s important to keep these brackets, and their indentation, intact – you’ll thank yourself later if you learn early on to properly indent and structure your code. Brackets are hard to spot when you make a mistake, so get comfortable with them!

Both classes override delegation methods. JSQMVC will call these methods when it needs them, so you provide the right function implementation, so you can customize JSQMVC to get data from the messages array.

The implementation of both methods is simple:

  • collectionView(_:messageDataForItemAt:) returns an item from messages based on the index from indexPath.item, effectively returning the message data for a particular message by its index.
  • collectionView(_:numberOfItemsInSection:) returns the total number of messages, based on messages.count – the amount of items in the array!

Create message bubbles in the right colors
Next up, the message bubbles and their colors. There are two colors for the messages, just like in the iMessage app:

  • Blue for outgoing messages, i.e. messages that the user sends
  • Gray for incoming messages, i.e. messages that the user receives

You can provide JSQMVC with a “bubble image data” object via another delegation method. You can create the image data from a factory method, which essentially creates the object for you based on a color you put in – quite literally, like a bubble factory!

First, add the following properties to the top of the class. Make sure to add them right below the message property, but before the viewDidLoad() method.

lazy var outgoingBubble: JSQMessagesBubbleImage = {
    return JSQMessagesBubbleImageFactory()!.outgoingMessagesBubbleImage(with: UIColor.jsq_messageBubbleBlue())

lazy var incomingBubble: JSQMessagesBubbleImage = {
    return JSQMessagesBubbleImageFactory()!.incomingMessagesBubbleImage(with: UIColor.jsq_messageBubbleLightGray())

Oooh, big chunk of complex code!

  • You’ve just created two properties called outgoingBubble and incomingBubble, both of type JSQMessagesBubbleImage.
  • Both properties are computed properties. This means that they don’t have a fixed value, like 42, but a computed value: the result of a function.
  • Both properties are lazy, which means that they’re only initialized once – when they’re accessed. When you access a lazy property for the first time, its value is initialized (“created”). For every subsequent access, the value of the property isn’t recreated (as for typical computed properties), but simply returns the initial value.
  • In Swift, typical computed properties can’t be lazy! You’ve coded around that by creating a closure. The closure’s result is the value for the lazy property, effectively creating a lazy computed property.

What’s the benefit? The bubble factory only creates bubbles once – saving resources.

Next, let’s put those properties to use. Add the following function to the class, below the other delegate methods:

override func collectionView(_ collectionView: JSQMessagesCollectionView!, messageBubbleImageDataForItemAt indexPath: IndexPath!) -> JSQMessageBubbleImageDataSource!
    return messages[indexPath.item].senderId == senderId ? outgoingBubble : incomingBubble

This function is yet another delegate, that’s called by JSQMVC when it needs bubble image data. Inside the function the ternary conditional operator a ? b : c is used to return the right bubble:

  • When messages[indexPath.item].senderId == senderId is true, return outgoingBubble
  • When that expression is false, return incomingBubble

Neat, right?

Hide avatars for message bubbles
OK, now let’s hide the avatars for message bubbles. JSQMVC can show them, but you don’t need that now.

First, add this function to the class, right below the other methods:

override func collectionView(_ collectionView: JSQMessagesCollectionView!, avatarImageDataForItemAt indexPath: IndexPath!) -> JSQMessageAvatarImageDataSource!
    return nil

This function simply returns nil when JSQMVC wants avatar image data, effectively hiding the avatars.

Then, add the following code to viewDidLoad(), right below where you set the senderDisplayName and senderId:

inputToolbar.contentView.leftBarButtonItem = nil
collectionView.collectionViewLayout.incomingAvatarViewSize =
collectionView.collectionViewLayout.outgoingAvatarViewSize =

The first line hides the attachment button on the left of the chat text input field. The other two lines of code set the avatar size to zero, again, hiding it. Check your progress with this screenshot:

Set the name label for message bubbles
Lastly, let’s make sure the name label for the message bubbles is configured. This label shows up on top of the message bubble, and you can use it to display the name of the user that sent the chat message.

Add the following two methods to the class, right below the other methods:

override func collectionView(_ collectionView: JSQMessagesCollectionView!, attributedTextForMessageBubbleTopLabelAt indexPath: IndexPath!) -> NSAttributedString!
    return messages[indexPath.item].senderId == senderId ? nil : NSAttributedString(string: messages[indexPath.item].senderDisplayName)

override func collectionView(_ collectionView: JSQMessagesCollectionView!, layout collectionViewLayout: JSQMessagesCollectionViewFlowLayout!, heightForMessageBubbleTopLabelAt indexPath: IndexPath!) -> CGFloat
    return messages[indexPath.item].senderId == senderId ? 0 : 15

Again, these two delegate methods are called when JSQMVC needs information:

  • collectionView(_:attributedTextForMessageBubbleTopLabelAt:) is called when the label text is needed
  • collectionView(_:collectionViewLayout:heightForMessageBubbleTopLabelAt:) is called when the height of the top label is needed

Inside the methods, you see a similar a ? b : c code, like you used before, to determine if the message is sent by the current user, or sent by someone else. Logically, if the current user sent the message, you don’t have to show their sender name, so the label stays empty and hidden.

In the code you can also see messages[indexPath.item].senderDisplayName.

This code accesses a particular message by index, and then uses the property senderDisplayName on the resulting object. The type of this object is JSQMessage, as defined in property messages, so that’s why you can use the property on that message object.

Pfew! That’s quite a bit of code… Ready to finally write some interactive, message-sending code?

Quick Tip: Every time you make significant changes to your project’s code, either build (Cmd-B) or run (Cmd-R) your app to make sure you haven’t made any mistakes. If errors show up, fix them before you move on.

Get the screencasts, and more

Learn how to build this iOS app step-by-step

Enroll in our iOS courses to get the screencasts for this tutorial, and more: app templates, lifetime updates and helpful support.

Sending A Chat Message With didPressSend()

At this point all you’ve done is configuring and preparing, but now you’re ready to actually send chat messages. And that’s surprisingly simple!

When the user has typed a chat message, and then presses the Send button, in the app, you will respond to that action by sending data to Firebase.

Add the following function to the ChatViewController class, right below the other methods:

override func didPressSend(_ button: UIButton!, withMessageText text: String!, senderId: String!, senderDisplayName: String!, date: Date!)
    let ref = Constants.refs.databaseChats.childByAutoId()

    let message = ["sender_id": senderId, "name": senderDisplayName, "text": text]



This function overrides a function on JSQMessagesViewController, the class you subclassed. This enables you to override what happens when a user presses the Send button, effectively creating your own implementation.

So… what happens?

  1. You create a reference to a new value, in Firebase, on the /chats node, using childByAutoId()
  2. You create a dictionary called message that contains all the information about the to-be-sent message: sender ID, display name, and chat text
  3. You set the reference to the value – you store the dictionary in the newly created node
  4. Finally, you call finishSendingMessage(), a function that tells JSQMVC you’re done

The data for message is provided via the function parameters, so JSQMVC tells you what the user typed in the chat field.

That childByAutoId() function is interesting. It’s part of Firebase, and it can be used to generate a unique key.

In the code above, you’re creating a new chat message. You designed previously to store all the messages on the path /chats/. The function childByAutoId() will generate a new unique key on that path, so you can attach data to it.

An example path is /chats/-KmLvuZfWAmvictV04_u.

You can see it as a tree. The tree starts at the root, and then branches out. One branch is /chats, that sub-branches into a new branch – the one generated with childByAutoId().

You need those randomly generated keys, otherwise you’d end up with a conflict: what if 3 users all named their branch “message42”?

Then, at point 3, you assign a value to that path, like “branch = value”. It had no value before (“null”) and with setValue(_:) you’ve given it a value.

But… here’s the kicker – the values are branches too! That’s the beauty of Firebase: it’s all one big tree structure. Like this:

For instance, the value of path chatapp/chats/-KmLtIPCrC-wYSwGg8nX/text is Do you take the red or the blue pill, Neo?. Awesome!

Why don’t you try it out for yourself?

  • Run your chat app with Command-R
  • Type in a chat message and press Send
  • Open Firebase, and go to Database -> Data
  • Your message data is shown there!

But… you aren’t seeing any chat bubbles yet. Let’s change that!

Observing Firebase For New Chat Messages

Your chat app is close to coming together! Let’s add the most critical piece of code: observing Firebase data.

One of the grea things Firebase can do is push real-time data to your app, with very little effort or code. It’s literally real-time: you change your data in Firebase, and your UI or data on your iPhone changes. Perfect for a chat app!

First, add the following code to the end of viewDidLoad(). Make sure to add it within the methods closing bracket }, right below the collectionView ... lines.

let query = Constants.refs.databaseChats.queryLimited(toLast: 10)

_ = query.observe(.childAdded, with: { [weak self] snapshot in

    if  let data        = snapshot.value as? [String: String],
        let id          = data["sender_id"],
        let name        = data["name"],
        let text        = data["text"],
        if let message = JSQMessage(senderId: id, displayName: name, text: text)


The code looks badass, but it’s actually quite simple. Here’s what happens:

  1. Create a query to get the last 10 chat messages
  2. Observe that query for newly added chat data, and call a closure when there’s new data
  3. Inside the closure the data is “unpacked”, a new JSQMessage object is created, and added to the end of the messages array
  4. The function finishReceivingMessage() is called, which prompts JSQMVC to refresh the UI and show the new message

Let’s take a closer look at the lines of codes. First, this one:

let query = Constants.refs.databaseChats.queryLimited(toLast: 10)

This declares and initializes a constant called query. It’s assigned the result of queryLimited(toLast: 10). This function is called on databaseChats, the reference you created earlier in Constants.swift. This line of code effectively prepares a query: an instruction for the database which data to retrieve.

Then, in this part of the code, the actual retrieving happens:

_ = query.observe(.childAdded, with: { [weak self] snapshot in

The function observe(_:with:) is called on the query. The Firebase framework now starts “observing” the query for changes. Whenever a new object in Firebase is created, the closure is called.

A closure is like a function, but then different. You can use closures to pass around blocks of code, as if they’re variables, but you can also invoke those blocks of code, as if they’re functions.

You’ve probably guessed by now that there’s some interaction between didPressSend() and observe(_:with:). When a new chat message is typed and sent, it’s returned via the observer function, and shown on screen. (That’s why you didn’t see chat messages before!)

Then, next is that set of if let code:

if  let data        = snapshot.value as? [String: String],
    let id          = data["sender_id"],
    let name        = data["name"],
    let text        = data["text"],

The if let statement uses optional binding to unwrap and cast snapshot to a dictionary of strings. Subsequent lines then assign values of that dictionary to constants.

The last line of code checks if text isn’t empty, and if it is, the block of code below it (between the { }) doesn’t execute. This makes sure that no empty text bubbles show in the UI.

Finally, this block of code:

if let message = JSQMessage(senderId: id, displayName: name, text: text)


With it, a new JSQMessage object is created. It’s provided with the id, name and text from the snapshot – the data that’s returned from Firebase.

The if let conditional binding statement is needed because JSQMessage can return an optional, so it needs to be unwrapped.

Finally, the newly incoming message is appended to the messages array. When finishReceivingMessage() is called, JSQMVC updates the UI and shows the new message bubbles.

Try it! Is it working?

Setting A User’s Display Name With A Dialog

At this point the app is almost done, but one thing is missing. It’s not ready for multiple users!

Because senderId and senderDisplayName are hard-coded to two default values, all installs of this app would show up as the same user…

Let’s change that!

Add the following function below viewDidLoad(), so right after the closing squiggly bracket }, but before the function collectionView(_:messageDataForItemAt:).

@objc func showDisplayNameDialog()
    let defaults = UserDefaults.standard

    let alert = UIAlertController(title: "Your Display Name", message: "Before you can chat, please choose a display name. Others will see this name when you send chat messages. You can change your display name again by tapping the navigation bar.", preferredStyle: .alert)

    alert.addTextField { textField in

        if let name = defaults.string(forKey: "jsq_name")
            textField.text = name
            let names = ["Ford", "Arthur", "Zaphod", "Trillian", "Slartibartfast", "Humma Kavula", "Deep Thought"]
            textField.text = names[Int(arc4random_uniform(UInt32(names.count)))]

    alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { [weak self, weak alert] _ in

        if let textField = alert?.textFields?[0], !textField.text!.isEmpty {

            self?.senderDisplayName = textField.text

            self?.title = "Chat: \(self!.senderDisplayName!)"

            defaults.set(textField.text, forKey: "jsq_name")

    present(alert, animated: true, completion: nil)

Big function! What does it do?

  • First, create an alert controller. With it, you can display an alert dialog box on screen. Provide it with a title and a message.
  • Then, add a text field to the alert dialog. The text field is either provided with a value from UserDefaults, or with a random item from the array names.
  • Then, add an action to the alert dialog. When the user taps “OK”, the closure is executed. In the closure, the sender display name is changed, as well as the view controller title, and the new name is stored in UserDefaults.
  • Finally, the alert dialog is presented on screen.

But… what’s it really do? Well, the dialog asks you for a new sender display name. With it, you can determine your chat username! The alert dialog saves your chosen username to UserDefaults, a special kind of storage for app settings.

Quick Note: If you’re on Swift 3 / Xcode 8, remove the @objc in the function definition.

Next, replace this code in viewDidLoad()

senderId = "1234"
senderDisplayName = "[yourname]"

With this:

let defaults = UserDefaults.standard

if  let id = defaults.string(forKey: "jsq_id"),
    let name = defaults.string(forKey: "jsq_name")
    senderId = id
    senderDisplayName = name
    senderId = String(arc4random_uniform(999999))
    senderDisplayName = ""

    defaults.set(senderId, forKey: "jsq_id")


title = "Chat: \(senderDisplayName!)"

let tapGesture = UITapGestureRecognizer(target: self, action: #selector(showDisplayNameDialog))
tapGesture.numberOfTapsRequired = 1


The code does this:

  • First, create a temporary constant for the standard UserDefaults
  • Then, check if the keys jsq_id and jsq_name exist in the user defaults.
    • If they exist:
      • Assign the found id and name to senderId and senderDisplayName
    • If they don’t exist:
      • Assign a random numeric string to senderId and assign an empty string to senderDisplayName
      • Save the new senderId in the user defaults, for key jsq_id and save the user defaults (with synchronize())
      • Show the display name alert dialog (the one you coded earlier)
  • Then, change the view controller title to "Chat: [display name]"
  • Finally, create a gesture recognizer that calls the function showDisplayNameDialog when the user taps the navigation bar.

The code above effectively checks whether a display name and sender ID were previously set. If they weren’t, it generates a random sender ID, and gives the user the opportunity to set a display name.

The user can also change their display name at any point by tapping the navigation bar.

So, one of two scenarios can happen:

  • When a user starts the chat app for the first time, they get a random sender ID, and they’re asked to input a sender display name
  • When a user starts the app for a subsequent time, the app uses their previously set sender ID and display name. The user can optionally change their display name at a later point by tapping the navigation bar.


Get the screencasts, and more

Learn how to build this iOS app step-by-step

Enroll in our iOS courses to get the screencasts for this tutorial, and more: app templates, lifetime updates and helpful support.


And… that’s all there is to it!

Fire up iPhone Simulator, run your app, and start an interactive chat session.

You can open multiple simulators in Xcode 9 by choosing Hardware -> Device -> iOS in iPhone Simulator, and picking a different device than you’re currently running. Next, run the app in Xcode for that device, and restart the app on your current device. Result? Two apps on two iPhones!

Check out the chat app, right here:

Enjoying this guide? Share it on social media!

Build A Real-Time iOS Chat App With Firebase And SwiftClick To Tweet

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.

Comments & Questions

Got a comment or question? Let me know! I read and respond to every one. Thanks!

  • prakash jangid

    its very usefully for me .Thanx a lot dear

  • Did you check the link I sent you? It’s all explained in there. Try to get the code to receive notifications in the app working first, then figure out a way to send them, and then figure out how to send them when a chat message gets sent. You’ll have to connect the dots – I won’t give you a ready-made solution.

  • Jishnu Raj

    how i could code it? in my sever or any other way on firebase ,can we code on firebase>?

  • Yes, you’ll have to code it, for it to happen automatically. You can look into triggering a push notification when the data in your database changes, or send push notifications directly between chat clients.

  • Did you code the “showDisplayNameDialog()” function? This error most likely indicates that there’s no such function, so when you call it, an error occurs.

  • Jishnu Raj

    how push notification automatically sent works? do i need to send a push notification manually from firebase console that is not possible in case of chat app

  • Taylor Dallas Martin

    I am getting an error at the end of the project. Everything worked up until I changed the:

    senderId = “1234”
    senderDisplayName = “[yourname]”


    let defaults = UserDefaults.standard

    if let id = defaults.string(forKey: “jsq_id”),
    let name = defaults.string(forKey: “jsq_name”)
    senderId = id
    senderDisplayName = name
    senderId = String(arc4random_uniform(999999))
    senderDisplayName = “”

    defaults.set(senderId, forKey: "jsq_id")



    title = “Chat: (senderDisplayName!)”

    let tapGesture = UITapGestureRecognizer(target: self, action: #selector(showDisplayNameDialog))
    tapGesture.numberOfTapsRequired = 1


    The error I get is Use of local Variable ‘showDisplayNameDialog’ before its declaration

  • You’ll need to implement Push Notifications. Since you’re already using Firebase, I recommend Firebase Cloud Messaging. Read more here:

  • Jishnu Raj

    Hey Reinder,
    can you help me on a problem with JSQmessage , how can i enable notification like (number badge on icon and notification on shutter) when i get new message from any user ?

  • Any and all app templates for my tutorials are included in my paid courses. More info here:

  • Bara Ndiaye


    Can you please post the link to the finished project please? cant find it on here. thanks alot

  • Don’t worry, it happens to the best of us ;-)

  • Derrick Johnson

    facepalm emoji that was the problem

  • It could be nothing, but did you accidentally type “displayName” with a capital “D”?

  • Derrick Johnson

    I’m receiving an error “Argument labels ‘(senderId:,DisplayName:,text:)’ do not match any available overloads”
    This is in regards to the code “if let message = JSQMessage(senderId: id, DisplayName: name, text: text)
    What are some possible reasons for this type of error? I’ve looked into the JSQMessage definition and searched stack overflow and still haven’t been able to come up with a solution.

  • Thanks! 1) Not sure what you mean here exactly. That query will get all messages, whether you’ve seen them or not. 2) Yes, but you’d have to change the database structure. See some of the other comments on this page, I’ve explained there.

  • Faraz

    Hi Reinder, Nice tutorial. 1) I wanted to know that if I am having a group chat in which I haven’t messaged yet. Can I get the exiting msgs of other users by that query varible you defined in viewDidLoad method and 2) Can I make separate chat threads like If I want to get specific chat conversation? As I am getting from the code it is fetching latest 10 messages considering all messages a single conversation.


  • Thanks! Glad you like it. The message view cell has a property “cellTopLabel” that you can use. If that conflicts with the sender name, I recommend you use user avatars to identify senders. More info in the docs:

  • Nenad Nikolic

    Hi, first of all GREAT chat app!

    I have one question. Can I show message time below message? Above message is the name of the sender, that is great, but I also need to show time when the message is sent/receved. Is this posible and availible?
    Thanks in advance!

  • Awesome! Thanks – glad you liked it. If you’re ready for more, consider becoming a member:

  • Oh, that sucks! Make sure you’ve added JSQ correctly to your workspace with CocoaPods. Clean your project with Cmd-Alt-Shift-C. Restart Xcode. Build your project with Cmd-B. See if the import now works.

    SIGABRT is a generic crash, so it’s hard to know what’s caused it. See if you can find an error message in the Debug Area. I think, if you add JSQMVC on screen, but don’t set certain properties and delegate functions, it’ll crash because of that missing stuff. This article can help too:

    Oh, and if you want to learn more about iOS development, do consider becoming a member:

  • Victor Melo

    Hey Reinder, excellent tutorial. It helped me to have a first contact with firebase, and also, I’ve never developed a chat app before, it was very interesting. Thanks a lot :)

  • Edison Espinosa

    No such module ‘JSQMessagesViewController’ is what I’m getting when I import it.

    I also get this in the App Delegate.. Thread 1: signal SIGABRT despite not having anything linked… ughh any help wouuld be huge!

  • Great! Not sure – check out the docs:

  • Not sure – check out the docs here:

  • Nidhi Dhorajiya

    how can i display attachment button(like paper pin)in thi stutorial

  • boqian cheng

    Thank you very much, and I use collectionView.contentInsetAdjustmentBehavior = .never, and solved the problem and it is ok.

    One more problem: how do I add a audio recorder button?

  • This might be an issue with iOS 11, see: Otherwise I’d check the JSQMessagesViewController GitHub repository and issues.

  • Sounds like a great app project! If you want to learn how to create your own iOS app projects, and get helpful support building them, I recommend you check out our LearnAppMaking courses & community here:

  • Remi Dubian

    Hello !

    I would to creat a sample chat app with an unique message which people can send, for send a notification saying ” your friend ***** boost you to study “.

    What I should to change in the code to transform the chat to an unique message sendable with one button ?

    Thank u,

  • You have to use the media attachments. Check out the official docs + “Getting Started” guide ->

  • Arvind

    Hi All,
    I have implemented JSQ chat in my app but how to send images in chat using JSQ Message?

  • Madhuri

    Finally Done it thank you

  • Check the JSQMessagesViewController “Views” documentation and code, I’m sure there’s a way you can get to the button. Then set its background as you normally would.

  • Madhuri

    How to change the send button bacground?

  • Are you adding the new message to the messages array and reloading the chat view? Other than that, I’m afraid I can’t help you – what you’re asking is beyond the scope of this free tutorial. If you’re looking for 1-on-1 help, check out our community right here:

  • Prince Verma

    Hi Reinder,
    I just want to create a static Chat app for POC purpuse. I didn’t include Firebase and other server, i just want to see the sent messages in the blue bubble and the static array which includes certain message should be shown in Gray.
    I have impleted all delegates methods. But when i enter some text in Textfield and enter send button, there is nothing to show in blue bubble.
    Kindly help!

  • That’s great to hear! Keep up the good work ;-)

  • Vinoth Vino

    Awesome man! Finally I created one chat app with your tutorial. Keep up the work!

  • Chhavi krishan kaushik

    Thank you so much sir for your suggestion . i will keep in mind forever

  • I’m happy you got it sorted out! With Firebase, you can’t make complex SQL-like “WHERE” queries, because you’re working with a flat no-SQL database. If your app requires this kind of functionality, it’s easier to work with something like Parse Server.

    However, you can solve these where-queries by structuring your data differently. This is called “denormalization”. You effectively store the messages on a branch that has the ID of the chat room number, instead of storing a property chat room ID on a message.

    Don’t stay lazy! If you stay lazy, you’ll always have to rely on people like me to help you out and do the work for you. We may help you today, but maybe we won’t tomorrow. It’s more sustainable if you learn how to figure stuff out yourself. So learn to Google! Learn to read the Firebase documentation! Learn how to give your mind direction. If anything, being able to learn is a permanent skill, one that sets you apart from all the other people that only know how to follow instructions.

    Good luck!

  • Chhavi krishan kaushik

    i did same as you suggest and i am able to make multiple chat rooms

  • Chhavi krishan kaushik

    finally my app is working thanks for writing your great tutorial .with out your tutorial and without your help i was not able to complete my first chat app

  • Chhavi krishan kaushik

    last thing i am unable to configure that how to query from firebase DB .
    let myRef = Constants.refs.databaseChats.queryLimited(toLast: 50)
    _ = myRef.queryOrdered(byChild: “roomNo”).queryEqual(toValue: defaults.string(forKey: “roomNo”)).observe(.childAdded, with: { [weak self] snapshot

    i want to query with room_no and Room_id how can i achieve this ??. i am very lazy person so i dont do google a lot but when you give me direction my mind work .

  • Awesome!

  • Chhavi krishan kaushik

    you are my hero i found my problem with senderId. thanks again and i fixed it .

  • Yes, check the “incomingBubble” and “outgoingBubble” properties, make sure they use the right factory functions. Also, check your implementation of the “messageBubbleImageDataForItemAt” function. If that’s not it, there’s something wrong with the filtering based on senderId.

  • Chhavi krishan kaushik

    Can you please suggest me why my incoming and outgoing both messages appearing right side only. ???

  • You rock, sir! Glad you solved it :-)

  • Chhavi krishan kaushik

    WOW great catch . its working fine now thank you so much

  • Do you mean when sending the data to Firebase? You can just add extra items to the array that’s sent to Firebase. You’d have to figure out how to get the right data in “didPressSend” though, because your extra variable isn’t provided as a function parameter.

  • Chhavi krishan kaushik

    hello every one can i send one more parameter to message lets say sender_id , text , displayName , MyKey so message will accept 4 parameters how can i achieve this . every thing is working fine for me i follow your tutorial please help if it is possible

  • Is that the only error? There’s usually more text above it, with more hints. My guess is that the filename of the bridging header and what you set in the Build Settings don’t match. Also, make sure to save the Bridging-Header.h in the right folder.

  • Ayan Sheikh

    After adding bridging header.h file into build settings Xcode shows error when I build the project.. error is. Command /Applications/ failed with exit code 1

  • Ghiggz Pikkoro

    Ok thank you for your answer, I will check it now. It will be a great idea to consider it yes.

  • Yes, it’s for group chat. I’ll consider making an addition for 1-on-1 chat! For now, please check this comment for some ideas on how to implement it:

  • Ghiggz Pikkoro

    Thank you for this tutorial, I want to try it but I wonder if this tutorial is only for group chat with several user in the same conversation? Or it’s for one to one chat? I want to do a one to one chat? If it’s not one to one, do you have another tutorial for make a one to one chat ? In Swift 4 or less. Thanks for your answer

  • Yup, check out my previous comment:

    One-to-many is effectively chatting in groups. So you could create a chat group, give people the option to enlist in the group, and then store chat messages on that group branch.

  • ferdinan dharma putra

    hallo, I already implement this guide,
    Question 1:
    how to implement chat 1 – 1 ?
    ex: I login as User A, and I want to chat with user B?
    is that possible?

    Question 2:
    how to implement chat 1 to many ?
    ex: I login as User A, and I want to chat with user B, user C and user D but without user E, user F, etc?
    is that possible?

    Hope all the best for You

  • Mayank Misra

    Will definitely try that out, thanks a lot for your prompt reply. Much appreciated!

  • You can check your pod version of Firebase with:

    With a 1-on-1 chat you’d have to change the database structure, because now it’s indeed all in one chat room. You could for instance make a contacts list and then store the chats as child data for that contact. You can also denormalize the chats, and make multiple chat rooms, and let people subscribe to those chat rooms. You can also make chat rooms with a key like “$username1_$username2” (ordered alphabetically) so that two users always end up in the same chat room. You’d have to figure out how to query which chats belong to which user, so you probably have to save the name of the chat room as a child for the user data.

    Hope that helps!

  • Mayank Misra

    That’s strange because I’m using Xcode Version 9.0 (9A235) too (and using firebase from cocoapods), and “Database” shows error while with “FIRDatabase” works perfect, I’m can run the chat on simulators too, nevertheless, thank you so much for such a beautiful detail oriented tutorial, thanks a lot.
    Just had a small query, how can i tweak this to make a One-to-One text based chat? and use it in production app ? right now this tutorial is more like a Group Chat? Please guide if possible?

  • This depends on your version of Swift, and your version of the Firebase SDK. In Swift 4, Xcode 9, the correct usage is “Database” instead of “FIRDatabase”.

  • Mayank Misra

    Change “FIRDatabase” Instead of “Database” in Constants.swift

    struct Constants
    struct refs
    static let databaseRoot = FIRDatabase.database().reference()
    static let databaseChats = databaseRoot.child(“chats”)

  • Oh no! Don’t worry, that happens to the best of us… ;-)

    Here’s what you need to do. Use the guide below to set a breakpoint within the didPressSend() function, then run your app, and see if the breakpoint is triggered. If it is, then you know that the function is called OK, but what’s within the function probably doesn’t work. If the breakpoint isn’t triggered, there’s something wrong with subclassing JSQMVC.

    Good luck!

  • Chris Greening

    Hello, Last night when I stopped and saved I had the Chat app working. Today when I open my App everything is working but the send button won’t work. When I tap it it is not even calling the didPressSend Function. Can someone try to assist me with this? I can post code if you need me too!. Thank you so much for any help offered

  • Make sure to check out the documentation of JSQMVC to find out how to work with media attachments. See:

  • SD

    can you please implement code for location and audio messages ?

  • OK, I see. Summarizing, you want to show the alert view controller, to change the name, when you press “Menu” in the chat view controller? Or do you want the “Menu” button to go back to the first view controller, i.e. to go away from the chat?

    If you want to show the alert to change the name, you’ll need to add an “action” to the navigation bar button. You can do this in code and Interface Builder, with an IBAction, and connect that to the bar button. Within that action (a function) you then call the “present” method on the alert. The code for that is in the tutorial.
    If you want the button to go back to the previous view controller, simply remove the bar button item from Interface Builder and the back button will reappear. If you want to keep the bar button item, then also create an action for it, and in that action you call the popViewController(animated:) method.

    Sounds like you also need to learn a bit more of basic iOS development! Have you done any courses? I’m working on a new basic course – do you want me to ping you when it’s ready?

  • Alex

    in you can see the structure of the chatviewcontroller. in see the full app, a view controller with a button that leads to chatviewcontroller and see the button that if pushed I should bring it to the viewcontroller but it does not work

  • Not sure I understand – could you upload screenshots 1) showing what you see in your app and 2) how it should be different?

  • Alex

    The problem is when I put a bar button item in the chat navigation bar. When I go to the button I opens the alert view to change the name. And I do not back
    Sry for this inconvenient

  • Great! So put the view controller in a navigation controller (UINavigationController class), push the chat view controller onto the navigation stack with pushViewController(_:animated:). This will show a transition, and add a back button to the navigation bar to go back from the chat view controller.

  • Alex

    Im using a simple viewcontroller with button in view

  • Are you using a UINavigationController? If not, use it, and then call navigationController.popViewController(animated:true) to go back.

  • Alex

    I try to explain it better. I have a view controller with a button. The button opens the chat. In chat I should put a button to go back. I tried many things but they did not work!
    Thx a lot

  • Thanks! Not sure I understand – go to which “home”? It’s a one view / non-navigational app, so there’s nowhere to go…

  • Alex

    how do I put a button to go back to the home?

  • Not sure if I understand correctly…

    You can get multiple Simulators to work in Xcode 9. Simply start it on one device model, then start it on another. It’s explained in one of the last paragraphs.
    If you only see blue bubbles, you might have made a mistake somewhere. Check that you’re setting the color correctly, with the computed property, and that you’ve got the conditional right that checks “is this a message from the current user”.

    Good luck!

  • Alex

    I have a problem, I have followed all the points but I do not work. In the video you see with two iphone how the chat is done, but I only see blue bubbles

  • Sure! It’s right there on the page…

  • georg odermatt

    hey can you give a link for the finished XCode project, would help me alot :)

  • Try this:

    You can open multiple simulators in Xcode 9 by choosing Hardware → Device → iOS in iPhone Simulator, and picking a different device than you’re currently running. Next, run the app in Xcode for that device, and restart the app on your current device. Result? Two apps on two iPhones!

  • Rajesh sharma

    In this Video , There is a Two Simulator How To create , 2 simulator. Please Help

  • That’s a generic error. Check out this blog post, especially the “Exception Breakpoints” section, it should help you find the bug:

  • mo

    i followed everything down to the T and i keep getting this error, im new to coding any chance anyone knows what im doing wrong?

    Thread 1: signal SIGABRT
    libc++abi.dylib: terminating with uncaught exception of type NSException

  • chauhan vipul

    thank you for answer
    i did

  • Sure! Check out the docs for JSQMessagesViewController, there are certain cell properties you can use to show a typing indicator, and a “read” indicator. You can use Firebase to keep track of which user has seen the message, for instance as an array/dictionary on the message object.

  • chauhan vipul

    u know how to impliment functionality if sender send the message and how to many people read the message when we tap on message detail button all seen user display

  • john s

    This is awesome thanks! I actually bought a course and couldn’t do it. This one is free and actually works!