Replace observe: Ryan Ackermann up to date this tutorial to iOS 12, Swift four.2, Xcode 10, MessageKit, and Cloud Firestore. Tom Elliott wrote the unique tutorial. You’ll be able to safely ignore the warnings about updating to Swift four.2 since MessageKit just isn’t but up to date.
It looks like each main app on the market has a chat function — and yours must be no totally different! This Firebase tutorial will present you ways.
Nevertheless, making a chat software can appear to be a frightening process. There’s no native UIKit controls designed particularly for chat, and also you’ll want a server to coordinate the messages and conversations between customers.
Thankfully, there are some nice frameworks on the market that will help you: Firebase enables you to synchronize actual time knowledge with out writing a line of server code, whereas MessageKit provides you a messaging UI that’s on par with the native Messages app.
On this Firebase tutorial, you’ll construct RWRC (Ray Wenderlich Relay Chat) — an nameless chat software. For those who’ve used IRC or Slack, this kind of software ought to already be acquainted to you.
Alongside the best way, you’ll discover ways to do the next:
- Arrange the Firebase SDK and MessageKit with CocoaPods.
- Synchronize knowledge in actual time with the Cloud Firestore.
- Authenticate anonymously with Firebase.
- Leverage MessageKit for an entire chat UI.
- Create a number of message threads.
- Use Firebase Storage to ship footage.
- 1 Getting Began
- 2 Enabling Nameless Authentication
- 3 Logging In
- 4 Firebase Knowledge Construction
- 5 Chat Interface Setup
- 6 Setting Up the Show and Format Delegates
- 7 Creating Messages
- 8 Sending Messages
- 9 Synchronizing the Knowledge Supply
- 10 Sending Pictures
- 11 The place to Go From Right here?
Use the Obtain Supplies button on the prime or backside of this tutorial to obtain the starter venture. To get you began the challenge incorporates a easy dummy login display, the place the credentials are saved to Consumer Defaults.
The starter undertaking has a couple of helper courses that deal with sending knowledge to Firebase and saving knowledge to Consumer Defaults. Be happy to browse the starter undertaking a bit to get acquainted with the code.
Within the starter challenge you’ll discover ChannelsViewController.swift which listens to modifications in a Firebase Firestore database and updates a desk view every time the consumer provides a brand new channel. You’ll construct an analogous implementation for displaying chat messages as an alternative of channels.
You’ll use CocoaPods to put in each the Firebase SDK and MessageKit. Should you’re new to CocoaPods, take a look at our Cocoapods with Swift tutorial to rise up and operating.
Open Terminal on the venture’s folder location and run the next command to put in your dependencies:
pod set up
This will likely take a couple of minutes, however as soon as the packages have put in, open RWRC.xcworkspace in Xcode. Earlier than you possibly can run the app, you’ll have to configure Firebase.
In the event you’re new to Firebase you’ll have to create an account. Don’t fear — that is straightforward and completely free.
Create a Firebase Account
Head to the Firebase signup website, create an account, after which create a brand new Firebase challenge.
In Xcode, click on on the goal and alter the Bundle Identifier to any worth you want, and choose a Group within the Signing part.
Comply with Steps 1 and a couple of of the directions so as to add Firebase to an iOS app, beginning right here:
Subsequent, enter within the app’s bundle ID into the shape, after which you’ll obtain and add the GoogleService-Information.plist config file to your challenge beneath the Supporting Information group as proven within the Firebase directions. This .plist file accommodates the configuration info wanted for Firebase integration together with your app.
Warning: Do solely Steps 1 and a couple of of the directions. The remaining is already finished within the starter venture and your app will crash in the event you duplicate the steps.
Now construct and run the app. You need to see the next:
Enabling Nameless Authentication
Firebase lets customers log in via e-mail or social accounts, however it could additionally authenticate customers anonymously, supplying you with a singular identifier for a consumer with out figuring out any details about them.
To arrange nameless authentication, open the Firebase App Dashboard, choose the Authentication choice on the left, click on Set Up Signal-In Technique, then choose the Nameless choice, change Allow in order that it’s on, then click on Save.
Identical to that, you’ve enabled tremendous secret stealth mode! Okay, so it’s actually simply nameless authentication, however hey — it’s nonetheless cool. :]
Open LoginViewController.swift and add the next beneath import UIKit:
To log in to talk, the app might want to authenticate utilizing the Firebase authentication service. Add the next code to the underside of signIn:
That line of code from the FirebaseAuth framework will submit the Notification.Identify.AuthStateDidChange notification that AppController is listening for. As soon as the notification is fired AppController will replace the basis view controller for you.
Construct and run your app, enter a show identify and faucet Get Began.
As soon as the consumer indicators in, they navigate to the ChannelsViewController, whose job it’s to point out the consumer an inventory of present channels and permit creating new channels. The desk has a single part to show all obtainable channels. There’s a toolbar on the backside with an indication out button, a label displaying your identify, and an add button.
Firebase Knowledge Construction
Earlier than you dive into sending messages in realtime, take a second and take into consideration the info construction first.
Cloud Firestore is a NoSQL JSON knowledge retailer. Primarily, every thing within the Cloud Firestore is a JSON object, and every key of this JSON object has its personal URL.
Right here’s a pattern of how your knowledge might look as a JSON object:
“MOuL1sdbrnh0x1zGuXn7”: // channel id
“3a6Fo5rrUcBqhUJcLsP0”: // message id
“content”: “Wow, that’s so cute!”,
“created”: “May 12, 2018 at 10:44:11 PM UTC-5”,
“4LXlVnWnoqyZEuKiiubh”: // message id
“content”: “Yes he is.”,
“created”: “May 12, 2018 at 10:40:05 PM UTC-5”,
Cloud Firestore favors a denormalized knowledge construction, so it’s okay to incorporate senderId and senderName for every message merchandise. A denormalized knowledge construction means you’ll duplicate lots of knowledge, however the upside is quicker knowledge retrieval. Tradeoffs — we haz them! :]
Chat Interface Setup
MessageKit is a souped-up UICollectionViewController that’s custom-made for chat, so that you don’t need to create your personal! :]
On this part of the tutorial, you’ll concentrate on 4 issues:
- Dealing with enter from the message bar.
- Creating message knowledge.
- Styling message bubbles.
- Eradicating avatar help.
Virtually every little thing you’ll have to do requires that you simply override strategies. MessageKit supplies the MessagesDisplayDelegate, MessagesLayoutDelegate, and MessagesDataSource protocols, so that you solely have to override the default implementations.
Open ChatViewController.swift and, on the prime of ChatViewController, outline the next properties:
personal var messages: [Message] = 
personal var messageListener: ListenerRegistration?
These properties are just like these added to the channels view controller. The messages array is the info mannequin and the listener handles clear up.
Now you can begin configuring the info supply. Above the MessageInputBarDelegate part, add the next:
// MARK: – MessagesDataSource
extension ChatViewController: MessagesDataSource
func currentSender() -> Sender
return Sender(id: consumer.uid, displayName: AppSettings.displayName)
func numberOfMessages(in messagesCollectionView: MessagesCollectionView) -> Int
func messageForItem(at indexPath: IndexPath,
in messagesCollectionView: MessagesCollectionView) -> MessageType
func cellTopLabelAttributedText(for message: MessageType,
at indexPath: IndexPath) -> NSAttributedString?
let identify = message.sender.displayName
.font: UIFont.preferredFont(forTextStyle: .caption1),
.foregroundColor: UIColor(white: 0.3, alpha: 1)
There’s a bit happening right here:
- A sender is an easy struct that has an id and identify property. You create an occasion of a sender from the nameless Firebase consumer id and the chosen show identify.
- The variety of messages within the assortment view might be equal to the native array of messages.
- Your Message mannequin object conforms to MessageType so you possibly can simply return the message for the given index path.
- The final technique returns the attributed textual content for the identify above every message bubble. You possibly can modify the textual content you’re returning right here to your liking, however these are some good defaults.
Construct and run the app, add a channel named Cooking after which navigate to it. It ought to now appear to be:
To date, so good. Subsequent, you’ll have to implement a number of extra delegates earlier than you begin sending messages.
Setting Up the Show and Format Delegates
Now that you simply’ve seen your new superior chat UI, you in all probability need to begin displaying messages. However earlier than you do this, you must deal with a number of extra issues.
First, you’ll high quality tune some format parameters from the MessagesLayoutDelegate. Add the next part under the MessagesDisplayDelegate part:
// MARK: – MessagesLayoutDelegate
extension ChatViewController: MessagesLayoutDelegate
func avatarSize(for message: MessageType, at indexPath: IndexPath,
in messagesCollectionView: MessagesCollectionView) -> CGSize
func footerViewSize(for message: MessageType, at indexPath: IndexPath,
in messagesCollectionView: MessagesCollectionView) -> CGSize
return CGSize(width: zero, peak: eight)
func heightForLocation(message: MessageType, at indexPath: IndexPath,
with maxWidth: CGFloat, in messagesCollectionView: MessagesCollectionView) -> CGFloat
Right here’s the break down:
- Returning zero for the avatar measurement will cover it from the view.
- Including somewhat padding on the underside of every message will assist the readability of the chat.
- On the time of writing, MessageKit doesn’t have a default implementation for the peak of a location message. Because you gained’t be sending a location message on this tutorial, return zero because the default.
The messages displayed within the assortment view are merely pictures with textual content overlaid. There are two kinds of messages: outgoing and incoming. Outgoing messages are exhibited to the suitable and incoming messages on the left.
In ChatViewController, exchange the MessagesDisplayDelegate extension with the next:
extension ChatViewController: MessagesDisplayDelegate
func backgroundColor(for message: MessageType, at indexPath: IndexPath,
in messagesCollectionView: MessagesCollectionView) -> UIColor
return isFromCurrentSender(message: message) ? .main : .incomingMessage
func shouldDisplayHeader(for message: MessageType, at indexPath: IndexPath,
in messagesCollectionView: MessagesCollectionView) -> Bool
func messageStyle(for message: MessageType, at indexPath: IndexPath,
in messagesCollectionView: MessagesCollectionView) -> MessageStyle
let nook: MessageStyle.TailCorner = isFromCurrentSender(message: message) ? .bottomRight : .bottomLeft
return .bubbleTail(nook, .curved)
Taking the above code step-by-step:
- For the given message, you examine and see if it’s from the present sender. Whether it is, you come back the app’s main inexperienced colour; if not, you come back a muted grey shade. MessageKit makes use of this shade when creating the background picture for the message.
- You come back false to take away the header from every message. You should use this to show thread particular info, akin to a timestamp.
- Lastly, based mostly on who despatched the message, you select a nook for the tail of the message bubble.
To tie this all collectively, add the next to the underside of viewDidLoad():
messageInputBar.delegate = self
messagesCollectionView.messagesDataSource = self
messagesCollectionView.messagesLayoutDelegate = self
messagesCollectionView.messagesDisplayDelegate = self
Verify that your app builds and you may navigate to one among your channels
Consider it or not, that’s all it takes to configure a MessagesViewController subclass to show messages! Properly, it will be extra thrilling to see some messages, wouldn’t it?
Time to get this dialog began!
Create the next technique under viewDidLoad() in ChatViewController:
// MARK: – Helpers
personal func insertNewMessage(_ message: Message)
guard !messages.accommodates(message) else
let isLatestMessage = messages.index(of: message) == (messages.rely – 1)
let shouldScrollToBottom = messagesCollectionView.isAtBottom && isLatestMessage
This helper technique is just like the one which’s in ChannelsViewController. It makes positive the messages array doesn’t already include the message, then provides it to the gathering view. Then, if the brand new message is the newest and the gathering view is on the backside, scroll to disclose the brand new message.
Add a check message by overriding viewDidAppear(_:):
override func viewDidAppear(_ animated: Bool)
let testMessage = Message(consumer: consumer, content material: “I love pizza, what is your favorite kind?”)
Construct and run the app; you’ll see your message seem within the dialog view:
Growth — that’s one good wanting chat app! Time to make it work (for actual) with Firebase.
First, delete viewDidAppear(_:) to take away the check message in ChatViewController and add the next properties on the prime of the file:
personal let db = Firestore.firestore()
personal var reference: CollectionReference?
On the prime of viewDidLoad add the next:
guard let id = channel.id else
reference = db.assortment([“channels”, id, “thread”].joined(separator: “/”))
The reference property is the purpose within the database the place the messages are saved. The id property on the channel is optionally available since you won’t but have synced the channel. If the channel doesn’t exist in Firestore but messages can’t be despatched, so returning to the channel record makes probably the most sense.
Subsequent add the next technique to the highest of the Helpers part:
personal func save(_ message: Message)
reference?.addDocument(knowledge: message.illustration) error in
if let e = error
print(“Error sending message: (e.localizedDescription)”)
This technique makes use of the reference that was simply setup. The addDocument technique on the reference takes a dictionary with the keys and values that characterize that knowledge. The message knowledge struct implements DatabaseRepresentation, which defines a dictionary property to fill out.
Open Message.swift and look at the implementation of DatabaseRepresentation. As you’ll be able to see, it maps its properties to readable keys and solely units the content material of the message if there isn’t a obtain URL.
Again in ChatViewController.swift, add the next delegate technique contained in the MessageInputBarDelegate extension:
func messageInputBar(_ inputBar: MessageInputBar, didPressSendButtonWith textual content: String)
let message = Message(consumer: consumer, content material: textual content)
inputBar.inputTextView.textual content = “”
Right here’s what’s happening:
- Create a message from the contents of the message bar and the present consumer.
- Save the message to Cloud Firestore utilizing save(_:).
- Clear the message bar’s enter area after you ship the message.
Construct and run; open up your Firebase App Dashboard and click on on the Database tab. Choose a channel, then ship a message within the app and you must see the messages seem within the dashboard in actual time:
Excessive 5! You’re saving messages to Cloud Firestore like a professional. The messages don’t seem on the display, however you’ll deal with that subsequent.
Synchronizing the Knowledge Supply
Add the next to under insertNewMessage(_:) in ChatViewController:
personal func handleDocumentChange(_ change: DocumentChange)
guard let message = Message(doc: change.doc) else
That is similar to how ChannelsViewController observes new database modifications. For brevity, the one change sort you deal with within the change assertion is add.
Subsequent, add the next code under the reference initialization in viewDidLoad():
messageListener = reference?.addSnapshotListener querySnapshot, error in
guard let snapshot = querySnapshot else
print(“Error listening for channel updates: (error?.localizedDescription ?? “No error”)”)
snapshot.documentChanges.forEach change in
Firestore calls this snapshot listener each time there’s a change to the database.
To wash issues up add a deinit in the direction of the highest of the file:
Construct and run your app; it is best to see any messages despatched earlier together with any new ones you enter:
Congrats! You’ve gotten an actual time chat app! Now it’s time so as to add one ultimate crowning glory.
To ship pictures, you’re going to comply with principally the identical precept as sending textual content with one key distinction. Somewhat than storing the picture knowledge immediately with the message, you’ll use Firebase Storage, which is best suited to storing giant information like audio, video or pictures.
To start out, you should add the Photographs import to ChatViewController.swift:
Add the next above the Helpers part:
// MARK: – Actions
@objc personal func cameraButtonPressed()
let picker = UIImagePickerController()
picker.delegate = self
if UIImagePickerController.isSourceTypeAvailable(.digital camera)
picker.sourceType = .digital camera
picker.sourceType = .photoLibrary
current(picker, animated: true, completion: nil)
This technique will current a picture picker controller to permit the consumer to pick a picture.
Subsequent, add the next to viewDidLoad():
let cameraItem = InputBarButtonItem(sort: .system)
cameraItem.tintColor = .main
cameraItem.picture = #imageLiteral(resourceName: “camera”)
cameraItem.setSize(CGSize(width: 60, peak: 30), animated: false)
messageInputBar.leftStackView.alignment = .middle
messageInputBar.setLeftStackViewWidthConstant(to: 50, animated: false)
messageInputBar.setStackViewItems([cameraItem], forStack: .left, animated: false)
Going by way of this:
- Create a brand new InputBarButtonItem with a tint colour and a picture.
- Join the brand new button to cameraButtonPressed().
- Lastly, add the merchandise to the left aspect of the message bar.
Sending a photograph message is a bit totally different then sending a plain textual content message. Saving a photograph to Firebase Storage returns a URL, however this may increasingly take a few seconds — maybe longer, if the community connection is poor. Moderately than blocking the consumer interface throughout this time, which can make your app really feel sluggish, you’ll begin sending the message and disable the digital camera message bar merchandise.
Add the next properties on the prime of ChatViewController:
personal var isSendingPhoto = false
self.messageInputBar.leftStackViewItems.forEach merchandise in
merchandise.isEnabled = !self.isSendingPhoto
personal let storage = Storage.storage().reference()
and add this technique to the underside of the Helpers part:
personal func uploadImage(_ picture: UIImage, to channel: Channel, completion: @escaping (URL?) -> Void)
guard let channelID = channel.id else
guard let scaledImage = picture.scaledToSafeUploadSize,
let knowledge = scaledImage.jpegData(compressionQuality: zero.four) else
let metadata = StorageMetadata()
metadata.contentType = “image/jpeg”
let imageName = [UUID().uuidString, String(Date().timeIntervalSince1970)].joined()
storage.youngster(channelID).baby(imageName).putData(knowledge, metadata: metadata) meta, error in
The isSendingPhoto property takes care of updating the digital camera merchandise when it modifications and the storage property is a reference to the basis of Firebase Storage. uploadImage(_:to:completion:) uploads a picture to the required channel within the Firebase Storage.
Under uploadImage(_:to:completion:), add:
personal func sendPhoto(_ picture: UIImage)
isSendingPhoto = true
uploadImage(picture, to: channel) [weak self] url in
guard let `self` = self else
self.isSendingPhoto = false
guard let url = url else
var message = Message(consumer: self.consumer, picture: picture)
message.downloadURL = url
This technique takes care of updating the isSendingPhoto property to replace the UI. As soon as the photograph add completes and the URL to that photograph is returned, save a brand new message with that photograph URL to the database.
Subsequent, to make use of sendPhoto(_:), add the next picture picker delegate strategies to the UIImagePickerControllerDelegate extension:
func imagePickerController(_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo information: [UIImagePickerController.InfoKey : Any])
picker.dismiss(animated: true, completion: nil)
if let asset = information[.phAsset] as? PHAsset
let measurement = CGSize(width: 500, peak: 500)
choices: nil) outcome, information in
guard let picture = outcome else
else if let picture = information[.originalImage] as? UIImage
func imagePickerControllerDidCancel(_ picker: UIImagePickerController)
picker.dismiss(animated: true, completion: nil)
These two strategies deal with the instances when the consumer both selects a picture or cancels the choice course of. When choosing a picture, the consumer can both get one from the photograph library or take a picture instantly with the digital camera.
Right here’s what this does:
- If the consumer chosen an asset, the chosen picture must be downloaded from iCloud. Request it at a hard and fast measurement. As soon as it’s efficiently retrieved, ship it.
- If there’s an unique picture within the information dictionary, ship that. You don’t want to fret concerning the unique picture being too giant right here as a result of the storage helper handles resizing the picture for you. Take a look at UIImage+Additions.swift to see how the resizing is completed.
Almost there! You’ve now arrange your app to save lots of the picture knowledge to Firebase Storage and save the URL to the message knowledge, however you’ve not but up to date the app to show these pictures. Time to repair that.
Get began by including the next to the underside of the Helpers part:
personal func downloadImage(at url: URL, completion: @escaping (UIImage?) -> Void)
let ref = Storage.storage().reference(forURL: url.absoluteString)
let megaByte = Int64(1 * 1024 * 1024)
ref.getData(maxSize: megaByte) knowledge, error in
guard let imageData = knowledge else
This technique asynchronously downloads a picture on the specified path from Firebase Storage.
Subsequent, change the guard assertion from a continuing to a variable within the handleDocumentChange(_:) technique:
guard var message = Message(doc: change.doc) else
Then, in handleDocumentChange(_:), exchange the content material of the .added case with the next:
if let url = message.downloadURL
downloadImage(at: url) [weak self] picture in
guard let self = self else
guard let picture = picture else
message.picture = picture
Notice: You’ll have to open the Firebase Console and allow Storage. To do that, first choose storage on the left, click on Get Began, then select default safety guidelines.
Construct and run the app; faucet on the little digital camera icon and ship a photograph message in your chat. Discover how the digital camera icon is disabled when your app is saving the photograph knowledge to Firebase Storage.
Kaboom! You simply made an enormous, dangerous, actual time, photograph and textual content sending chat app. Go seize your self your favourite beverage, you earned it!
The place to Go From Right here?
Use the Obtain Supplies button on the prime or backside of this tutorial to obtain the finished undertaking.
You now know the fundamentals of Cloud Firestore and MessageKit, however there’s lots extra you are able to do, together with one-to-one messaging, social authentication, and avatar show.
To take this app even additional, you possibly can check out the Firebase iOS documentation. You can too check out our 22 half video course on Starting Firebase!
I hope you’ve loved this Firebase tutorial; if in case you have any questions be happy to go away them within the non-anonymous but avatar-enabled dialogue under! :]
(perform(d, s, id)
var js, fjs = d.getElementsByTagName(s);
if (d.getElementById(id)) return;
js = d.createElement(s); js.id = id;
js.src = “//connect.facebook.net/en_US/sdk.js#xfbml=1&version=v2.5&appId=118196468516511”;
(doc, ‘script’, ‘facebook-jssdk’));