firebase ios messages Tech Tools & Libraries Tools and Libraries tutorial

Firebase Tutorial: Real-time Chat

Firebase Tutorial: Real-time Chat

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.

Firebase_Chat2-Wordpress-feature

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.

Getting Began

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:

Add Firebase to iOS App

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:

Login Screen

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.

Firebase Auth Console

Identical to that, you’ve enabled tremendous secret stealth mode! Okay, so it’s actually simply nameless authentication, however hey — it’s nonetheless cool. :]

Stealth Swift

Tremendous secret stealth mode achieved!

Logging In

Open LoginViewController.swift and add the next beneath import UIKit:


import FirebaseAuth

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:


Auth.auth().signInAnonymously(completion: nil)

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.

Empty Channel List

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:



“channels”: [
“MOuL1sdbrnh0x1zGuXn7”: // channel id
“name”: “Puppies”,
“thread”: [
“3a6Fo5rrUcBqhUJcLsP0”: // message id
“content”: “Wow, that’s so cute!”,
“created”: “May 12, 2018 at 10:44:11 PM UTC-5”,
“senderID”: “YCrPJF3shzWSHagmr0Zl2WZFBgT2”,
“senderName”: “naturaln0va”,
,
“4LXlVnWnoqyZEuKiiubh”: // message id
“content”: “Yes he is.”,
“created”: “May 12, 2018 at 10:40:05 PM UTC-5”,
“senderID”: “f84PFeGl2yaqUDaSiTVeqe9gHfD3”,
“senderName”: “lumberjack16”,
,
]
,
]

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:

  1. Dealing with enter from the message bar.
  2. Creating message knowledge.
  3. Styling message bubbles.
  4. 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.

Word: For extra info on customizing and dealing with MessagesViewController, take a look at the complete the documentation right here.

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

// 1
func currentSender() -> Sender
return Sender(id: consumer.uid, displayName: AppSettings.displayName)


// 2
func numberOfMessages(in messagesCollectionView: MessagesCollectionView) -> Int
return messages.rely


// three
func messageForItem(at indexPath: IndexPath,
in messagesCollectionView: MessagesCollectionView) -> MessageType

return messages[indexPath.section]


// four
func cellTopLabelAttributedText(for message: MessageType,
at indexPath: IndexPath) -> NSAttributedString?

let identify = message.sender.displayName
return NSAttributedString(
string: identify,
attributes: [
.font: UIFont.preferredFont(forTextStyle: .caption1),
.foregroundColor: UIColor(white: 0.3, alpha: 1)
]
)

There’s a bit happening right here:

  1. 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.
  2. The variety of messages within the assortment view might be equal to the native array of messages.
  3. Your Message mannequin object conforms to MessageType so you possibly can simply return the message for the given index path.
  4. 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:

Empty Message Thread

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

// 1
return .zero


func footerViewSize(for message: MessageType, at indexPath: IndexPath,
in messagesCollectionView: MessagesCollectionView) -> CGSize

// 2
return CGSize(width: zero, peak: eight)


func heightForLocation(message: MessageType, at indexPath: IndexPath,
with maxWidth: CGFloat, in messagesCollectionView: MessagesCollectionView) -> CGFloat

// three
return zero

Right here’s the break down:

  1. Returning zero for the avatar measurement will cover it from the view.
  2. Including somewhat padding on the underside of every message will assist the readability of the chat.
  3. 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

// 1
return isFromCurrentSender(message: message) ? .main : .incomingMessage


func shouldDisplayHeader(for message: MessageType, at indexPath: IndexPath,
in messagesCollectionView: MessagesCollectionView) -> Bool

// 2
return false


func messageStyle(for message: MessageType, at indexPath: IndexPath,
in messagesCollectionView: MessagesCollectionView) -> MessageStyle

let nook: MessageStyle.TailCorner = isFromCurrentSender(message: message) ? .bottomRight : .bottomLeft

// three
return .bubbleTail(nook, .curved)

Taking the above code step-by-step:

  1. 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.
  2. 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.
  3. 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

Another Empty Thread

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!

Creating Messages

Create the next technique under viewDidLoad() in ChatViewController:


// MARK: – Helpers

personal func insertNewMessage(_ message: Message)
guard !messages.accommodates(message) else
return


