Unity Compress Image for Upload to Firebase

Update notation: Yusuf Tör updated this tutorial for iOS 14, Swift 5, Xcode 12 and MessageKit three.half dozen. Tom Elliott wrote the original.

It seems like every major app out there has a chat feature, and yours should be no different! This Firebase tutorial volition show you how to add real-fourth dimension chat to your app.

However, creating a conversation tool tin can seem similar a daunting task. There are no native UIKit controls specifically designed for chat, and you demand a server to coordinate and store the conversations between users.

Fortunately, in that location are some great frameworks out at that place to help you:

  • Firebase lets y'all synchronize real-time information without writing a line of server code.
  • MessageKit gives you a messaging UI that'southward on par with the native Messages app.

In this tutorial, you'll build RWRC, or Ray Wenderlich Relay Chat, an bearding chat app. If you lot've used IRC or Slack, you're already familiar with this sort of app.

A conversation thread about puppies

Along the way, you lot'll learn how to:

  • Cosign anonymously with Firebase.
  • Create multiple chat channels.
  • Leverage MessageKit for a consummate chat UI.
  • Synchronize data in existent-time with the Firestore database.
  • Employ Firebase Storage to send pictures.

Getting Started

Click Download Materials at the top or bottom of this tutorial to download the starter projection.

Open the starter project and take a quick look around. The starter projection contains a simple dummy login screen that saves the credentials to UserDefaults. Information technology also has a few helper classes for sending data to Firebase and saving information to UserDefaults.

In the starter project, you'll find ChannelsViewController.swift, which listens to changes in a Firebase Firestore database and updates a table view whenever the user adds a new channel. Y'all'll build a similar implementation to brandish chat messages instead of channels.

You'll find the Firebase SDK and MessageKit are already in the project as Swift Packages. These will automatically install when you open up the project.

Before you can run the app, you'll demand to configure Firebase.

Creating a Firebase Account

If you're new to Firebase, you'll demand to create an account. Don't worry! It's like shooting fish in a barrel and free.

Head to the Firebase signup site and create an business relationship. Then create and name a new Firebase project called RWRC. Brand certain that you disable support for Google Analytics every bit it won't be necessary for this tutorial.

In Xcode, click the target and alter the Parcel Identifier to whatsoever value yous like. Then select a Squad in the Signing section.

In the Project Overview in Firebase, click iOS. You'll meet instructions to add Firebase to your iOS app:

Add Firebase to iOS app

Next, enter the app'south bundle ID (the 1 you chose before in Xcode) and name (RWRC) into the grade and click Annals app:

Register bundle ID and app name with Firebase

Download and add together GoogleService-Info.plist to your project nether the Supporting Files group as shown in the Firebase instructions. This file contains the configuration data yous need to integrate Firebase with your app:

Download config file

Annotation: Do merely steps 1 and two of the instructions. The rest is already done in the starter projection and your app will crash if y'all duplicate the steps.

Now build and run. Yous'll see the post-obit:

Login screen

That'southward a good start, but right now the application login screen doesn't actually do annihilation. Y'all'll now hook that up to Firebase.

Enabling Anonymous Authentication

Firebase lets users log in through email or social accounts. However, it can also authenticate users anonymously, giving them unique identifiers without knowing their personally identifiable information.

To gear up anonymous authentication, open the Firebase console for the app you made earlier. Select Authentication on the left and click Become started:

Firebase authentication console

Then select Anonymous. Toggle Enable and click Save:

Enabling anonymous authentication

Only like that, you lot enabled super hugger-mugger stealth mode! Okay, so it's just anonymous authentication. But hey, it'southward still cool. :]

Stealth Swift

Information technology's now time to set the login within the app itself.

Logging In

Open LoginViewController.swift. Under import UIKit, add together:

import FirebaseAuth        

To log in to conversation, the app will need to authenticate using the Firebase hallmark service. Add the following code to the end of signIn():