messages.append(message)
messages.type()

let isLatestMessage = messages.index(of: message) == (messages.rely – 1)
let shouldScrollToBottom = messagesCollectionView.isAtBottom && isLatestMessage

messagesCollectionView.reloadData()

if shouldScrollToBottom
DispatchQueue.primary.async
self.messagesCollectionView.scrollToBottom(animated: true)


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)
tremendous.viewDidAppear(animated)

let testMessage = Message(consumer: consumer, content material: “I love pizza, what is your favorite kind?”)
insertNewMessage(testMessage)

Construct and run the app; you’ll see your message seem within the dialog view:

Test Message

Growth — that’s one good wanting chat app! Time to make it work (for actual) with Firebase.

Sending Messages

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
navigationController?.popViewController(animated: true)
return


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)”)
return


self.messagesCollectionView.scrollToBottom()

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)

// 1
let message = Message(consumer: consumer, content material: textual content)

// 2
save(message)

// three
inputBar.inputTextView.textual content = “”

Right here’s what’s happening:

  1. Create a message from the contents of the message bar and the present consumer.
  2. Save the message to Cloud Firestore utilizing save(_:).
  3. 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:

Observe: The primary time you view the database on the console, it’s going to immediate you to pick a database sort. For this tutorial, you’re utilizing Cloud Firestore. After clicking the Create Database button, choose the check mode choice. For an actual world setup, you’ll need to configure safety guidelines for Firestore. You possibly can learn extra about safety guidelines right here.

Firebase Messaging

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
return


change change.sort
case .added:
insertNewMessage(message)

default:
break

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”)”)
return


snapshot.documentChanges.forEach change in
self.handleDocumentChange(change)

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:


deinit
messageListener?.take away()

Construct and run your app; it is best to see any messages despatched earlier together with any new ones you enter:

Previous Messages

Congrats! You’ve gotten an actual time chat app! Now it’s time so as to add one ultimate crowning glory.

Sending Pictures

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:


import Photographs

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
else
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():


// 1
let cameraItem = InputBarButtonItem(sort: .system)
cameraItem.tintColor = .main
cameraItem.picture = #imageLiteral(resourceName: “camera”)

// 2
cameraItem.addTarget(
self,
motion: #selector(cameraButtonPressed),
for: .primaryActionTriggered
)
cameraItem.setSize(CGSize(width: 60, peak: 30), animated: false)

messageInputBar.leftStackView.alignment = .middle
messageInputBar.setLeftStackViewWidthConstant(to: 50, animated: false)

// three
messageInputBar.setStackViewItems([cameraItem], forStack: .left, animated: false)

Going by way of this:

  1. Create a brand new InputBarButtonItem with a tint colour and a picture.
  2. Join the brand new button to cameraButtonPressed().
  3. 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
didSet
DispatchQueue.foremost.async
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
completion(nil)
return


guard let scaledImage = picture.scaledToSafeUploadSize,
let knowledge = scaledImage.jpegData(compressionQuality: zero.four) else
completion(nil)
return


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
completion(meta?.downloadURL())

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
return

self.isSendingPhoto = false

guard let url = url else
return


var message = Message(consumer: self.consumer, picture: picture)
message.downloadURL = url

self.save(message)
self.messagesCollectionView.scrollToBottom()

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)

// 1
if let asset = information[.phAsset] as? PHAsset
let measurement = CGSize(width: 500, peak: 500)
PHImageManager.default().requestImage(
for: asset,
targetSize: measurement,
contentMode: .aspectFit,
choices: nil) outcome, information in

guard let picture = outcome else
return


self.sendPhoto(picture)


// 2
else if let picture = information[.originalImage] as? UIImage
sendPhoto(picture)



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:

  1. 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.
  2. 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
completion(nil)
return


completion(UIImage(knowledge: imageData))

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
return

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
return

guard let picture = picture else
return


message.picture = picture
self.insertNewMessage(message)

else
insertNewMessage(message)

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.

Photo Message

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! :]

Obtain Supplies

(perform(d, s, id)
var js, fjs = d.getElementsByTagName(s)[0];
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”;
fjs.parentNode.insertBefore(js, fjs);
(doc, ‘script’, ‘facebook-jssdk’));