Auth.auth().signInAnonymously()        

This method asynchronously logs into Firebase anonymously. If the device has already signed in, then the existing user is signed in, otherwise a new user is created. Once the sign in has completed Firebase posts the AuthStateDidChange notification that AppController is listening for. AppController updates the root view controller for you when the notification fires.

Build and run. Enter a brandish proper name and tap Go Started:

Empty channel list

In one case the user signs in, they automatically navigate to the ChannelsViewController. They'll encounter a list of current channels and take the option to create new channels. The table has a unmarried section to display all available channels.

At the bottom, they'll see a toolbar with a sign-out button, a label displaying their name and an add push button.

Before yous dive into sending messages in real-fourth dimension, take a minute to learn about the databases Firebase has to offer.

Choosing a Firebase Database

Firebase comes with two NoSQL JSON databases: Firestore and Realtime Database.

Initially, Firebase only had Realtime Database, an efficient, low-latency database that stores data in one big JSON tree.

However, this wasn't the best solution for all use cases. Then the Firebase team improved on the success of Realtime Database with a new, more intuitive data model chosen Firestore.

Firestore stores data as documents that comprise a prepare of cardinal-value pairs. It organizes these documents into collections. Each certificate can have sub-collections.

Each database has strengths and weaknesses.

The Realtime Database:

Strengths

  • Supports user presence, and so you can tell when a user is online or offline.
  • Has extremely low latency.
  • Charges for bandwidth and storage but not for operations performed in the database.
  • Scales to 200k concurrent connections.

Weaknesses

  • Has no multi-region back up. Information is available in regional configurations only.
  • Has limited sorting and filtering functionality.

The Firestore database:

Strengths

  • More structured than the Realtime Database and tin perform more complex queries on the information.
  • Designed to scale improve than the Realtime Database. The scaling limit is currently around i meg concurrent connections.
  • Has multiple data centers storing data in distinct regions and can support multi-regional configurations.
  • Charges primarily on operations performed in the database and, at a lower rate, bandwidth and storage.

Weaknesses

  • Doesn't allow documents to update at a charge per unit greater than one time per second.
  • Doesn't support user presence.

For this tutorial, you'll use Firestore equally your database. However, in a production chat app with lots of reads and writes to the database, you lot may choose the Realtime Database to reduce costs.

Yous can also use both the Firestore and Realtime Database within your app. For more data most these databases, have a await at Firebase's documentation.

At present that you know a niggling near the Firebase database, it is time to learn well-nigh the construction of the data you'll shop in the database.

Firebase Data Structure

You learned that Firestore is a NoSQL JSON information store, but what is that exactly? Essentially, everything in Firestore is a JSON object, and each key of this JSON object has its own URL.

Hither's a sample of how your data could look equally a JSON object:

{   "channels": [{     "MOuL1sdbrnh0x1zGuXn7": { // aqueduct id       "proper name": "Puppies",       "thread": [{         "3a6Fo5rrUcBqhUJcLsP0": { // message id           "content": "Wow, that's then cute!",           "created": "Apr 12, 2021 at x:44:11 PM UTC-5",           "senderId": "YCrPJF3shzWSHagmr0Zl2WZFBgT2",           "senderName": "naturaln0va",         },         "4LXlVnWnoqyZEuKiiubh": { // message id           "content": "Yep he is.",           "created": "April 12, 2021 at x:40:05 PM UTC-five",           "senderId": "f84PFeGl2yaqUDaSiTVeqe9gHfD3",           "senderName": "lumberjack16",         },       }]     },   }] }        

You can come across here there is a primary JSON object with a unmarried fundamental called channels. The channels value is an array of objects. These channel objects are keyed by an identifier and comprise a proper name & a thread. The thread is an array of objects, each of which is a single message containing the message in the content field, a created date and the identifier and name of the message's sender.

Firestore favors a denormalized information structure, so information technology's okay to include senderId and senderName for each bulletin item. A denormalized data structure means you'll duplicate a lot of information, but the upside is faster data retrieval.

That data structure looks good so it'southward time to crack on with setting upwards the app to handle those chat threads!

Setting Up the Chat Interface

MessageKit is a souped-upward UICollectionViewController customized for chat, so yous don't have to create your own! :]

In this section of the tutorial, you'll focus on four things:

  1. Treatment input from the input bar
  2. Creating message data
  3. Styling bulletin bubbles
  4. Removing avatar support

Virtually everything you need to do requires y'all to override methods. MessageKit provides the MessagesDisplayDelegate, MessagesLayoutDelegate and MessagesDataSource protocols, then y'all simply need to override the default implementations.

Note: For more information on customizing and working with MessagesViewController, cheque out the full documentation.

Open ChatViewController.swift. At the superlative of ChatViewController, define the following properties:

private var letters: [Message] = [] private var messageListener: ListenerRegistration?        

The messages array is the information model, and the messageListener is a listener which handles clean upwards.

At present you lot can starting time configuring the data source. Above the InputBarAccessoryViewDelegate section, add:

// Marker: - MessagesDataSource extension ChatViewController: MessagesDataSource {   // i   func numberOfSections(     in messagesCollectionView: MessagesCollectionView   ) -> Int {     return messages.count   }    // 2   func currentSender() -> SenderType {     return Sender(senderId: user.uid, displayName: AppSettings.displayName)   }    // three   func messageForItem(     at indexPath: IndexPath,     in messagesCollectionView: MessagesCollectionView   ) -> MessageType {     return letters[indexPath.section]   }    // 4   func messageTopLabelAttributedText(     for message: MessageType,     at indexPath: IndexPath   ) -> NSAttributedString? {     let proper name = message.sender.displayName     return NSAttributedString(       cord: proper name,       attributes: [         .font: UIFont.preferredFont(forTextStyle: .caption1),         .foregroundColor: UIColor(white: 0.3, blastoff: ane)       ])   } }        

This implements the MessagesDataSource protocol from MessageKit. There's a fleck going on here:

  1. Each message takes up a department in the drove view.
  2. MessageKit needs to know name and ID for the logged in user. You tell information technology that by giving it something conforming to SenderType. In your example, it'south an instance of Sender.
  3. Your Message model object conforms to MessageType so you return the message for the given index path.
  4. The concluding method returns the attributed text for the proper noun higher up each message bubble. You tin can change the text y'all're returning here to your liking, just these are some good defaults.

Build and run. Add a aqueduct named Cooking and and so navigate to it. It'll look like this:

Empty message thread

So far, so good. Adjacent, y'all'll need to implement a few more delegates earlier you lot start sending messages.

Setting Upward the Brandish and Layout Delegates

At present that you've seen your new crawly chat UI, y'all probably want to start displaying letters. But earlier you do that, yous accept to take care of a few more things.

Still in ChatViewController.swift, add together the following section below the MessagesDisplayDelegate section:

// MARK: - MessagesLayoutDelegate extension ChatViewController: MessagesLayoutDelegate {   // 1   func footerViewSize(     for message: MessageType,     at indexPath: IndexPath,     in messagesCollectionView: MessagesCollectionView   ) -> CGSize {     return CGSize(width: 0, acme: viii)   }    // two   func messageTopLabelHeight(     for message: MessageType,     at indexPath: IndexPath,     in messagesCollectionView: MessagesCollectionView   ) -> CGFloat {     return 20   } }        

This code:

  1. Adds a petty bit of padding on the bottom of each message to improve the chat's readability.
  2. Sets the height of the elevation label higher up each bulletin. This characterization volition agree the sender's name.

The letters displayed in the collection view are simply images with text overlaid. In that location are two types of messages: approachable and incoming. Outgoing letters display on the correct and incoming letters on the left.

In ChatViewController, replace MessagesDisplayDelegate with:

// Marker: - MessagesDisplayDelegate extension ChatViewController: MessagesDisplayDelegate {   // 1   func backgroundColor(     for message: MessageType,     at indexPath: IndexPath,     in messagesCollectionView: MessagesCollectionView   ) -> UIColor {     return isFromCurrentSender(message: message) ? .primary : .incomingMessage   }    // 2   func shouldDisplayHeader(     for message: MessageType,     at indexPath: IndexPath,     in messagesCollectionView: MessagesCollectionView   ) -> Bool {     return false   }    // iii   func configureAvatarView(     _ avatarView: AvatarView,     for message: MessageType,     at indexPath: IndexPath,     in messagesCollectionView: MessagesCollectionView   ) {     avatarView.isHidden = true   }    // iv   func messageStyle(     for message: MessageType,     at indexPath: IndexPath,     in messagesCollectionView: MessagesCollectionView   ) -> MessageStyle {     let corner: MessageStyle.TailCorner =        isFromCurrentSender(message: message) ? .bottomRight : .bottomLeft     return .bubbleTail(corner, .curved)   } }        

Taking the lawmaking to a higher place pace-past-step:

  1. For a given bulletin, you cheque to see if it'due south from the electric current sender. If it is, you return the app's primary green colour. If not, you return a muted greyness color. MessageKit uses this color for the background prototype of the message.
  2. Yous return false to remove the header from each message. Yous could use this to display thread-specific information, such as a timestamp.
  3. Then you hide the avatar from the view equally that is non necessary in this app.
  4. Finally, based on who sent the message, you choose a corner for the tail of the message bubble.

Although the avatar is no longer visible, information technology still leaves a blank space in its place.

Below setUpMessageView() add:

individual func removeMessageAvatars() {   guard      let layout = messagesCollectionView.collectionViewLayout       equally? MessagesCollectionViewFlowLayout   else {     return   }   layout.textMessageSizeCalculator.outgoingAvatarSize = .null   layout.textMessageSizeCalculator.incomingAvatarSize = .zero   layout.setMessageIncomingAvatarSize(.cipher)   layout.setMessageOutgoingAvatarSize(.cypher)   let incomingLabelAlignment = LabelAlignment(     textAlignment: .left,     textInsets: UIEdgeInsets(pinnacle: 0, left: 15, bottom: 0, correct: 0))   layout.setMessageIncomingMessageTopLabelAlignment(incomingLabelAlignment)   let outgoingLabelAlignment = LabelAlignment(     textAlignment: .right,     textInsets: UIEdgeInsets(meridian: 0, left: 0, bottom: 0, correct: 15))   layout.setMessageOutgoingMessageTopLabelAlignment(outgoingLabelAlignment) }        

This code removes the blank space left for each hidden avatar and adjusts the inset of the top characterization above each message.

Next, add together the following to the bottom of viewDidLoad():

removeMessageAvatars()        

Finally, prepare the relevant delegates. Add the following to the bottom of setUpMessageView():

messageInputBar.delegate = cocky messagesCollectionView.messagesDataSource = self messagesCollectionView.messagesLayoutDelegate = self messagesCollectionView.messagesDisplayDelegate = self        

Build and run. Verify that you tin can navigate to one of your channels.

Another empty message thread

Believe it or non, that's all it takes to configure a MessagesViewController to display messages!

Well, it would be more exciting to see some messages, wouldn't it? Fourth dimension to become this chat started!

Creating Messages

In ChatViewController, beneath viewDidLoad(), add:

// Marker: - Helpers private func insertNewMessage(_ message: Message) {   if letters.contains(message) {     return   }    messages.append(message)   letters.sort()    let isLatestMessage = messages.firstIndex(of: message) == (messages.count - 1)   permit shouldScrollToBottom =     messagesCollectionView.isAtBottom && isLatestMessage    messagesCollectionView.reloadData()    if shouldScrollToBottom {     messagesCollectionView.scrollToLastItem(animated: true)   } }        

This method adds a new message. It first makes certain the bulletin isn't already nowadays, so adds it to the drove view. Then, if the new bulletin is the latest and the drove view is at the lesser, information technology scrolls to reveal the new bulletin.

At present add the post-obit underneath viewDidLoad():

override func viewDidAppear(_ animated: Bool) {   super.viewDidAppear(animated)      let testMessage = Bulletin(     user: user,     content: "I beloved pizza; what is your favorite kind?")   insertNewMessage(testMessage) }        

This adds a simple test bulletin when the view appears.

Build and run. You'll see your bulletin appear in the conversation view:

A test message

Boom! That's i expert-looking chat app! Time to make it work for real with Firebase.

Sending Messages

First, delete viewDidAppear(_:) to remove the test message in ChatViewController. So, add together the following backdrop at the elevation of the class:

individual permit database = Firestore.firestore() private var reference: CollectionReference?        

Beneath viewDidLoad(), add:

private func listenToMessages() {   baby-sit let id = channel.id else {     navigationController?.popViewController(animated: true)     return   }    reference = database.collection("channels/\(id)/thread") }        

Outset the id on the channel is checked for cipher because you lot might not have synced the channel still. Information technology shouldn't exist possible to send messages if the channel doesn't exist in Firestore even so, and so returning to the aqueduct list makes the virtually sense. So the reference is set up to reference the thread assortment on the channel in the database.

Then, in viewDidLoad(), add the post-obit beneath super.viewDidLoad():

listenToMessages()        

Adjacent, add the following method to the top of the Helpers section:

private func salve(_ bulletin: Message) {   reference?.addDocument(information: message.representation) { [weak self] error in     guard let self = self else { return }     if let error = fault {       print("Mistake sending message: \(fault.localizedDescription)")       render     }     self.messagesCollectionView.scrollToLastItem()   } }        

This method uses the reference you just set upwards. addDocument on the reference takes a dictionary with the keys and values representing that information. The message data construction implements DatabaseRepresentation, which defines a dictionary property to fill out.

Back in ChatViewController.swift, add the following consul method within InputBarAccessoryViewDelegate:

func inputBar(   _ inputBar: InputBarAccessoryView,   didPressSendButtonWith text: String ) {   // 1   let message = Message(user: user, content: text)    // 2   save(bulletin)    // 3   inputBar.inputTextView.text = "" }        

Here yous:

  1. Create a Message from the contents of the input bar and the electric current user.
  2. Save the message to the Firestore database.
  3. Articulate the input bar's text view later on you send the message, prepare for the user to ship the next message.

Side by side, you need to create a database.

Creating the Database

Open up your app's Firebase console. Click Firestore Database on the left and Create database:

Creating a Firestore database

When you create a database for a real-world setup, yous'll want to configure security rules just they're non necessary for this tutorial. Select Start in test mode and click Side by side. You tin read more virtually security rules in the Firestore documentation.

Configuring security rules for Firestore

You can configure Firestore to store data in different regions beyond the world. For now, leave the location as the default setting and click Enable to create your database:

Setting the Firestore database location

Build and run. Select a aqueduct and send a bulletin.

Y'all'll run across the messages appear in the dashboard in real-time. You may demand to refresh the folio if y'all don't run into whatsoever updates when you add together the first message:

Saving a message to the database

High 5! You're saving messages to Firestore like a pro. The messages don't appear on the screen, but y'all'll have care of that next.

Synchronizing the Data Source

In ChatViewController, add the following below insertNewMessage(_:):

private func handleDocumentChange(_ modify: DocumentChange) {   baby-sit let message = Message(document: modify.certificate) else {     return   }    switch change.blazon {   case .added:     insertNewMessage(bulletin)   default:     break   } }        

For simplicity in this tutorial, the only alter blazon you handle in the switch statement is added.

Next, add together the post-obit code to the bottom of listenToMessages():

messageListener = reference?   .addSnapshotListener { [weak self] querySnapshot, error in     baby-sit permit self = self else { render }     guard permit snapshot = querySnapshot else {       print("""         Error listening for aqueduct updates: \         \(error?.localizedDescription ?? "No mistake")         """)       render     }      snapshot.documentChanges.forEach { modify in       self.handleDocumentChange(change)     }   }        

Firestore calls this snapshot listener whenever there's a modify to the database.

You need to clean up that listener. And so add together this in a higher place viewDidLoad():

deinit {   messageListener?.remove() }        

Build and run. You'll run across any letters sent before along with whatsoever new ones y'all enter:

Messages showing in the channel

Congrats! Yous have a real-time chat app! Now information technology's fourth dimension to add together one final finishing affect.

Sending Images

To send images, you'll follow mostly the same principle as sending text with one primal difference. Rather than storing the image data directly with the bulletin, you'll use Firebase Storage, which is meliorate suited to storing large files similar audio, video or images.

Add the following at the top of ChatViewController.swift:

import Photos        

Then above the Helpers section add together:

// Marker: - Actions @objc private func cameraButtonPressed() {   let picker = UIImagePickerController()   picker.delegate = self    if UIImagePickerController.isSourceTypeAvailable(.camera) {     picker.sourceType = .camera   } else {     picker.sourceType = .photoLibrary   }    present(picker, animated: true) }        

This method presents an image picker controller to let the user select an prototype.

And so, add the following code below removeMessageAvatars():

individual func addCameraBarButton() {   // 1   let cameraItem = InputBarButtonItem(type: .organization)   cameraItem.tintColor = .primary   cameraItem.image = UIImage(named: "camera")    // 2   cameraItem.addTarget(     cocky,     action: #selector(cameraButtonPressed),     for: .primaryActionTriggered)   cameraItem.setSize(CGSize(width: 60, height: 30), animated: false)   messageInputBar.leftStackView.alignment = .center   messageInputBar.setLeftStackViewWidthConstant(to: 50, blithe: false)    // three   messageInputBar     .setStackViewItems([cameraItem], forStack: .left, animated: simulated) }        

Here you:

  1. Create a new InputBarButtonItem with a tint color and an image.
  2. Connect the new button to cameraButtonPressed().
  3. Add the detail to the left side of the message bar.

Next, add the following to the lesser of viewDidLoad():

addCameraBarButton()        

Sending a photo message is a little different than sending a plain text message. Uploading a photo to Firebase Storage may take a couple of seconds, possibly longer if the network connection is poor.

Rather than blocking the user interface during this time, which will make your app feel slow, y'all'll outset sending the message and disable the camera message bar detail.

At the top of ChatViewController add together:

private var isSendingPhoto = false {   didSet {     messageInputBar.leftStackViewItems.forEach { particular in       baby-sit let item = detail as? InputBarButtonItem else {         return       }       particular.isEnabled = !self.isSendingPhoto     }   } }  private let storage = Storage.storage().reference()        

isSendingPhoto updates the photographic camera button to exist enabled only when there is no photo sending in progress. storage is a reference to the root of Firebase Storage.

Then, add this method to the bottom of the Helpers section:

individual func uploadImage(   _ image: UIImage,   to channel: Channel,   completion: @escaping (URL?) -> Void ) {   baby-sit     let channelId = aqueduct.id,     allow scaledImage = prototype.scaledToSafeUploadSize,     allow information = scaledImage.jpegData(compressionQuality: 0.4)   else {     return completion(zippo)   }    allow metadata = StorageMetadata()   metadata.contentType = "image/jpeg"    let imageName = [UUID().uuidString, String(Date().timeIntervalSince1970)]     .joined()   permit imageReference = storage.child("\(channelId)/\(imageName)")   imageReference.putData(data, metadata: metadata) { _, _ in     imageReference.downloadURL { url, _ in       completion(url)     }   } }        

This method uploads an epitome to the specified channel in the Firebase Storage.

Below uploadImage(_:to:completion:), add together:

individual func sendPhoto(_ image: UIImage) {   isSendingPhoto = truthful    uploadImage(image, to: aqueduct) { [weak self] url in     guard let self = self else { render }     self.isSendingPhoto = false      guard let url = url else {       render     }      var message = Message(user: self.user, paradigm: prototype)     message.downloadURL = url      self.save(message)     cocky.messagesCollectionView.scrollToLastItem()   } }        

This method first updates isSendingPhoto to update the UI. Then it kicks off the upload and one time the photo upload completes and returns the URL to that photo, it saves a new message with that photograph URL to the database.

Earlier y'all can use sendPhoto(_:), you need to add some paradigm picker delegate methods. To UIImagePickerControllerDelegate add together:

func imagePickerController(   _ picker: UIImagePickerController,   didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Whatsoever] ) {   picker.dismiss(animated: true)    // ane   if let asset = info[.phAsset] equally? PHAsset {     let size = CGSize(width: 500, peak: 500)     PHImageManager.default().requestImage(       for: asset,       targetSize: size,       contentMode: .aspectFit,       options: nil     ) { result, _ in       guard let prototype = upshot else {         return       }       self.sendPhoto(prototype)     }    // 2   } else if let image = info[.originalImage] as? UIImage {     sendPhoto(image)   } }  func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {   picker.dismiss(blithe: true) }        

These 2 methods handle cases when the user either selects an paradigm or cancels the selection process. When choosing an image, the user can either become one from the photograph library or take an image directly with the camera.

Here's a breakdown:

  1. If the user selected an nugget, you lot asking to download information technology from the user's photo library at a fixed size. Once it'southward successfully retrieved, you send it.
  2. If there's an original image in the info dictionary, ship that. You don't demand to worry about the original prototype being besides big because the storage helper handles resizing the image for y'all. Look at UIImage+Additions.swift for the resizing implementation.

Nearly there! You've ready your app to save the image data to Firebase Storage and save the URL to the message data. But y'all oasis't updated the app to brandish those photos still.

Fourth dimension to fix that.

Displaying Photos in Threads

To the bottom of the Helpers department, add:

private 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) { data, _ in     guard allow imageData = data else {       completion(nil)       return     }     completion(UIImage(data: imageData))   } }        

This method asynchronously downloads an image at the specified path from Firebase Storage.

Next find handleDocumentChange(_:) and change the variable in the guard from a constant to a variable:

guard var message = Message(document: change.certificate) else {   return }        

So, in handleDocumentChange(_:), replace the content of the .added case with:

if let url = message.downloadURL {   downloadImage(at: url) { [weak self] image in     guard       allow cocky = self,       permit prototype = image     else {       return     }     message.image = prototype     self.insertNewMessage(bulletin)   } } else {   insertNewMessage(message) }        

Now build and run the app. Tap the little camera icon and send a photo message in your chat. Notice how the photographic camera icon isn't enabled when your app saves the photo data to Firebase Storage.

Sending a photo message to the channel

Kaboom! You fabricated a large, bad, real-time photo and text sending chat app.

Grab your favorite drink. You earned it!

Where to Get From Hither?

Click Download Materials at the top or lesser of this tutorial to download the completed project.

You at present know the basics of Firestore and MessageKit. Only in that location's enough more you can exercise, including one-to-ane messaging, social authentication and avatar display.

To take this app fifty-fifty further, have a look at the Firebase iOS documentation. Yous tin as well have a look at our 22 role video grade on Beginning Firebase!

I hope you've enjoyed this Firebase tutorial. If you lot have whatever questions, experience complimentary to exit them in the not-anonymous withal avatar-enabled discussion beneath! :]

brigmangraid1944.blogspot.com

Source: https://www.raywenderlich.com/22067733-firebase-tutorial-real-time-chat

0 Response to "Unity Compress Image for Upload to Firebase"

إرسال تعليق

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel