SwiftUI: Build Chat App for Beginners (2021) | Afraz Siddiqui | Skillshare

Playback Speed


  • 0.5x
  • 1x (Normal)
  • 1.25x
  • 1.5x
  • 2x

SwiftUI: Build Chat App for Beginners (2021)

teacher avatar Afraz Siddiqui, Engineer | Designer | Teacher

Watch this class and thousands more

Get unlimited access to every class
Taught by industry leaders & working professionals
Topics include illustration, design, photography, and more

Watch this class and thousands more

Get unlimited access to every class
Taught by industry leaders & working professionals
Topics include illustration, design, photography, and more

Lessons in This Class

17 Lessons (2h 28m)
    • 1. Welcome: Class Overview

      1:38
    • 2. Getting Started

      4:28
    • 3. App Icon & Images

      2:34
    • 4. Create Chat Row

      13:30
    • 5. Chat Field & Send Button

      10:00
    • 6. Conversation List

      8:59
    • 7. Navigation Bar Buttons

      4:12
    • 8. Search View

      10:39
    • 9. Dismiss Search

      5:16
    • 10. Sign In View

      8:41
    • 11. Sign Up View

      7:55
    • 12. Set Up Firebase

      10:57
    • 13. Sign In, Sign Up, Sign Out

      14:05
    • 14. Search Users

      9:53
    • 15. Observing Chat

      13:24
    • 16. Sending Messages

      17:15
    • 17. Chat Photos

      4:59
  • --
  • Beginner level
  • Intermediate level
  • Advanced level
  • All levels
  • Beg/Int level
  • Int/Adv level

Community Generated

The level is determined by a majority opinion of students who have reviewed this class. The teacher's recommendation is shown until at least 5 student responses are collected.

172

Students

1

Project

About This Class

d9777d4d.png

Welcome to the SwiftUI Chat App Class – I'm glad you're here!

In this course, we will learn to build a realtime SwiftUI Chat App together from scratch for beginners. You'll get immersed in to the world of mobile iOS development with first hand experience building an interactive app. Not only will we cover designing beautiful views, but we will make the backend support the real time nature of messaging applications.

What You'll Learn

  • Fundamentals of SwiftUI & iOS Development
  • Building modular views that look great
  • Saving & Passing Data in Your App
  • Propert Code Architecturee Patterns
  • Building Chat App
  • Setting Up Firebase for Backend
  • Sending Realtime Messages
  • Managing & Sorting by Dates
  • Using Xcode, Previews, & Simulators
  • Professional Clean Code Patterns
  • Much More!

Who Is This Class For

This course was designed with beginners in mind. Whether you have never touched SwiftUI in your life or you have begun to dabble in the world of it, this course is perfect for you. A chat app is robust enough to cover a variety of concepts, while being scoped enough to stay concise and enjoyable.

This course does require you to be working on a Mac computer and install Xcode from the Mac App Store which is free. All code available in the class or that you right is yours to use and extend!

Class Project:

Meet Your Teacher

Teacher Profile Image

Afraz Siddiqui

Engineer | Designer | Teacher

Teacher

Hi there, I'm Afraz!

I've been writing code, building awesome software, and designing creative projects for over 12 years. If you are interested in

iPhone or Android App Development Website Development / Design Cloud Computing Photoshop / Adobe Suite Tech In General, Just Improving Yourself

you are certainly in the right place!

I have taught hundreds of thousands of people just like you how to succeed in a variety of disciplines through actionable content, interactive feedback/help, and straightforward walk throughs. With a background in technology and entrepreneurship I have successfully navigated several complex challenges and learned a lot along the way - now it is my time to give back.

I am currently leading Software @ Microsoft and previously comp... See full profile

Class Ratings

Expectations Met?
  • Exceeded!
    0%
  • Yes
    0%
  • Somewhat
    0%
  • Not really
    0%
Reviews Archive

In October 2018, we updated our review system to improve the way we collect feedback. Below are the reviews written before that update.

Why Join Skillshare?

Take award-winning Skillshare Original Classes

Each class has short lessons, hands-on projects

Your membership supports Skillshare teachers

Learn From Anywhere

Take classes on the go with the Skillshare app. Stream or download to watch on the plane, the subway, or wherever you learn best.

Transcripts

1. Welcome: Class Overview: Hey there, welcome to building a chat app with Swift UI. My name is, uh, froze and I will be your instructor. This is the class where we are going to be building out a real time chat application with Swift UI for iOS. We're going to begin by creating beautiful views. This includes signing in, sign-up conversation list and of course, the chat UI with these awesome chat bubbles that we have come to know and love. From there, we're going to grasp the fundamentals of passing data around so we can actually get our chat app to be functional. We'll hook it up to our backend, which will consist of Firebase. And Firebase will be used to store our users conversations, messages, and even user accounts. We'll go step-by-step to understand every fundamental concepts and talk through how to extend and customize all the pieces so you can go and make this really shine out as your own piece of work. Now, I've had the privilege of working on iOS and mobile apps for more than 10 years now, contributing to applications with hundreds of millions of users. I'm so excited to have the opportunity to share what I've learned with you and also builds out a real-world application from scratch. To finish. That all said, I'm so excited you're here. Let's get into the project. Let's start coding. 2. Getting Started: All right, We're gonna get started with our project by going ahead and opening up Xcode and creating a new one. If you don't have XCode installed already, you can grab it from the Mac App Store for free, but we'll go ahead and create a new project here. We're going to want to stick with the app template under the iOS tab. And let's go ahead and give our project a name of messenger. Now, before you continue, make sure your language here is set to Swift and both your lifecycle and interface are set to Swift UI. Go ahead and continue save the project folder wherever you'd like. I'll save it to my desktop and let's jump right in in this first part. But we are going to do is we're just going to talk about a high level of the pieces will need. I will go ahead and create some empty files where we'll be working. So our application is going to have a bunch of views, so things a user can see, as well as a bunch of objects that represent data. So things like messages and conversations and all that good stuff. So we'll create some of those now before we do so on the left-hand side, what I like to do is create at least two or three folders just to organize those files. So I'm going to right-click and create a new group. And the first folder will create here is for views. Let's go ahead and do that two more times. The second one is going to be models, which will be all of the data related needs that we have. And the last one that I like to create, I call IT resources. So basically anything that doesn't fall into models or views. Now we already have one view by default at the template gave us called content of view that I'm going to drag interviews. And what we're gonna do with the rest of this stuff. So assets and this messenger app, dot swift, we'll drag it under resources. So that said we have this content view already which isn't really named the best because content views a little vague. So if we think about a broadly or applications should launch to a conversation list. So the first thing we're gonna do is actually rename this to be a conversation ListView. And go ahead and change it down here at the bottom of this file. And you'll also need to change it in your Messenger app dot swift file. This is the first view that your Apple launched up to. Now that we've got that, we want to right-click our views folder and we want to create a couple more files. So I'm going to right-click this and a new file. We're going to search for a swift UI file. And we want to create a bunch of views. So presumably we're going to allow a user to sign in. So I'm going to create a Swift UI view called sign-in view. We also need to allow a user to sign up because by default they're not going to have an account. So we're going to create another view, and this one is going to be a sign up view. Now if we think about how a chat UI works, basically it's a view which has a bunch of rows for different messages. So the first thing I'm going to create here is a chat view, which is going to have several chat message rows inside of it. So there is our chat view. Let's go ahead and create one more. And that will be a, another swift UI view. And this one will be a chat row. And the last thing we'll need, the last major thing I should say we'll need for views is a place for a user to search for other users. So I like to call this the search view. So we'll go ahead and create this as well. And the last view actually we needed here is a Send button to actually send a message. Now we'll talk about later on in a separate video why we're abstracting the send button. But for now, let's just go ahead and create a send by n as another swift UI view to like that. So we've got all of these views here. This is actually believe it or not, all the views we're going to need in this app. Now the next thing we're gonna do is create two files under models. And this time we're going to stick with just a swift file. And the first one is going to be a message dot swift file. And this is going to represent a single message in our app. And the second one and the last one I should say that we're going to create in this folder. We're gonna go ahead and call it a app state model. And this is going to basically represent all of the data and operations or app needs to do so. If we're chatting with somebody, you know, the current messages, sending a new message and all that good stuff. So this is basically the foundation that will need to get started. So this is the end of this part. I'll see you guys in the next part, we will bring in some images and app icons. 3. App Icon & Images: All right guys, welcome back to the next part. In this part we're going to bring in some images as well as some app icon assets so we can start actually seeing a pretty nice application. So in our project we have this assets folder here, and you'll see we've got an app icon in here already. Then before we mess with the app icon, we want to bring in two photos as profile pictures for some mock data as we build stuff out. So I'm going to right-click and hit new image set and I'm going to call the first one photo one. The next one I'm going to do in a call it photo 2. And then the last one we're going to need here is a Apple logo. Go ahead and create one called logo. Now, in the assets provided, you're going to find these images that I've got on the right-hand side, we can grab them one-by-one and simply drag them in. So there is our logo, photo1. Pick any of these two head shots that we got here and drag them in. So there's that first one and the second one. Go ahead and drag that remaining image just like that. So now that that's done, we actually need to create an app icon. So the way we can do that, first and foremost is close out Xcode. And we want to open up something that you can find the App Store called the assets catalog. So as the catalog is a tool that can help you generate an app icon, There's also free versions available online if you just Google iOS App Store, icon generator. But basically we'll drag on the image that we want to generate a icon set for in the various templates here under iOS, we're going to hit app icon. I will go ahead and hit Create. Now, that'll do is it'll create this folder here, which has a app icon dot image set in. It's going to want to drag that into our project. So let's go ahead and open up Xcode again. We're going to jump into our project. And you guys can see here we have an app icons. I'm going to click it and then hit Backspace, and that'll go ahead and actually remove it. And now what we can do is we can drag in this folder here, which is app icon dot app icon set. And if you drag this guy in and click on it, you'll see that you have your app icon now in all of the different sizes that Xcode wants to be in. So now that we have it will delete that folder and we can expand this once more. So those are all the app icons and images we're going to need in the project. That's the end of this part. I will see you guys in the next one, we start putting together some views. 4. Create Chat Row: All right, welcome back guys. In this part we're in and build out arguably the most important view in our application, which is a single chat rows. So if you guys recall, we had created all of these views here and we have one element here which represents a single chat row. Now on the right-hand side you see this big gray area. This is actually very important with Swift DY because it lets you see your application's views as you build them. So the first thing I'll do appear with this drop-down and select a preview device. So I've got the 12 Pro Max. We're going to hit this little Resume button at the top right-hand side. And what this will let your project do is basically render a preview of whichever view you are working on. So it's a little finicky and can be a little slow, so just bear with it. And once it loads up, we're going to build out a chat bot with the appropriate colors and all that good stuff. So here's a little iPhone. I'm going to actually put it into dark mode by clicking on this icon. And you can see there's an option here for various things to be configured. I'm going to change the scheme to be dark and our device will be in dark mode. So let's talk about a chat row. So if you think about chat room abstractly, it's nothing more than a horizontally stacked piece of text. We'll first add a h stack, which is the horizontal stack. And inside of here we're going to want some text. So I'm going to create a label and we're just going to put hello world. And here a few times I'm just going to copy and paste it so we can talk about a large piece of text. And this is what we're starting with. So this is kind of ugly. The first thing that we wanna talk about here is we want to set a background color on our actual text, but we also want it to look like a chat bubble. So what we're gonna do is we're actually going to put in another horizontal stack in here. We're going to add some padding onto our text. And then we'll go ahead and add a little bit color to our view by saying the background here will be colored dots, blue. And now we're looking a little better. So we not only have some padding between the edges of this horizontal box, but we also have a color. So this is looking pretty good. We probably also wants a nice corner padding, so a corner radius, I should say. So we're going to add another modifier for a corner radius. And we can go ahead and let's make sure we do this on a new line. So let's go ahead and line break and say corner radius. And we can maybe go ahead and give it six points of corner radius. So that's looking pretty good. Now he also wants to know if this chat row is for a message we sent or a message we received. Because based on that, we need to align the bubble to the right or left. So we do need to add initializer. And what we'll go ahead and say is, what's the type of this message? So we're gonna say pass in a message type. And basically we want to go ahead and hold onto this by saying type is going to be a message type. And what we can do down here now in the initializer is we can go ahead and say self.age type is going to equal a type. Now what the heck is message type, because we haven't actually created this. So let's actually jump into our models and go into message dot swift. And we're going to create an enum called message type. It's going to have a string extension right there. And there are going to be two types of messages, right? So you can either send a message or you either receive a message. So we'll say the message was either sent or received. Pretty simple. Let's jump back into our chat row. Alright, so these arrows should hopefully go away if you go ahead and just hit Command B, sometimes executed like to take a moment to get everything situated, but once it does, you'll see the errors will go away. So now we know if this row is where something we sent or received. So what I'm going to next do is say, we're going to create a new property is sender. Just to make things cleaner. We're going to say if is sender, which is going to be a bool, and we're gonna say return if the type equals cent. So this way we can use this as an if statement in our view to do things differently based on what the state of this row is. The first thing we'll do is we'll say if it's the sender, go ahead and add a spacer. And what that's gonna do is that is going to push our horizontal label here, this inner stack to the right. Now the reason it's not doing it right now is because our text is super long. So let me make this text smaller so you can see it in action. The other thing you'll notice is our preview isn't updating live anymore. And the reason is because we have to hit try again sometimes when you switch between files, it's slightly annoying but you know, it says Xcode for yes, so just bear with it here. Let's see if we have any issues. Looks like we do have an issue down here. We actually do need to pass in a mock piece of data. So I'll say type is send, sent. And that way the preview knows that you know, which version should it render. So now we can see our message is pushed over to the right-hand side. So that's looking pretty good. Now let's also go ahead and take a look at what this looks like if we pass in a received message. So we can have multiple previews here by wrapping this in a group and simply passing in another view. And what you'll see on the right-hand side is, we'll see that we have two iPhones now. The top one is sent and bottom one is received. So received is looking a little weird, it's centered. So what we want to go ahead and do here is we're gonna take this same if, and I'm going to put it right below this. So we're gonna say, if not sender, go ahead and add a spacer there. And you'll see this gets pushed to the left. Now what we're basically doing here is we're saying add a spacer to the left or right-hand side of the horizontal stack and label based on the state. So that makes a lot of sense. Now one other thing that you might notice is that the actual row is flush up against the edges of the actual device. This is intended because when we put this inside of the chat view itself where we have multiple messages, we'll add some padding there. One thing that is a little strange is the color should be different for a sent and received message. So for a sentence blue, which is okay. And we also want to make sure that the foreground text color is always white. So we're gonna say foreground color is going to be color dot Whites. However, in the actual received message case. So this bottom one here, we don't want this to be blue. We actually want this to be a light gray color. Well, we can say is for the background color, if it's the current center, we can say show blue. Otherwise, we're going to say show a color, which will be a gray for color system Grey 4. And a gray for color is a nice gray color that looks really nice in both the dark mode and light mode. So that's looking pretty good. Now one other thing that you might be wondering about is, well, what happens if I put this into light mode? What you'll notice is if I put this into light mode, this text is still going to be whites, and that's a problem because we can't read it. So this is fine for it to be white in the sent message case because the background is blue, but in the received case, we also want the foreground color to be dynamic. So what do we want it to be? Well, first we're gonna say if a sender is center, we're going to say the color is going to be blue, which is perfectly fine, or color is going to be white. And in the other case, we're gonna say the color will be a label color. And a label color is a special color which is going to adapt to either black and white to based on light mode and dark mode. So this is looking pretty good. So let's talk about further padding. So you notice when the message is small, things look pretty nice. But watch what happens when I copy and paste this and make it a super long message. You'll notice that it takes up the entire width of the screen. And while that might work functionally, it doesn't look too nice because when you have a bunch of messages, they take up the whole wave and you kinda want some padding either on the left if it's the sender or on the right if it's a received message. So once again, we're going to be clever about this and add some padding based on if it's the sender or not. So let's talk about how we can actually achieve that. The way we're going to achieve that is by saying dot padding. And if it's the sender, what we want to do is we want to add leading padding, which is left-hand side. Otherwise we're going to add trailing padding. And what we can go ahead and do is we can add a hard-coded amount of padding that we want, but we're going to say take the screen size, so UIs, green dot main, get its width and then divide that by three. And what you'll notice now is this message has a padding on its left, so that's the max width they can go to. And this one has some as well. So these are starting to look pretty good. We also have our corner radius, so it's nice and rounded. The last thing we're gonna do in our chat row here is when we have a sent message, we're not going to show any image, but we have a received one. We might want to show a. And maybe a little circle chat head for the person who has sent that message. So let's talk about how we can actually achieve doing that. Well, it's pretty simple actually because we already have this horizontal stack here and this one represents the container for the label. Now what we want to go ahead and do is add a component right on the right-hand side of it. So we're gonna say if knot is sender, we're going to want to add a circular image. Now we also want that circular image to be inside of a vertical stack and be pushed down with a spacer. So it doesn't look like it's centered with the message. So now what I'm gonna do is add a circle here just so we can see what it actually ends up looking like with a foreground color. We want to also make sure that we add a frame to this. So I'll go ahead and say width is maybe 45 and high is also going to be 45. So let's see what that looks like. So we saw that circle before, but now we don't see it at all actually. So I wonder why that is. So we have this here. We have a vertical stack and a spacer, but we're not seeing that anymore. So let's go ahead and add a background color to see if we can even see our stack there. So we do see our stack and we can see that it's taking up the entire height here. So what we presume to see is that circle is probably all the way at the bottom, which in fact is. Now this is okay in this view, what you'll notice is when we bring these views into our list of chat messages, it will be aligned to the bottom of this. Let's actually do that right now. So I'll go ahead and do is, let's jump into our chat view. And inside of here, what we are going to want to do is have a couple of components. The first thing that we're going to want inside of here is going to be a scroll view. Now this scroll view will be vertical and it's basically going to nest a bunch of chat rows. So let me actually go ahead and create h hat row here. And let's try to spell things correctly. So we'll say chalk row, and I'm going to go ahead and say the first one will be sent, and it will add one more and this one will be received. The right-hand side, you'll see that these are starting to look a lot better. Now you guys might remember I mentioned we want to add some padding here so we can go ahead and add a three-point padding here onto both of these. And that'll give us a little bit of space between each of these messages. The last thing we'll do before wrapping up this part is we had a hard-coded the padding on the left and right For sent and received was since we've added this little circle or we're going to show an image, it looks a little weird. The reason it looks a little off is because this message is more tightly constrained in its width. So what we're gonna do back into the chat row is we're going to change the padding here to be dynamic also. So we already dynamically pick leading or trailing. So in this case, what I'll say is if it's the sender, take the width of the screen and divide it by 3. Otherwise, go ahead and divide it by five. And what you'll notice is if you actually take a look down here, is that this message is no longer as cramped and it's a little easier to visualize when you go to the chat view. It looks a lot nicer. So here is the bare-bones of what our applications chat UI pieces will look like. That's the end of this part. I'll see you guys in the next part. 5. Chat Field & Send Button: All right guys, welcome back. So in this part, we're going to continue building out our chat UI here. So we already put together these rows. Now we need to have a couple more things to make chat actually work. The first thing that comes to mind is a text field to actually enter in some text. So before we even do that, well, we'll also want is a title up here based on, you know, whoever we're chatting with. So if I go ahead and say add a navigation title, I'm just going to add in a hard-coded string in here for Samantha. And we're gonna make this be dynamic later on once we actually begin to chat with a particular user, and we don't see it on the right-hand side, which is actually expected and we'll, we'll talk about that in a moment. But first, let's actually go and add the text field. So we have this scroll view already, and this scroll view represents all of our chat messages that we can scroll. So that's perfectly fine. What we actually want to do is tweak this and nest our scrollView inside of a vertical stack. And the reason we want to actually do this is because we don't want our TextField to actually be scrolling. We want it to be below all of our messages. Now our text field needs to have a field and then on the right of it we want to have a Send button. So the way we achieve horizontally positioned items is a horizontal stack. So I'm going to go ahead and create that here. Now the first thing we want here is a field, like I said. So we're going to create a text field. It's going to have a place holder of message dot, dot, dot. And then we also need to bind some text to it, which is going to be a dollar, C dollar, and we're going to call it message. And basically this up here, we need to create this state to hold the typed text in. So I'm going to say state message is going to be of type string. And by default it'll be some empty string and you'll see the error disappear. So that's looking pretty good. Let me actually resume our preview and the right-hand side, I'm going to put it back into dark mode. Developing in dark mode just looks a little nicer in my personal opinion. Feel free to keep it in lights. So let's take a look at what this field is going to look like. So we have it at the very bottom here. It's a little tough to see. The first issue you'll notice is we don't even have a background color and we also don't have any padding, so it looks a little strange. So let's go ahead and actually add some modifiers here. So first we're going to add some padding to it. Then I'm going to add a background color. And we're going to take the secondary system background, which just like the label color we talked about, is going to adapt based on light mode and dark mode. So there is our field, it's starting to look a little better. Let's also go and add a little bit of padding on this horizontal stack itself. So I'm going to add some padding to that and see if that makes things look a little better. So definitely does. It's not touching the edges of the screen. But we'd also maybe you want this to have not as pointy of a corners, we can add maybe a nice corner radius of four points just to round up the edges just a little bit. So it gives us a better look. You can actually even increment this to maybe seven if you want that look. But that's probably good enough for our field. Now on the right-hand side of this field we want to send button. So you asthma at recall, we had created another view for the Send button here. Now we're going to create the sand, but in rate in the chat view and then we'll move it there so you guys can see the benefit of having it in a different component. So let's create it right below here. It's going to be nothing more than a pretty basic and buy-in with a action as well as a view itself. So go ahead and pick this one. Now what is this actually going to be? It's actually going to be an image. And we're going to use one of the system images that Apple provides for us, which is called a airplane. So it actually looks like a little bit of a cent indicator. Actually not airplane. What I'm thinking of is paper plane. So if you take a look at the paper plane here actually looks a lot nicer. It's a little small and we don't have too much color going on just yet. So let's go ahead and make it bigger and maybe put it inside of a circle. So I'll go ahead and do is say a resizable, which will make it really, really big that we want to also go ahead and say that it has an aspect ratio to fit and that way it won't be distorted. The next thing that we want to do is give it a maybe foreground color of color dot whites. And we want this to be inside of a circular clipped background. So I'm going to say background is going to be color blue. And then we can say clip to a particular shape and we're gonna say clip to a circle. So if you take a look now it'll be inside of it just like that. Now, you'll notice that it's a little cut off. And the reason that it's doing that is because we need to decrease the size here. So we're gonna say frame. And this frame is going to have a width of perhaps 55 and a height of 55 as well. And that'll make the actual frame smaller, but our images still cut off. And the reason is because we need to decrease the font size of this as well. So we'll go ahead and add a font modifier. And we want the one with the system size. And you can play with this number a little bit, but I'm going to stick with 22 and we'll see what that looks like. So it looks like it didn't actually decrease the size there. So let's see what is going on. So we have resizable here. Let's go ahead and move that they're above the actual other modifier. Go ahead and hit Command B. Sometimes a preview doesn't update. So one important lesson here is the order of modifiers actually makes a huge difference. So if we go ahead and move this font silence, move it down here. Let's see what that looks like. Where you can actually also do is change the font size to have some drastic changes. That way you can see if it's actually doing anything at all. So what I'm going to actually do is let me comment out all of these modifiers and let's get the font size getting smaller before we go and change things further. All right, go ahead and hit Resume. Once we have this getting smaller, will know that it is being applied appropriately. So let's see how we can achieve that. So let's go ahead. We already said the image needs to be resizable. We also said font size is going to be pretty darn small here. So let's see why this actually is it getting smaller. So we can actually go ahead and say this is a font size. You can use a variety of different font sizes here. So we have resizable already. So let's try putting this before that and we now see that it gets really small. So let's actually play with this number and make it a system size of perhaps 42 or 32 and see if that adjusts it, we can see that it does in fact adjusted. So we'll we'll go ahead and do is stick with 44 maybe, which is looking pretty good. We'll add resizable after this, the resizable modifier. And actually it looks like we don't even need it, but let me uncomment all of these modifiers here. And let's see what that ends up looking like. You can hit Command slash to comment and uncomment the whole line. And now we can see this is looking better, but still it's a little cut off, so let me decrease the size a little more. We can try 22. Maybe it's a little too small. Try 33, which should look just about correct. Which for me it does. So we're going to stick with that. All right, so now that we have all of this situated, when we actually tap on this button, we want to go ahead and send a message. But what you can see in here already is that this file is getting kind of large. So we can actually do is we can take this entire button that we have here and we could actually move it inside of our sandbox and filed here. And the benefit of this is we can keep these pieces separate, keeping our files more isolated and smaller. Now when we do come in here, when we press Send, we do need to get access to the information in the text field. We need to pass that in a via a binding. So you need to create a binding here, which is going to be a text of type string. And we tap on the button, we're going to say self dot send message. And we need to go ahead and create this year. So we'll go ahead and say Send message. We're going to validate that the text is not empty. Otherwise we can't send an empty message, will go ahead and return. But how do we pass that text in here? Because we certainly don't have it in here. So what we can do is we can go back to our chat view where we originally had the button and we can add in is sent by n. And we can actually pass in the text that we have up above by saying dollar message, which will just pass the message up here, down to the sandbox n. And if you hit Resume, you'll notice once everything updates that everything should look and feel the same. It will actually show your button here until you hit a live preview or you do run it in a simulator. And the reason for that is because we have separated these views and this preview here is only for this view. So that is the end of this part. I will see you guys in the next one as we further build up a UI before hooking up all of the backend functionality to make our chat application work. 6. Conversation List: All right, welcome back guys. In this part we're going to start working on the conversation view, which is where we will see a list of all of our conversations that we can tap into to open up the related chat. So if you guys recall, we have this conversation view under Content View here. We had renamed this and we should probably also rename the file here. So it's not as confusing. So there's our conversation view. We can go ahead and hit Resume here on the right-hand side to start loading up our preview. Let's go ahead and hit try again. Sometimes it's the sides to be finicky so you have to hit it. I've realized either two or three times. Sometimes you might just even have an error. So what will actually also do if the preview isn't working? I've actually opened up some simulators here. We'll also just start to run the app in the simulator to see what it actually looks like. So let's start building this out. So our conversation view should basically have a base component of a navigation view. And inside of it what we want is a vertical stack which has a scroll view in it. So you have a number of options here. You could just add a scroll view directly, which we will do here first. So we'll add a scroll view, like so. Now we want to have a title on here. So we're gonna say navigation title. And this title is going to be conversations. And if you go ahead and give this a run either in your preview or in your simulator. This is what we are working with. Now what we expect to see is a list of our conversations. If we don't have any, it'll be empty. Of course, the other two things that we want is a button at the top left here that we can tap to sign outs and a button at the top right to search for users. And it looks like our preview decided to start working here. So let's go ahead and first created that list of conversation. So, but we can actually do is I'll just mock some data here. So let's say hypothetically, once we get there, we'll actually make it hookup to our database, but we have a array of conversations. Let me actually call a user names. And let's say here we have Joe, Jill, Bob. But we'll go ahead into, is we basically want to have a loop over these guys. And for each of them, we want to actually show a circle image for their profile picture and their name. So we can simply do a for each over the usernames where the ID is going to be self. And this is going to be named in pretty simple. Now what do we actually want to do inside of here? What we want to have a horizontal stack with. Keep in mind that we also wanted to be tappable. And the reason we need it to be tappable is because we want to open up the chat to view with the related top we give it. So there's two ways you can accomplish this. One is a navigation link, can be, other is a tap gesture. We are going to use a navigation link here. And there's actually a variety of options to choose from. So the one that is most easy to hook up with is a destination and a label. Now we're just a nation is going to be the Chat view at the moment. And our label will actually be a horizontal stack. On the left-hand side of it. We are going to want a circle to represent our profile picture. We'll go ahead and give it a nice pink color so we can actually see it. And on the right of the circle we're going to want a name. I'll also go ahead and maybe bold the naming dividend, a font with a nice big size of perhaps 32. Let's see what that looks like. So go ahead and hit Resume. Let's see if our preview can do its job. All right, so there are our circles and that now it's looking pretty off. The first reason that it's looking pretty off is because in the horizontal stack we wanna go ahead and add a spacer to the rights. So it pushes everything to the left-hand side, which it's not doing at the moment. So let's figure out why. So we're inner scroll view. We have this navigation link. Now this has a horizontal stack. Now we haven't given this a color, sir, rather a frame. So let me go ahead and give our circle a frame of maybe 65 by 65 for both the width and the height. And once we do that, if the alignment should match for all of them because they're all aligned vertically. Now we can also see that the circle is touching the edge of the screen, which doesn't look all that nice. So where you're gonna go ahead and add a padding modifier onto our horizontal stack. And that'll go ahead and add a nice bit of padding between each of these rows. Now, we can also see that the text here is a blue color, which doesn't look too great either. So let's go ahead and add a foreground color modifier and we're going to use colored label. And if you recall, that is the color for black or white based on light mood or dark mode. Now we tap on these, we want to go into chat. So if you actually hit this little Play button and then tap on any of these, you'll see that it will actually in fact open up the chat view, so it's looking pretty darn good already. The other thing that's pretty important is when we tap into a conversation, it's important to let the chat view know for whom did we tap. So we're going to actually pass into our chat view, the other username, which is going to be name. Now that said, we need to actually go to our chat to view and allow it to accept a other username and other username here is basically just the username of the other person that we are chatting with so we can see and hear self-doubt. Other username is going to be other username. And we also want to go ahead and create that on here as a string. Now that we have the other person's username, we can replace our hard-coded Samantha with other username. And we also want to make sure we pass in a mock username for our actual preview here. So we'll pass in Samantha for our mock again. So now if you go ahead and give this a run, you'll see, well first we have an error somewhere. So let's go ahead and see what's going on. So this is other user name. And it looks like this is complaining because we probably have a typo. So other username, yes, in fact, we do. We have two m's instead of one, so let me go ahead and fix that. So we'll put that there, put that there and fix it here as well. And now what do you actually give it a run? What you'll notice is that in your simulator, you should see the username for whom you tapped the actual cell for. So if we come over here and tap into maybe Jill, we want to send a message, we'll see Jill at the top here. So this is looking pretty good. We also want to bring in those icons now. And one other thing that we intentionally actually left out is we had hard coded these messages. And for our actual chat application, we don't want to hard-code then we actually want to pass in the text. So we're going to extend our chat rows here to take in at some text. And I'm just going to say Hello World in a both of these just for the time being. So we'll say Hello World here as well. Now the actual messages, of course, will come from our database, but we do need to go into chat row and modify the init in here to allow for some text to be passed in. So I'll go ahead and put that there and we're going to hang on to that text by saying self dot text is going to equal text and that we want to come up here and just go ahead and add a text property. Now we can actually use this text in the label here instead of using our dummy data. And if you come back into your actual application by giving it a run and see it looks like we have an error somewhere here, probably the preview down here. Yep. All right, Let's go ahead and add in some test data here, as well as on test data here, and give it a run in your actual simulator. What you'll notice is when you tap on these, you can now see the test data that we're passing in. But in the actual application will just pass in the data that we get from the real conversation. So that's actually the bare-bones of this. We'll do the top ends in the next parts just to keep things nice and concise in terms of these parts. So thanks for watching. I'll see you in the next part. 7. Navigation Bar Buttons: What's going on, guys, welcome back to the next part in this par, we are going to be adding the top left and right by n In our conversation view to allow the user to search for other users as well as actually log out of their account. So the way we're gonna do this is by using something called a toolbar modifier. So we have this scroll view here in our entire conversation view, we want to go ahead and add a tool bar modifier here. And what we want in here are two toolbar items. Now a toolbar item can take a placement as well as content. So first what we're gonna do is we're just gonna do the search by instance. It's nice and simple. Here what we want is navigation, leading, Baba, and if you're autocomplete isn't working like mine, where you could in fact do is type this whole thing out. So we can say a toolbar, item, placement, dots, navigation, bar, leading, which will be the right-hand side. And then in the content here itself, well, we are going to actually use is a button. This button will have an action, but it's actual contents of what it will show will be a system name. And we're going to use a magnifying glass, are gonna say magnifying glass, all lowercase, I believe. And when we tap on this basically in here, we want to show search view. So you might be wondering, well, do we want this to be in navigation link? We actually in fact do not. You could use a navigation link as well. We'll talk about the approaches, the two approaches and pros and cons for each. But first, let's go ahead and give this a run in our simulator by hitting Command R and we expect to see a search by. And so we actually added it to the left where we wanted it to the right. So this should actually be trailing like events. Let's go ahead and copy and paste this for the other one which will be leading, which is what we had before. And the leading button will be a little simpler, but we want leading to B is a simple by MS. Says Sign Out. And what we actually tap on it, we're going to go ahead and say self.name Hall, the sign-out function, which we are just going to stub out here for a moment. Let's go ahead and give that a run. And now you'll see we have a search icon here as well as sign outs. So we want the sign I'll buy into actually work. So you guys might recall that we had added a search view earlier on and believe in the first video. Now in the search view itself, I'm just going to stub this out with a vertical stack. And I'm just going to add a navigation title. So when we show it, we could actually see something. I'm going to add a title here. We'll go ahead and call it search. So pretty simple and nothing too fancy. Now, back in our actual conversation view, there are two ways I mentioned that we could actually go about opening up the search view. And the first way is by tapping on the button here and toggling some state. And the other way that we can do that once again, is with a navigation link. So let me actually show the navigation link approach since they're both pretty common and we already used navigation links, so might as well stick with it at this points. So once again, if you take a look at the list of options here, we're going to stick with destination and label. So we'll go ahead and save that this is the label and the destination is going to be our search views. Go ahead and create that. And let's see if this error here it goes away, which it in fact should go ahead and give this a run. And when you tap on this, we expect to open up search just like that. So looking pretty darn good, that is the end of this part. In the next part, we'll go ahead and build out the pieces in search and the rest of our UI before we hook all of this jazz up. So I'll see you guys in that part. 8. Search View: Welcome back guys. In this part, we're going to pick up where we left off building out the part of the view for a search. So search is going to be pretty simple. All we really need, and here is a TextField, a search by n, and then a way to render out the users that we actually found via the search. So we had to add it as vertical stack here. So let's first and foremost, add in a text field. Our TextField will have some placeholder texts for username. Since that is the thing we will be searching on the basis of. And when the user is actually typing, we are going to hang on to their text in a state property that we'll go ahead and call this text up here. Feel free the name it whatever you want. If you want to call it something else, it's going to be of type string and by default it's going to be a empty string. So that's looking pretty good. But once again, we have a problem where our actual text field is going to need a little bit of formatting to look nicer. So let me actually put my preview here back into dark mode and I'll show you guys a trick where you don't have to redundantly make your things look good again. So if you recall, in our chat view, we had set up our text field right here to have a background color and padding as well as some corner radius here. So, well, we could actually do is something a little clever. And that is, we can create a custom view modifier, which is basically all of these things we add here and just use that to wrap all of this stuff. So I'll go ahead and create it up here. You can go ahead and create a struct and you can call it a custom field. It is going to be of type View modifier. And there is a single function for body in here. And you can go ahead and say content and paste in all of those modifiers that we were using previously. And now what you could actually do is instead of using these everywhere, you can say we want to add a custom modifier. So you can say go ahead and add a modifier of type custom fields. And now I can take this here, jump into search, and I can actually apply this with a single line instead of having to copy and paste the background and padding and all that jazz. So if you go ahead and take a look at your preview now, your text field will start to look more appropriate. So that's looking awesome next up, but we want to go ahead and do is we want to add a search button right below this. I'm going to add a button here. We'll go ahead and call it search. And we're basically going to search as soon as the user taps on this stuff. And we want to push both of these components to the top of the screen. So we'll add a spacer down there. Now we also want to show our actual results in between here if we have any. So let's once again hypothetically for now we'll just stick on a usernames array. I'm just going to put one in here. We'll call it Julia. And what we basically want to do is we want to do a for each over the results that we get back. And we basically want to show a row that the user can tap on to start a conversation or re-enter that conversation if it exists with a given user. Now we're going to wrap this inside of a list because the list just makes it look a little nicer because it gives us some out of the box formatting. So if you go ahead and give this a run, we should start seeing Julia here momentarily. If in fact you don't, then we'll see what's going on. Actually though, we will actually see it yet because we need to actually add in the text for name. And I'm gonna go ahead and just bold it. And now we'll start seeing right there. Now we also want a circle on the left of the name because the name is just a little too plain. So we'll add a circle inside of a horizontal stack once more. Make sure you go ahead and also give this a frame with a fixed frame width and height. So we can go ahead and say height is 55. Height is 55 as well for both width and heights. The name ago operate on the right of it and then we'll have a space or to the right of that just to push everything to the left. And instead of bolting, it actually says go ahead and bump up the font size to something like maybe 24. We'll see what that looks like. And go ahead and hit this little live preview play button and we'll see if anything changes. But let's look in pretty good in my opinion. So the other pretty critical thing here is when we tap on each of these, something that should be happening. And the way we can control this is by adding on the horizontal stack a on tap modifier for this gesture. And we tap on this, but we actually want to do is we not only want to dismiss the search and go back to the other screen, but we also want to tell our conversation to go ahead and open up a chat with whoever we tapped on. So how do we communicate that between views was pretty straightforward actually. So let's work on the first one here. So we first need to pass in completion handler. So I'm going to create an init here and pass in a escaping completion handler. And basically we want to pass back a string for the username with whom we want to start a chat. So we're going to have the completion live on here, just like that. And now we can say self.com is going to equal completion. Pretty simple. And once we actually tap on one of these rows, we can say go ahead and call the completion handler with the name that we are currently using for this row. So that'll go ahead and actually let the other view know what we want to pass back. Now keep in mind, we'll need to go ahead and actually update the places we add the search view so we don't get any errors. So the main places that preview code at the bottom, as well as in our conversation view. So in our conversation view, we've got to find where the search of you is, which in fact it's right here. And we can use this shorthand syntax to do name in. Name will be the other user's name that we tagged to start a conversation with. So now in here what we want to actually do is say self dot other username is name. We are going to hang on to this other username and right in the conversation here. So we're gonna go ahead and say state var, other username will be a empty string by default. So that's looking pretty good. And the next thing that we want to actually go ahead N2 is say self, that show chat is going to be true. And we also want to hang on to show chat up here. So we're gonna say once again, var show chats by default it's going to be false because that would be a little strange. And let's see what else do we want to do? We also need to dismiss that other view, which we'll do probably in the next part. However, you'll start to notice that we're getting into a place where we now need to keep track of our data more so, so this actually brings us to our model's folder and our empty app state model. So what we're going to want to do in here is creates a class called app state model of type observable object. And don't forget to import Swift UI in here. And basically what will, what we're going to do is we're going to hold all the actual main data on here for our apps and things like is user signed in. So based on that, we want to show sign in or sign up. We're also going to hold the current user being chatted with. So basically the other user as well as things like messages and conversations. We're also going to want to add a couple of extensions on here where we're going to add the core functionality. So I like to group it by extension. It's kinda subjective, but we're definitely going to want some way to search for users. So I'm just going to copy and paste this a few times. We're going to want to get our conversations, will also want to be able to get chat and send messages. Pretty simple. And then finally, the most important thing is we want to be able to sign in and sign up users. So this is the bare bones of what all of this stuff is going to do. Let's jump back into our conversation view. And let's talk about how we're going to actually open up the chat to view automatically by tapping something in search. The way we're going to actually do that is by leveraging this show chat and other username. And what we'll do is at the bottom of this navigation view, we are going to automatically show a navigation link if the other username is not empty. So I'm gonna say if other username is not empty, well, we can go ahead and do is we can say navigation link. And we want to go ahead and show a navigation Lincoln. This time we want to pick the option here, which gives us a ability to say is active. So there's a couple of them, so I'll stick with this one here. The string will be empty. Chat view will be Chat view for the destination passing envy other username. And then finally is active is going to be show chat. And this is going to be false by default. But once we tap on something, we'll call this completion block here, making it true. And therefore it'll show this. So if you go ahead and just hit the play button to make sure everything is building, we can wrap up this part here. The one thing you'll notice by going in here and just tapping on somebody is that it still opens up another search, which is now what we want. We actually want to show the chart view. So what we wanna do is dismiss this first by sweat allowing it to swipe back. And then we'll actually go and allow us to show a different one. So that's all I've got for this part. I'll see you guys in the next one where we will hook up a dismissing the search UI piece here. 9. Dismiss Search: All right guys, welcome back. In this part, we're going to pick up where we left off and we still haven't actually got our search view dismissing. We tap on one of these rows. So we're going to work on that and opening up the chat to view. So let's come back into our search view and it's pretty simple to actually achieve that. What we want to go ahead and do once we jump into our search view is add a presentation mode environment property. So go ahead and do add environments. You'll see the autocomplete here and the one that you want is backslash dot presentation mode. Sometimes the autocomplete doesn't like to cooperate, but just go ahead and type it out. You can go ahead and call it a presentation mode. And it's going to be of a type of binding, and it'll be a presentation mode. Now what this is allowing you to do is basically dismiss the given presentation by using this property wrapper. So what we wanna do is wherever we call the completion, which is right here, we're going to basically say presentation mode wrapped value dot dismiss. Now what's going to happen is this will dismiss the search view, but there's one more problem that we want to take care of. And that is, once this completion is called, You see that we immediately assigned to show chat and the other username. So the problem becomes is we first want to wait for the search view to disappear before we tried to show the chat view. And the way we can fix this is by just adding a slight delay to assigning those. We can say dispatch after 1 second. So now plus 1 second and just paste those assignments in here. And if you go ahead and give it a run, what you'll notice now is when you tap on one of these, not only will dismiss, but it should in fact show the chat window. Now it's not showing chat because we actually need to make one more tweak. We have this if other username isn't empty, this navigation for the chat view outside here, we actually wanted to be inside the scroll view here, but at the bottom. So not in the actual navigation view, it needs to be in the scroll view itself. And let's see, Go ahead and give that a run and let's see what it's going to look like now. All right, we'll tap on this, we'll tap on this. And we should get a push of our chat view as soon as it dismisses. So it is now showing up looking at pretty good, the UI is actually in pretty good shape. The next thing we're going to want to do is finish up our last piece of view, which is the sign in, sign up and then we get a hook all of this stuff up. So before we close out this part, we first want to talk about how do we handle knowing if the user is signed in or not? Well, it's pretty simple. We had created on, in our models folder a app state model. What we'll want to go ahead and do is basically observe some property. And when it changes, we want to either assign the user in or sign the user out. So we're going to create an ad published. And what we'll go ahead and call this here is four, and I'm going to go ahead and call it show, showing, sign in. And it's going to be a Boolean by default and it's going to be false by default. So this is going to do is by default, it's going to go ahead and show sign in. Now, it's going to need to actually be assigned if the user signs in, we want to make sure that they stay signed in. But we'll do that in the next part. But in conversation view, we don't have a references app state, model. So how the heck do we get a reference to it? What's pretty easy? We're going to open up our resources and go to Messenger app. And we actually want to pass in a environment object directly here by saying Environment Object. And we're going to pass in a new app state model. Now this is really powerful and the reason is it allows you to access that environment objects and all of your subviews now of your application. So we can say at Environment Object, I can say var model is going to be a app state model. And now we know if the user is signed in or not because we can get it out of this model. So if the user is signed in, we want to show this scroll view for conversations. Otherwise, what we want to go ahead and do is show a full screen cover is what it's called in the Swift UI other the sign-in view. So here I'm gonna go ahead and say full screen cover. And we want is presented and content. So we can say this is dollar a model. And from the actual model itself we are going to say showing sign-in. Pretty simple. We can do this one without the dollar and the content itself here is going to be the sign in view. Now, we'll wrap it up here and in the next part we'll build out the sign in view. 10. Sign In View: All right guys, welcome back to the next part. In the last part, we left off with our full-screen it cover here. We want to build out the sign in view now so we actually have something to show the user. But first, let's go ahead and run our app to see what happens now when we open up the app, you'll notice we still see our conversations list here. So that's not that great. So what we actually want to do is go back to our app state model. And we want showing sign-in to actually be true by default if the user is not signed. And we'll show that. And if they are, we will show the conversation list. So now you see it shows this view when the app launches, which is just the Hello World template of the sign-in screen. So let's jump into sign-in. If you, and let's build this out, it's fairly straight forward. We're going to have a couple of things in here. First thing is a vertical stack. We want to have a nice heading. We're going to have some text fields. We want a way to actually let the user sign-up. And I believe that's all we really want. Let's also go ahead and we probably want to add a navigation title up here. We may or may not want it. Let's actually exclude it for now, for the time being, but let me do the heading first. So we probably want a nice image of the Apple logo at the top because logos look pretty cool, so we're going to make it resizable. I'm also going to save the aspect ratio will be fit and we'll also go ahead and give it a fixed width and height so it's not too big or too small, will try to 120 and we'll adjust as needed. So there is our image now rebel lower image, we want an application heading, so I'm going to go ahead and call this messenger. We're going to bold it. We're also going to go ahead and give it a font system size of, let's try 34 and let's see what that looks like. And let me just add a spacer here as well too. So it pushes both of those to the top. So let's go ahead and give that a run. And this is what we are working with. So we've got a really cool icon and we've also got our apps title. Now right below this, we want to show two fields for a username and password. So let's go ahead and put those inside of a vertical stack. So now in here we'll go ahead and say text field. And we'll go ahead and the first one here will be the username. And the actual field needs to bind it to a state. So we're gonna, instead of calling it text, maybe we can call it username. And then right below it we want another one, and this one is going to be called password. The place holder will be password. And actually this one, we want it to be a secure field. And the difference between a text field and secure field is pretty obvious. To secure field will just show you asterix when you type inside of your text. Now finally, we want to add a custom modifier to both of these. Before we do that, let's fix both of these errors by creating the states up here. We'll say this has a state of var username, which will be a string by default. And the next thing that we want is going to be a another state, and this will be a var password to go ahead and spell that correctly AS will be a password. Once again, this will be a string. And you guys remember I mentioned that environment object we can use in all of the views, some of the top level one. So here we can say Environment Object Model is going to be a app state model. And the reason we want that in here is this is the object, It's going to handle the sign-in. Hence, we are going to want to call a function on it in a bit. So now that we've got this vertical stack here, these fields, let's go ahead and add a custom modifier. And you guys might recall we have a custom fields already, so I'm going to add it to both of those. And let's also go ahead and add in a nice looking button that the user can actually tap to sign in. So what do we want in here? What we're going to have this button say sign in. We're gonna give it a frame with a whip, 220 and a high of 50, which is just a pretty common. But in size, we'll give it a background color. I'll also go ahead and give it a corner radius. And we'll probably want a foreground color of perhaps white. So let's go ahead and give this a run. Let's see what our form is looking like. I'll re and bear with us. All right, so here's what this is looking like. So the one thing that doesn't look too great is I don't want the field to be touching the edges of the screen on the left and right. So, but we could do to simplify that and fix it up is add a padding modifier to be a vertical stack, which contains our field and buttons. And now you'll see that it's really nicely spaced out, both vertically between the field and horizontally between the edges of the screen. So this is looking pretty good. We can actually even tap into here, and if your keyboard doesn't show up, you can hit Command K, which will pop up the keyboard. And what we need to do is we want to have a Waves Signer user up because I don't have an account yet, so I need to sign up. So we wanna go ahead and add one more thing at the bottom here and what it's going to be a horizontal stack. Make sure you uppercase the S there. And the first thing in here will be a piece of text that says, maybe let's go ahead and say new to messenger and then write on the rate of it's going to have. We can use a navigation link like we have been using for quite a while now. And basically we're going to say Create Account. And what's the destination can that be? Well, it's going to be a sign up view. So go ahead and give that a run and let's see what that looks like. All right, so down here we have new to messenger create an account so I can go ahead and tap on that. It should be pushing our actual sign-up view. And the reason it's not is because we forgot to embed this whole vertical stack inside of a navigation view. So let's go ahead and do that. So I'm going to cut all of that and embedded inside of a navigation view and give it a run once more. And now when we tap on this, it should push on this other view. So this is looking pretty good. Let's do one more thing and then wrap up the sign-in screen. So we have a button here and when we tap on it, we want to do something. So I'm going to go ahead and call a sign-in function. And in this function we want to validate before we try to sign in to our database that the actual pieces of text entered aren't empty. Now what happens if the user just types in a bunch of spaces? Well, we want to trim out all the whitespace is Ben, check if it's empty because if you press the spacebar in a few times, that still counts as some text. And then of course we want to do that with password as well. And then we can be even smarter about this and add one more check and say make sure that the passwords length is at least greater than or equal to six characters, which is what we will require. Now once we have this are validated, we can actually call a sign-in function on our model. So we haven't actually created this yet. So let's come into our app state model and create this function. So we're going to say sign in and work pieces of information. Do we have well, we have a username and we also have a password, so we can go ahead and pass those in right here. And here we are responsible for trying to sign in. And what we can come back to our sign-in view and do now is we can call that function on our model passing in the actual text. So we can say a model that sign in and this will be username and this will be password. So that is our sign-in screen. In a nutshell, we actually don't need to do anything else here. We do need to hook up the actual API call and whatnot, so we'll wrap it up here in the next part, we'll go ahead and design the sign-up screen, as well as some more of the app state model. The sign-up screen will be very similar in that it will be basically identical, except we'll have to allow the user to enter an email address in as well. So I'll catch you guys in the next part. 11. Sign Up View: All right. We're going to continue where we had left off. So we had finished making our sign-in view here. And actually what we can actually do is cheat a little bit by copying almost every single thing in here and pasting it into our sign-up view. Now our assignment view is going to be pretty darn similar to sign in, in that, that we will actually need just one more field. So we already have e-mail and rather username and password up here and we want to add email. So I'm just going to add a e-mail state. We still want our navigation view up here. I'm going to go ahead and change the title here to create a count. You actually could get rid of create account as well because we could just use a navigation title. Now we have a text field and it's secure fields, so we'll create another text fields. The first thing here will be the email address, and we wanna make sure we bind this to email. Now username again, that's looking pretty good at this is going to be called sign up. And perhaps we'll change the color here to be green for the background. I'm going to rename the sign-in function to be sign up. And we're going to rename this here as well. Then we also want to make sure that email isn't empty. So we'll go ahead and copy and paste that check. And we don't want to cross sign in now, so we'll go ahead and delete that. So for the most part we'll be looking good here. Number four, we give it a run. We actually want to get rid of this wrapping navigation view. And the reason is because the sign-in view actually has a navigation view and we're going to be pushing this detail view on. So we instead want to give it a navigation title. And basically we want to give it a title with a display style. So there is an option in here which you can pass a title with a display mode. So the title is going to be Create Account. This here will be in line. So if you go ahead and give this a run, Let's take a look at what our quicken, quick and useful copy and paste looks like. So we'll go ahead and tap on, sign up and you'll notice that it looks pretty good. The only thing that we want to get rid of actually is this bottom text here. Because if we hit this again, it's just going to keep showing a Create Account and that's not really intuitive or useful for all purposes that were needed using it. So let's go ahead and delete that. Create a count age stack. Make sure you do it in, sign up and not accidentally in, sign in. And let's go ahead and also hook up this sign-up function. So we had added a sign-in function in our model. So here we are also going to stub out a sign up function. And we have three pieces of information we can sign a user up with, and that is their email, their username, as well as their password. We're going to need all three of these and go ahead and create it just like that. Now while we're in here, we actually want to add two more things. Whenever a user signs in or signs up, we want to go ahead and hang onto the current user's email address as well as their username. So we're going to use this thing called App storage. And we can go ahead and creates a property which will be empty string by default. We can actually even call this current username. And you can copy and paste that and call the next one current e-mail. And basically we're hanging onto these. You can use them later on to distinguish if a operation is for someone who is a current user or for someone who is a user that we're chatting with. And we actually need to put these as keys inside of parentheses here with App storage, so we don't get any weird errors. So now that we've got all this stuff situated, I think we're just about at the points where we can start hooking up our database and authentication and all that good stuff. And the last thing that I'll stub out here is a sign-out call. We are going to want to sign out a user. We don't need to actually pass anything in because we'll be able to check which user is currently signed in. However, I do want to go ahead and add a few things here. And now that we're still here, we're going to need to hang on to all of the users conversations. So I'm going to create a published conversations array here. It's just going to be an array of strings. We're also going to want to hang on to the current user, rather other user that we're chatting with. So we could go ahead and do here is let me actually create it right here. We can say var. Other username is going to be empty by default. And what we are chatting with another user, we want to hang onto the messages in the chat here as well. So we can say messages will be an array of message objects. Now we haven't actually created this message object thing here yet. So what we can do is jump into message dot swift and we can define one of these. So it's going to be pretty simple. It'll be a message object. Every message is going to have a string. It's also going to have a type so we can tell if the message was sent or received. And then it's pretty important to also bring in a property called created, which will be the date that the message was created as a string. And the reason we need this is so we can make sure that we are sorting the conversation by the date that the message was received or sent. That way, or messages won't be jumbled. So let's go ahead and stub out some of these other functions here as well. When we search for a user, we would want to search users. So I'm just going to create a function here. We'll say Search users. Now what is this actually going to take in? This will take in a query text. And with a completion handler, basically we are going to call back and return a array of users that were found. So pretty simple. We'll implement this in the upcoming parts for conversations. This is pretty easy as well. We want to get conversations for the current user. And now only do we want to get them, but we want to add a listen. So we want to listen for conversations. And what that means is basically if somebody sends us a new message, we want to go ahead and so that conversation in real time and not have the user close the app and reopen it. So sending messages and getting chat, that's pretty simple as well. What we want to do is we want to observe a chat and we already have the other users, so we don't need to actually do anything there. And let's see what else do we need to do? We want to be able to send a message and we should take in the text that we are trying to send. Pretty simple. And here what we'll go ahead and do is we will also say creates conversation if needed. And what this will do is this will actually create a conversation objects in our database. So when you start a chat with somebody, there's two versions of that chat. One that the starting party has and the receiving party. So what that lets us do is if I go ahead and delete the conversation, it doesn't delete from the other person to app. So this is basically what we're going to need in this part. We'll wrap it up right here in the upcoming section and part we'll go ahead and do each of these one-by-one and set up our backend with Firebase. So thanks for sticking around. I'll see you guys in the next part. 12. Set Up Firebase: Welcome back guys. In this part we're going to pick up where we left off and we're going to actually set up Firebase, which will serve as our backend. And I've got a browser opened up here to Firebase.com. You can head on over and create a free Google account or sign in if you have one already. Before we jump there and set some stuff up, we need to add a little bit of code in our Messenger app file. So what we wanna do in here is we're going to need to configure Firebase once we bring in the library. But we need to do that in something called AppDelegate. So go ahead and create a class called app delegates. You want to make sure that it inherits from NSObject and it should also conform to UI application delegates. Now in here we're going to want one function which is called did finish launching with launch options, go ahead and do a return true in here. And now what we basically want to do is we want to tell Swift UI to go ahead and call this function when the app finishes launching. And the way we can do that is by using another property wrapper. We want to use the UI application delegate adapter, which is right there. We're going to let it know which file rather which class serves as our delegate, which will be AppDelegate dot self. And this here will go ahead and call application delegate of type app delegate. So if you go ahead and add that, if you actually just put a printed here to validate that it is in fact being called. You can just go ahead and say did a launch in our console down here, once our application launches, we should see did launch being printed in here. So if you take a look at the very first line we see did launch looking great. So awesome. We can come back to our Firebase console here and we can click on this big ad project plus. And the first thing that it wants from us is a name. So I'm going to say Messenger, and we're building this in Swift UI, so I'm going to call it messengers Swift UI. Go ahead and continue. It's going to ask us if we want to hook up a Analytics accounts. We're going to pretend like we read all of this. It just gives you some basic info that we can select our default account every accountable how villain and simply continue. Now while that's doing its thing back in our project here, but we can do is come to this top level folder, which is blue here. And you want to copy this bundle identifier string. And basically this is what we need to tell Firebase to link up to our app. So once you've got that, copied it, come back to your browser and let this finish up. It should not take too long beak and go ahead and continue. And now that you have a project, you'll need to create an iOS app in the middle here, happening on iOS. The first is an asked for is that bundle ID. We had just copied it. Go ahead and give it a name. You can call it whatever you want. I'll call it iOS app. And this part is pretty important. It's going to give you a services file. So hit those big blue Download button and it'll go ahead and download those Google's services, P list file. Come back to your project and drag that file in, and I'm going to drop it under resources, go ahead and bring it in. And basically this file has a lot of information of how to connect to your Firebase back-end. So by bringing this in, this allows or apps to communicate with the database and store it and all that. Goodness. So what we can then go ahead and do is just hit Next, Next, Next here it's going to guide us through how to bring in the frameworks. We'll do that in just a moment before we continue further here. Back in our code, we do need to enable a few things. We want to enable email and password authentication. So on the left panel you want to click authentication and hit this big Get Started button. And here are all the ways that Firebase supports to sign a user up in. And so we're just gonna do email and password today. Open it up and hit this on button and go ahead and hit Save, and you'll be in business. And the last thing you want to configure in here is Firestore. So go into fire store, which is a database you can click on Create database. We're going to start with a test mode, since it's easier to work with. And don't worry, this is all free of cost. Go ahead and continue. It's going to ask you which region you want to put your database in a defaulted mind to US Central, so we'll stick with that. If you want to change it for any reason, make sure you change it there because you actually do not have the ability to change it thereafter. While this does its thing. What we'll also do is back here we can actually close our Xcode project. And the reason we want to do that is because we need to bring in the dependency of Firebase. So go ahead and open up terminal. If you don't know a cocoa pods, are there a way to manage dependencies in your project? If you have them installed already, you'll be good to go. Otherwise you can do a quick Google search for installing them, but we need to cd into our project folder and run a pod init. And that'll go ahead and create a pod file for us so we can go ahead and open up that pod file. And we want to bring in the Firebase framework into our project. And there are three things we want to bring it in. The first thing that we want to bring in is.. firebase core, which will give us the core functionality, hence the name. The next thing we're going to bring in is.. firebase off with. So give us all the authentication related functionality. And the last thing will be Firebase, Firestore, which will give us access to the database. So once you have all of that entered in there, go into your terminal and you can run pod install, and this will start installing all of those frameworks. Now installation can take anywhere between 10 seconds and a minute, so just be patient with it. In the meantime, let's take a look at our dashboard and see how that's doing. All right, so it looks like it has finished up here. We don't have any information in our database yet like expected. We do want to open up the rules tab up here. And basically these are the rules that allow you to read and write to your database. You can see here that it has an if condition and it's going to allow us to write up until May 17th. And what this will allow us to do is basically be secure in our test environment. Now, we can actually just go ahead and remove this if and the colon. And it can go ahead and just save this. And basically what'll happen now is anybody in the public and read and write to the database, if they have the URL, it is not secure for production or to release to the app store. But since we're just learning today, that's okay. So we'll go back to our data and we're all set up in the dashboard here. Now let's take a look at our dependencies here. Looks like they have installed. Once you see everything has installed, you can open up your project folder. And instead of opening up the Xcode project, you want to open up VSCO workspace. Now, the workspace include all of the dependencies. So let's go ahead and select a simulator. We'll stick with our 12 Pro Max here and go ahead and hit the Play button. Now the first time you try to run your application after installing all the dependencies. And not only will you hear your computer fan get nice and loud, but it might take a few seconds for everything to build. So let's just bear with it here. And let's make sure everything is valid. And then we'll wrap up this part and in the next few parts will actually use each of these things from firebase, authentication and Firestore and all that stuff too, basically fill out the rest of our app state model. All of this stuff that we had stubbed out. So go ahead and bear with it while it builds your project. This also varies in terms of time based on what type of computer you're using. I've got a 2020 MacBook here myself, so hopefully it's fairly quick. So just bear with it just a few moments. And actually while it's doing its thing, what you could also do here is we can go ahead and start importing the Firebase related things that we need. So we're going to want a firebase off, and we're also going to want Firebase fire store. So we can say Firebase fire store just like that. And we'll also need to do is import in our messenger apps and come into here and we end here, we want to say go ahead and import just Firebase. And in that app delegate function where we had the prints, we can say Firebase app dot configure. And this is why we needed to add this AppDelegate because basically we want to tell Firebase to connect to our app. So Firebase is the backend as soon as the app is done launching. So now that we have all that setup and we've imported into our app state model, we can actually start using some of this stuff. So we're just going to create a reference here to our database and our off. So we can say database is going to be fire store, dot, Firestore. And let's see if autocomplete helps us out there it goes. And same thing for off, we can say auth is going to be firebase off. You can actually shorthand it by just saying auth dot off. And once this is done, we'll make sure our app is launching and then we'll start to leverage these pieces in the upcoming parts. So almost there was blue line has gotten me on the edge of my seat. Bear with us, making sure we don't have anything breaking and we'll be in good shape. We're at 2600 of 2740. And you might be wondering, why is it taking so long to build this time? The reason is, is compiling all of the code that we brought in via the CocoaPods. So we have a bunch of more code now that we want to include in our application. So execute here, It's taking its time to actually go ahead and compile all of it. In the meantime, we can move this Google services to trash, since when we brought it into our project that actually did copy it. So just like that, bear with it. And there it goes. It has successfully build awesome, awesome. And it has started our application here. Let's make sure nothing is crashing and we'll be in good shape. All right, there is our app beautiful. So we've brought Firebase and we've got everything in place to hook it up and wrap up this awesome chat app. So thanks for sticking around and thanks for bearing with Xcode. I'll see you guys in the next part. 13. Sign In, Sign Up, Sign Out: All right, welcome back guys. In the last part we brought in the Firebase dependencies. And in this part, we are going to actually start using some of that to actually get some functionality working. So the first thing which is a natural progression is to allow a user to create an account. So we're going to do just that. So let's jump into our app state model. And we had created a sign-up function down here already. And what we could actually do is use Firebase auth, which we brought in and use to say let off to create an account. Now when we create an account, we want to do two things. Not only do we want to create an account, we also want to insert username into database. It's important that we do both because our database is going to also hold our information regarding chats and messages. So we're gonna go ahead and say auth dot create. And we want to create a user with an email, which is the email address being passed in as a parameter. We also want to pass in a password and then we're going to have a completion in here with a results and a error. Now we want to validate the results doesn't equal nil. And we also want to validate the error is in fact nil. And if both of those cases are true, we know that we have successfully created an account and now we want to actually insert the user into the database. So let's go ahead and say data. And the data that we want to go ahead and insert is going to be a dictionary with a email of type, e-mail and a username. And as username is going to be the user name. So now we can go ahead and do is we can say self, the database. We went to get a collection of users. And on this collection of users, we want to create a new document entry for a single user and the documents key is going to be the username. So we're gonna say username dot setData. And we want to set a document data with the completion handler, so we'll say data, and if it succeeds, we shouldn't have an error. So we're going to validate that the error does in fact equal to nil, meaning it doesn't actually have an error. And then we know we have successfully created our data here and we can finish up. Now what is finishing up actually look like, well, we technically want to just remove this sign in and we wanna go to conversations. And you guys might recall that we already have something on here, which is called showing sign in. So what I can actually go ahead and do is say self.age showing sign-in equals false. And by doing that, it should go ahead and update our UI. Now we want to go ahead and make sure we do that on the main thread because it is going to update our user interface. And the other thing we wanna do is say self question mark there. So we don't leak memory for those of you familiar with that concept. If you're not familiar, just go ahead and add that and weak self. And you'll make sure that any errors that might occur don't actually appear. So now that we have that, go ahead and Command B to make sure everything is building. And let's see if we can sign up a user. I also still have Firebase opened up here and we're going to jump into authentication. We don't have any users yet. Once we sign somebody up, we should see that they appear here. And actually one other thing I forgot to do is once we have signed somebody up, we want to hang on to their current username and current e-mail that we defined up above previously. So we can also do here is say self dot. Current username is going to be username and self-doubt current email is going to be email it just like that. Go ahead and hit Command R to a build and run. And let's see if all of this works. So we're going to launch up on the simulator here. We're going to hit Create Account and I'm going to create a account. So the email will be hello at iOS academy dot io will say John. So one issue I see off the bat already here before we continue Is it is auto capitalizing and trying to spell check as we enter. So that's now what we want because we want things to be lowercase and we don't want things to be auto corrected. So we're going to jump into where our fields are in our sign-up view. And I'm going to say for the auto capitalisation, we're going to say none. And we're also going to say disable auto correct will be true. And I'm going to tackle these onto these field here. And I'm also going to do that as well in the sign-up view as well. Since we have fields in here and we don't want to have the same issues here as well, so we'll tak one on there. Let's think that was a text we'll tack on onto this as well as the secure fields. Go ahead and give your app or run once more. Let's make sure that things are not being auto capitalised. Alright, so here I'm going to start typing in my email. So hello, add iOS academy dot io. We're going to type in a username. Maybe we'll give it a username of. Let's go with Matt. And we'll go ahead and make it uppercase. And then we'll also go ahead and give a password here of perhaps password just to make sure we don't forget it. And when I tap on there, so we expect to happen is it should go to the database, it should create the user and then log in. So let's see, nothing is actually happening here. So let's see what's going on. So if I come to my Firebase Dashboard, nothing happened here. So let's see what's happening when that sign-up function is in fact called. So we're gonna jump into the sign-up view. And let's go down to that function. Let's see what's going on. So we're not actually calling signup here, which makes sense. We're gonna say a model that sign up with the e-mail, username and password. And if you recall, model is our environment object for App State Model. So go ahead and give it a run once more. And now we should actually be creating an account. So we'll come to here, we're going to hit Create Account. I'm going to type in a email, so we'll say hello it iOS academy. We're going to go ahead and give a username for Matt and or password will be password. We'll go ahead and tap on this and about a second and you should see the ETL sinus in. Now we have this dummy data here that will get rid of, but it looks like it created the accountant scientists in the two ways you can check that is, first and foremost, by going to your actual database and reloading authentication here and you'll see the email address. And the other thing you can check is in Firestore here we should have a collection of users and there should be a username with our document username here. And the data for it should be the email and the username we gave. So that's looking pretty darn good. Now there is one problem we need to address. And that problem is when I run this application again, what the heck happened? We're back on Sign-in. Didn't we just sign up soon and that leave us signed in. And the answer to that is yes, it should. So what we want to go ahead and do is we're going to open up our app state model. And in here, we hard code to show the showing sign-in to true. And the problem is, we don't necessarily want this to be true all the time. What we want this to be is once the APA, this model is created, we want this to be whether or not we are currently signed in or not. And the way we can actually do that is by overriding or implementing the initializer here. And I can say self.age showing sign-in. And we can go ahead and save this is if auth dot off dot current user equals nil. So if the current user is nil, aka nobody assigned in, show the sign-in UI, otherwise, don't show it. So now if you go ahead and give this a run, what you'll notice is when we launched the app will stay on conversations, which is looking pretty good. So that's exactly what we want. When I close the app and reopen it, it will stay on our logged in state because we have a user. Now let's go ahead and hook up, sign out and sign in y, we are at it. So sign out is actually very, very simple. If we come down to the sign-up function, all that we want to do is say do, try to sign out. If an error occurs, we'll go ahead and catch that error and we'll print it out. And once we successfully sign out, we want to say showing sign in. We'll turn to true. So go ahead and give this a run. I believe we already hooked up that sign out function. There's, when I tap on this, we'll notice is well actually it looks like we may be DID and hook it up. So let's jump back to our conversation view. And let's see we call sign out here and we should be saying here, model dot sign out. So try that one more time. And when we tap on Sign out, what we expect to see is the sign-in form appears just like that. And also when I close and reopen the app, it should show a sign in again like that. So finally we're going to hook up signing in. So this is pretty simple as well. We allow the user to sign in with a username and password. However, we need to actually tell Firebase the email that's associated with the username. So what we first need to do is get email from database and then tried to sign in. So let's go ahead and do this. So we're gonna say database, that collection of users. And we're gonna go ahead and we are going to say from that, we want to get the document for the particular user that we want to get information about. So we're going to say get the document for the username. And if you type gets on here, you can actually get that document information. And this will give us a snapshot back to us with a error. Now there's snapshot can be used to actually get data out. So we can say the email is going to be the snapshot dot data. And from there we can get the email as a string. Otherwise, something went wrong. And we also want to make sure that the error is in fact nil. Now how did I know this is going to be e-mail? Well, if you take a look at our sign-up function, the data that we are setting here is e-mail ID, username. So that's basically what we will be reading out. And here you'll see that it'll actually give me an error. And the reason for that is because this here might be optional. So what we need to do is add a question mark right there and that error will go away. So now that we have the e-mail address, we went to actually try to attempt to sign in. Now, we are going to capture a weak self here so we don't run into a memory leak. So let's go ahead and do that right here we'll say brackets weak self. And here we're gonna go ahead and say self dot auth. We're going to say try to sign in with a email and a password. So this one right here will pass an e-mail. We'll pass a password. And completion just like sign up is going to have their results and error. We're going to validate that the error is in fact a nil, and that results is not nil. Otherwise we're going to return. And then down in here we're gonna do the exact same thing that we're doing in sign up. So we're going to hang on TV, email and username on the main thread will say self that e-mail, current user email is going to equal e-mail. And I'm gonna say self.age, current username is going to equal username. And then finally, self.age, showing sign in terms to false. And this will allow us to successfully sign in. So let's go back to our sign-in view and make sure we're actually calling that model function. Alright, we definitely are. So let's try to sign in now. So the username I used was Matt and the password was password. We'll go ahead and click on that and boom, we are signed in. So we've got authentication fully working. And what we could actually do here is we could actually sign in on the, or sign up, I should say, on another simulator. And that's kinda why I've had both of these open since we are going to build a real-time chat app. It's pretty nice and pretty cool to see this in action on two different devices side by side. So I've went ahead and selected the 12 Pro simulator and just let it build and you should see it pop up here in just a moment. I've got this one setup and light mode and this one a dark mode, just so we can see the visual difference. We don't have an account since we're new here, so we're gonna go ahead and sign one up. Let's go ahead and save the e-mail will be, I don't know, Jill at gmail.com. The name is going to be Jillian. And the password once again will be password. Go ahead and hit Sign up and boom, we are logged in and in the next part we'll continue and talk about searching for users. So Matt over here can chat with Jill. So this was taken around. I'll see you in the next part. 14. Search Users: Welcome back guys to the next part and the last part we got sign in, sign out and sign up working. And in this part we're going to search, searching for users. So Jill over here, you can search for Matt and vice versa. So we already have the search views setup here. Who we wanna do is we wanna take the thing that was typed into username here. And when we hit Search, we want to search for something that has that text. So it's pretty simple actually, if we jump back to our app state model, we already have a function here. And this function, if I can find it up here, Search users with a query tags and we give back a collection of usernames. So the way that we want to actually go about doing this is we are going to say database and go ahead and find the collection for users. And we want to get all of the documents. And basically every document in here has a ID which is the same as a username. So we can see here is we got a snapshot and a error. And here we can go ahead and say let usernames. And we can go ahead and shorthand this by saying snapshots, dot documents, compact map and we care about the documents, document ID dot lowercased. You can actually even get rid of the lowercase. It's actually not that important. But once we have that and we can validate that the error in fact is nil. We can go ahead and return here. Now if something goes wrong and the completion we're going to say Go ahead and pass back the empty array. Otherwise we wouldn't pass back usernames. But what do we actually want to pass back? Because this is going to give us all our username. So we actually want to filter our usernames to include stuff that is actually prefixed with what we are trying to get. So here we've got username. So what I can do is be cleaner about this and say let's filtered. Filtered is going to be our usernames dot filter. And I'm going to say that the username dollars zero dot, lowercase dot has prefix. And what we want to use here is our query texts, which I believe is what we called it. We called the dip query texts as a parameter dot lowercase. And the reason we're lowercase in here is because it is case-sensitive. So our comparison needs to be checking a lowercase a versus a lowercase a instead of a capital which wouldn't work. And once you have filter, we can go ahead and return it. So pretty darn simple. Now back in our search view, what we need to do is actually call this function with a button is tapped. So our button is right here called search. So what we're going to do is we want to first make sure that the text that we want to pass in is not empty, which is our text in our field. And we want to make sure that we're not searching for whitespaces either, so we'll trim those outs. And once the user hits this, we can go ahead and say model dot search. Now we need to actually get access to that model so we can actually leverage the environment object. Again, just like we've been doing in other views, we can say it's a model of a type app state model. And here we can go ahead and say model. And we want to go ahead and search for users with the given text. And here we'll basically get usernames back. And what we want to go ahead and do is here we had hard-coded some usernames for search results. Instead of doing that, we want to make this a States. And by default, we're not going to have any search results because that would just be silly. So this is going to be an array of strings. And once we have them back here, we can go ahead and say self dot usernames is usernames. And that'll basically trigger our actual results here to show up. So let's go ahead and hit Command R and make sure that we can in fact search for users. So we'll go ahead and it looks like it's running on this device and I want the other ones, so let me change it to a 12 Pro Max here. We'll go ahead and give this a run. And it doesn't bear with our Xcode building here, nice and slow. And we should see it over here in just a moment. And what we also want to do is when we tap on a user once we have found them, we need to either enter in a previous chat we've had with them or we want to create a new chat. So we'll actually hook that up as well since we're doing pretty good on time. All right, so in maths account here, if I'm not mistaken, I am going to search for the username to the left here, which I believe was Jillian. So I'll search for GIL and boom, there is Jillian. I can tap on Jillian and it'll go ahead and open up this dummy data with jelly. And now we want to get rid of this mock list of conversations because that's not all too helpful so, well, we actually want to do is now that we can search for users, we want to go ahead and do two things. The first thing that we want to do is actually update our conversation list here. So when our conversations loads, at the moment, you can see that we're using this list of usernames and we don't actually want to do this. And the reason is because our app model is going to hang on to our conversations. So we're gonna say do a for-loop over model.py conversations, which is empty by default. Now, whenever this view appears, we want to actually go and fetch conversations. So we're going to add another modifier for on up here. And what we can say is first we want to make sure that the user is in fact signed in. And the way we can do that is by saying model dot auth. And we're going to say make sure the current user doesn't equal nil. Because if this appears and then we show sign and we don't want to try to get conversations if there is no user. The reason this conversation view appears is because this is where the sign in view is presented on top of it. And it's actually this code right here, two lines. So now that we have this, we can say model, get conversations. And notice this doesn't return anything and that's actually by design. And the reason is when we call it get conversations, we want to actually add a listener to get conversations because every time somebody new messages us, we want to make sure updates in real time. So the way we can add a listener is by saying database. We first want to add a path to listen to. So we're going to listen to a collection of users. Now off of that, we want to listen to the current users chats. So we're gonna say document for username should be a collection on there as well. And let's see why this isn't cooperating. Let's try that one more time. We'll say document. For our current username is what I'm looking for, a current username, current username. And off of this, we want to get another collection of chats. And this is what we want to observe. So we can go ahead and say add snapshot listener. And the one that you want does not include the metadata. So we're gonna go ahead and do that. And every time a new chat is added to the database at this path, this block will get called. And what we can do with this is we can actually update the conversations that are up here, this published conversations. And that'll basically update our view in real time. Now this whole thing returns a listener. So we're gonna say conversation listener equals, and we want to hang on to this. So I'm going to create this conversation listener up here. We can go ahead and say this is a var and it's going to be a listener. Registration, optional just like that, so we can hang on to the listener. And then finally in here we want to actually get all of the usernames out whom which we are chatting. So Let's go ahead and say guard. Let username is going to be snapshot dot documents. And once again, we are going to compact map this and we're gonna say $1 dot document ID. And we also want to make sure that the error is in fact a nil. And once we go ahead and confirm both of those things, we can actually assign this to our conversations, our ray, we want to make sure we hang on to week self because we don't want to cause a memory leak, which is now good and makes your app crash. We're gonna say conversations is usernames that we are having conversations with. Now this is going to update the UI. It's a published property. So we need to do this on the main thread. So that's looking pretty good. So go ahead and give this a run. And what you'll notice is we won't see these mock conversation list here anymore, at least on the right device. So looking pretty good. So in the next video we'll talk about how to actually create a conversation, as well as we'll see how we do on tile will also go ahead and start sending some messages. So thanks for sticking around. I will see you guys in the next part. 15. Observing Chat : What's going on, guys, welcome back to the next part. In this part we're going to continue where we left off. We can finally search for users. But now when we tap on a user, we want to actually go into a chat and start listening to it. So it's pretty similar to what we did already for conversations. We're going to start by jumping into the chat view. So the chat view gets created with a other username property here. So we need to do a few things with this guy. So the first thing I'm gonna go ahead and do is on this chart view, we want to first actually get a reference to our model. So we're gonna say adds environment object once more, just like we've done in all of our other views. We're going to say model is going to be our app state model looking pretty good. The next thing that we want to do is instead of having these hard-coded chat rows, we want to actually do is do a for each over our model data messages. And basically here we'll get a message in. And we want to also say that the ID will be self just like that. And we want to show a chart row for each of these positions in our messages. Now, we have an error here because it's yelling at us that message isn't hashable. So to fix this, it's pretty simple. Actually go to your message and just make your message hashable. And that'll actually go ahead and resolve that error. If you go back to your chat view, you should see that this is nice and happy. Now, let's go ahead and hit Command B, and hopefully that error goes away. Are some looking good. So now what it's gonna do is any of the messages that are in the model.py messages array it'll show here so we won't have, you know, strange dummy data anymore. So once our actual view appears for the chatter view, we wouldn't go ahead and start observing the conversation that we're in. So we're gonna say on, up here, we are first going to say model.py, other username will equal other username. That's how we can hang on to it globally. And then we're gonna say model, and then we want to go ahead and get the actual messages. I believe we called it observed chat. And now we want to actually have observed chat that function listen to the chat messages in a given conversation. So we're going to copy and paste this conversation, the listener. And I'm going to call it a chat listener. And well, we can actually do is basically copy and paste this thing that we did for conversations and do the same thing for observed chat. Just make sure to make this a chat listener. And we not only do we want to change that, but we want to observe users dot current user chats. And then from chats, we want to actually observe v document for the child we care about. So we care about a chat with the other username. And you might be wondering where I'm getting this. It's that global property that we had set all the way up here. And now that we've set that there. Save from that document, we want to get the collection of messages. And this is where we're going to add a snapshot listener. And now instead of getting usernames out, we wanted to message objects out. So we can say that messages is going to be documents. And from the document we want to get the document data. Now this is going to return an array of dictionaries. So we can actually do is change this messages and we can say that this is objects. And down here we want to create a message model out of those objects. So we're gonna say objects that compact MAP. And what are we going to create? We're going to return a message model that we created earlier on. It's going to need to take in text, a type, as well as a created date as a string. So what thing that we might want to do is instead of having this creative be a string, we can save it in the database is a string. But let's be smarter about this and make this a datatype since we can sort those more easily. So this is pretty easy. We can say dollar 0, which is e to message. We want to see the text and convert it to a string. If you fail to go ahead and make it empty. For a type, we're gonna go ahead and say if the sender, which will be something we save in the database as a string equals the current user. This message was sent. Otherwise we know that this message was received. And then finally here we can go ahead and do is we can say date formatter. Go ahead and give me a date from a string, and this string is going to be our dollar is 0, the created as a string. Otherwise, if that's nil, we'll go ahead and give it an empty string. So by doing that, what we're doing here is we're taking this dictionary and we're getting the text sender and the created pieces out and recasting them to the appropriate expected types as well as for our actual date here, we're creating this date and this is yelling at us because if it fails to create a date, we need to give it a current date. And for this one in particular here we have sent and received. So let's see, I believe I called it sent and something else. We call that sent and received. Let's make sure we spelled that correctly and see what's going on here. So we're gonna save that as a string equals current e-mail. This should be current username. And see what else is going on here. Let's see what this error is actually yelling at us about. So it's saying can I call value of non-function type string array? So let's see what is happening here. So we are using parentheses when we should be using square brackets. So just be careful about that. They'll look very similar. And what do we have these messages out? We can go ahead and say self.name messages, messages. And by doing that, it will actually update our messages collection, giving us the particular the particular up-to-date, the UI that we expect, just adding a self here because that also needs to be solved optional. And that's basically it to observe a conversation. So if you go ahead and run the app now, one thing that you'll notice is when we go in search for a user and it brings up their conversation for the first time. You'll notice that it'll be empty as expected. So when I open up, Jillian, it looks like something happened there. A little weird. So let me sign in again. So we'll say Matt, password. Let's go ahead and sign in. We'll search for Jillian here. And let's see, looks like it pushes on search again, and then it pushes on Jillian against whom interests. And let's see what's going on. I haven't feeling of this is related to the dispatcher delay that we added, not being delayed fast enough. So let's go back to our conversation view and let's see what we are doing here. So whenever we tap on this, we go ahead and do a dismissal. So what we'll need to do here is we are going to save as other username in this chat. So let me actually set the other username like so the show chat will be set after 1 second. So let's see what is going on. I believe what's actually going on is if you take a look at where we are showing the search view in the Toolbar Options, see if I can find it. All right, search view here, we are using a, we're not using an active here. So one thing that you want to do is add in is active. And what we could do here is we could use Show chat up here. And we can move this in here that way. If the chat is shown, we know that we cannot show this guy. So I'm gonna go ahead and say what a recall it, show chat. Now this needs to be the opposite because this is telling us if it's active or not. So we're gonna go ahead and actually move those back here. And what I'll actually do is let's create a, another one here and we'll say show search, which will be now false. And this one here it will be so search as well. We'll say show search and let me go ahead and hang onto search up here as a state. So we'll say var shows search is going to be false by default. So go ahead and give this a run. Let's see if that fixes up our search issue. Because we definitely don't wanna go into search again after picking something. So I'll go into here and search for a user. I'll tap on Jillian. And it looks like it now opens up or Gillian appropriately and search doesn't open up again. Beautiful, so cool, so looking pretty good. The next thing we wanna do is be able to send a message. So right now we'll go to our database. You'll notice something a little interesting if I reload this page. We should have created under both users, mat and Gillian, a collection for chats as well as. A collection for chats under Gillian. So we haven't actually done that. And the reason that's not actually happening is because, well obviously we haven't written the code. But more specifically, if we go back to our chat view, all recalling on a pier is observed chat. So what we wanna do in here also, if you remember, we created a function down here and it is create conversation if needed. So I'm just going to say create conversation. So let's go ahead and say Create conversation here. And what we want to go ahead and do is create a conversation entry for both Jillian as well as Matt. So both users. So we're gonna go ahead and do that by saying database. We're going to get a collection of users. We want to get the document for the current username. First. We're going to get chats. And let's see, we want that to be a collection called chats, which is pretty simple. And let's go ahead and line break all of this. We want to create a document for the other user. So we can say here other username. So current user has a chat with other user. We're just gonna say setData on it. It doesn't really matter what you put in here. You can see created is true. And we want to do the same thing for the other username, but flipping it. So for other username, we want to create a chat with current username. So that way will actually, if we go into a chat, we'll create it and let me go ahead and run this on both devices. And now you'll start to see that things are starting to look a little more real time. And since this is getting a little longer of a part, we'll probably wrap it up after we validate this is working and the next one, the next part we'll take a look at sending messages and actually seeing them populate in real time. So bear with me here. While this compiles, it always gets a little slower when switching simulators. So if you see that happening, it is expected. So that one's running over there. Let's also go ahead and run it on our other simulator on the right-hand side here. So you'll notice this one here just shows conversations and this one here, I should show that as well. Bear with executed while it builds the project. And we're actually almost done. We'll have to just send messages and we'll be in good shape and bring in some of those photo images. So it looks a lot nicer and we'll be in good shape. So let's go ahead on this rate simulator. Once it finishes launching, we'll go ahead and search for Jillian 0 we expect to happen is on the left-hand side here we expect to see mat pop up because it should be observing conversations. And bear with this here looks like had an issue. Launching sometimes launching is just a little flaky. So go ahead and give that a launch again. And you'll see that it should launch. Bear with a simulator here. Sometimes you need to actually close and reopen it, which is slightly frustrating, but not much we can do. All right. Bear with it. All right, there it goes. Cool. So we're gonna go ahead and hit search. I'm going to search for Jillian. And when I tap on Jillian, you'll notice that it created it opened up the chapter. Look at the left-hand side here we have Matt matches popped up randomly. I will not randomly as expected, so we can both tap into the respective conversation. And this is the part you've all been waiting for sending messages. So I'll wrap it up here. Thanks for your patients and sticking around. I'll see you guys in the next part where we can finally start sending some messages. 16. Sending Messages: What's going on, guys, welcome back to the next part. In last par, we actually got conversations showing up in real time on both of these devices here. And in this part we're going to actually start to send messages. We already have the field setup in the center by n. All that's left to do is actually insert the message into the database. So let's get to it. So we want to first start by going to our Send button view. Make sure that we're calling our model. That's send a message which were not. So when send a message, just have to revalidate the text isn't empty. And I'm also going to trim out the whitespaces in case someone tries to pull a fast one and pass in some spaces and try to send it. And then we'll go ahead and say model dot send a message. Now for our model reference, we wanna go ahead and get it here. So we're gonna say environment, objects, VAR model is going to be our app state model. And we can go ahead here and say a model that send message passing in the text. Now the other important thing that you wanna do here is now that we've sent the message, we want to go ahead and clear out that text. So I'm gonna go ahead and just set the text to an empty string. And what that'll do is that'll clear our text field here. So let's go and implement the send message function. It's pretty simple. So we're already observing that we're sending a message, so we want to insert into the database to messages. And yes, you heard that right to messages. Now you might be wondering why two, and the reason we want to insert two is because we have two different copies of the conversation, one for the receiver and one for us. And that allows us to delete the conversation on our app and the other user not having a deleted on theirs. So we can actually do here is I can grab in this conversation, this first one. So any users, current user chat, we want to go ahead and get the document for the other username and then messages, which is going to be a collection. We want to create a new document so we can go ahead and create a document. And what do we pass in here? Well, we're going to pass in a new message ID. So we're gonna say new message ID is going to be UUID dot string. And this is basically just a random new identifier. We can go ahead and pass that in. And then more important, we want to set some data, which is going to be our message data. And it's important that you use the same data for both of the calls we make and that the keys in here are accurate. So texts should be taxed. And then we want a sender. Sender should always be current username. And then finally we want a created. And before you run this, make sure that this text center and created matches this. So text. Center as well as created, It's really important that the match otherwise reading your conversations won't work. Now what is created is going to be, this is pretty simple. It's going to be a date formatter. And we're going to try to get a string from the current date if we're not able to pass in an empty string. And now that we've got this data here, we can actually go ahead. And actually it looks like we don't even need this part of the warning. We can set the data on this and we want to go ahead and copy and paste this here. And what we'll do is we'll reverse the username. So this one here will be other username and this one will be current username. So what we're doing is set this conversation message. It's the new message ID still, we've got the data here, it's under the message is collection. And we should have already created the conversation for both of those users. And that's actually all there is to it. Now, one thing that we haven't done yet that will take a look at in a moment. Let me ask you run the app and take a look at it. But the one thing we haven't done yet for our messages, we need to make sure we sort these to be most recent to oldest. So it looks correct. So if I come into here and if I open up this as if I'm sending a new message, we're gonna go ahead and type in, Hey, how is it going? Maybe we can toss in an emoji for good measure, put a heart there, go ahead and hit this. And what you'll see is like that. So we start to see something shows up here. It isn't using our message text, but we are sending a message. So the reason we're seeing hello world is because if we go back to our chat view in our for each, we don't want to send in HelloWorld here we want to send in a message dot text. And that's what we are doing, the for loop over. So let's see message dot txt. And this here will be message dot dtype. That way, the actual rendering view will know what tags to put in there as well as it should be in the text sent configuration or received. So there is, our cent message is looking pretty awesome. Let's go ahead and run this on the other simulator as well to see if we can also send and receive messages and make sure it looks correct on both sides of the conversation. So let us compile and while it actually does it stay, let's talk about sorting here. So we've got messages is pretty easy to sort. We're going to say sorted by. And we're going to have a first, second in here. And we're basically going to say first dot created, which is a date less than. And here we're going to say second dot created. And this way it will make sort of sorted by date. But if will come into here we can see, look at that. We've got the other side of the message even showing up already. So let's go ahead and open up of this and this one on the right-hand side as well. And I'll say hello. It's nice to hear from you. And when I go ahead and tap this, we expect it to show up over here, but look at that because our sorting is not in place, we're seeing it pop up above it. Even though this message is newer than this one that we've sent on the right-hand side. So let's go ahead and run this one more time on both simulators and make sure that we are sorting the message is correctly. It's pretty critical when you're chatting with somebody. So it looks like we did it backwards. We want this to actually be greater than, Let's try that one more time. And then we'll run it on this right simulator as well to make sure that we in fact are sorting correctly. So we're saying, Hey, how's it going and then we're passing that in. So let's see what's going on. So this should be sorting by created. What I'm going to go ahead and do is, Let's actually also print out the message is because something seems to be a little wonky. Because apparently doing both of these is still giving us the reversed order. I see. Okay, So what we actually want to do is when we insert into the database, we do want to also reverse. And the reason we do that is because for this exact issue of we wanna go ahead and first reverse the actual stuff that we create from the objects. And then we want to do a sort. So let's go ahead and try that and make sure it's ordered correctly. Our database actually should get all of the chat messages inserted in the correct order. So if I go ahead and open up mats, we should have chats and then we should have a chat with Jillian. And there are our messages in here. So if I tap on messages, we should see two messages with our random IDs. Let's come back here and tap into the conversation and it is now ordered. Let's see. Is it ordered? So let's see. Yeah, this is definitely the order because that's the message we had said. Let me send one more over here. We'll go ahead and say sent by us just to make sure that it is proper order. Looks like this is not maintaining order at this point. Let me try running that one more time. I'll take a look at how to clean up this sorting. All right, let's run that once more. We're going to tap into the conversation. So one thing that could potentially be causing the incorrect ordering is the fact that we are inserting the current date, which should be correct. So we're inserting the current date for created. And let's see what we're doing up here. Here what we're doing is we're saying date formatter get the date from the created as a string. So what I can actually go ahead and do here is instead of coalescing it's to a empty string. But we want to go ahead and do. And instead of doing that, we can say if we're not able to create a date, something went wrong. So we can say guard, let date is going to be this. Otherwise we can go ahead and return nil here. So we'll say created ads and we're missing a, we're missing a string there. And if something goes wrong, we're gonna go ahead and return date. We can use date right down here. So let's see if this is looking correct. We're saying date formatter. Give me the date from this particular string. And if you're not able to create it, go ahead and put in a date in there. So let's see what the problem down here now is. So let's go ahead and get rid of that. Let's do one thing at a time. All right, so in this case there'll be returning, I am going to explicitly save this as a array of messages to get our warnings to go away. Sometimes Swift is a little tricky with when you're trying to have it, you know, figure out the particular types. And now it looks like the order is still not correct. We are reversing here. So let me drop the reverse. And let's actually be smarter about this and just look into our database and let's make sure we have a creative. If you take a look at that, our actual created string is empty. And that's actually what's causing the problem. So this is probably a good practice for you guys to take a look at as to what's going on here. So what we're gonna do is here we want to get a string from the current date and we want to put that into created. And for whatever reason, if that's the thing causing an issue for us. So what we'll go ahead into is, Let's go ahead and send a new message. So we have a date formatter. Our saying give us a string from a particular date. So I'm gonna say this is a date string. And we are going to save this is let date string and this should be giving us a date string and the fact that it's not as causing the issue here. So let's take a look as to what is going on. So we're looking at Gillian at the moment. So what I'm going to go ahead and do is we're gonna go into chats. Let's actually go back all the way to Gillian, since that's the user we care about. So we'll go to Users. We're gonna go into Jillian, Julian's chats. And then we care about Julian's chow with mats. And then the message is, and we'll see here that this is the same issue that we have. We still have created as an empty string. So let's figure out why that's happening here. So here we'll actually say guard, that day string isn't empty because we are pretty heavily relying on the data string. So let's see what's going on. And if it is empty, I'm gonna go ahead and print out date string empty, just like that. And let's see, this is going to be data here, that is data as well. So we should have this not being empty. So let me go ahead and give this a run and try to send another message. All right, so we're gonna click on that and then I'm going to just type some random junk in here. I will go ahead and hit Send. And we can see that it actually gets rid of it, but it doesn't send anything. And we have a log here of date string is empty, which is not good. So let's see why that's actually happening. This is a date, so let's see what this data looks like. So we can say let's date equals dates. And we're saying date formatter, go ahead and create one of these so I can go ahead and say, this is let formatter will be a date formatter. Let's go ahead and see what's going on with this. Alright, so I finally realize what we were missing here. So a date format or actually doesn't have a format by default. What I meant to use here was a ISO 8601 date formatter. And this has a standard format for the string itself. Well, we could actually do is move that there and move it this sky to be a single line. Or once again, that way we don't have to have such a verbose code here. And the other important thing that we want to take away from this is not only do we need to change the actual formatting here, but we need to actually change the formatting here as well. So in when we try to actually create a dates, we need to use the ISO 8601 formatter and try to create a date from that. And then we will actually succeed to create a date. And we can add back of the sorted by. And once we have the sorted by again here we can actually save return first dot created is less than second dot created. And if you go ahead and give this a run, one thing that you'll notice is that not only will our things begin to render properly, but now we have some differing messages. Why is that? And the reason for that is because it's actually filtering out those messages that don't have a proper date because we're returning in returning nil here. And I went ahead and sent some date text test messages here. So we wanted to test that out. So if we actually had on over to our browser, we can clear out. All right, So being back here in our database, I'm going to quickly do a refresh. And what we'll see here is that we have messages under this conversation. So what I'm gonna go ahead and do is hit this three dots icon will say delete collection. And we're going to type in messages to confirm and delete that there. Now we need to make sure we are also deleting it under the other users. So if we go back to at this point users and then Matt then shuts with Jillian. We also need to clear out these messages because we have a two-sided conversation. So let's also clear these out like that and refresh the page and everything should be in working order. Let's go ahead and run our project on both simulators, both left and right here, we should have a empty conversation. And let me also go ahead and give this a run on the other simulator on the right-hand side here. So we have that one freshly running our latest fixes and code. So bear with it. And we'll be able to send conversation messages. So if I open a mat here, you'll see that we should be able to send a message to Matt and we'll get it over here on this device. So let me go ahead, bring up the keyboard on both of these. And let's go ahead and send a message. So say, Hey, how is it going? And we'll go ahead and hit Send and boom, it pops up over there. I'm doing well. Let's go ahead and send that. That pops up at the bottom. This is awesome. Let's throw in some emojis to make sure those are looking good. So over here we'll throw in a laugh at a heart, maybe a thumbs up. And let's see, we don't care about this emoji stuff. So let's go ahead and just hit Send and boom, look at that. Our order is correct. We've got emojis coming through and everything is in working order. So little bit of debugging halfway through unplanned. So hopefully that process was in general, helpful to you guys to understand how to fix your bugs and work your way through them. So that's all I've got for this part. They just sticking around. I will see you in the next part where we will add images to these circles here just to clean things up as well as in our conversation. So thanks for being around. I'll see you in the next one. 17. Chat Photos: Welcome back everyone. This is the last part of our series and we are going to be adding a photo to both the conversation list and the chat view. So before we actually do that, if you take a look at the chat view here where there's one thing that we want to clean up. We see the name at the top for the title is a little large. And as we scroll up, it gets smaller as we would expect it to be from the default state. So one thing we can do is jump into our chat view dot swift here. And we're going to change the navigation title here. This modifier to also take in a display mode. And we're gonna so display mode of inline. So if you're autocomplete isn't working, you can just type it out like I did there. Sometimes you might need it. Use autocomplete to get the exact, exact syntax looks like I do, at least today. So we're going to type it title and you want to select the option with a display, I believe it's display mode, like so. And this is going to be inline. So when you go ahead and give this a run, what you'll notice is that when you tap into a conversation, the name is small from the beginning as you would expect it to be. So that's pretty, pretty good, looking pretty good. So the last thing we wanna do is bring in an image. Now, you might want to extend this application to allow the user to upload images. Now we did it, do that. So what we're gonna do is we're just going to use one of the images that we brought in and the one of the earlier videos. So we have this circle here. So instead of using a circle, what I'm going to actually go and do is make this image and call it photo1. It's going to be resizable. It's also going to have a scale to fill a modifier. And we're also going to say go ahead and clip this to a shape of a circle. And if you go ahead and give this a run, you'll see that instead of a red circle. And now in the conversation, you'll see that this is from an actual person, which looks a whole lot nicer. And if we go ahead and do the same thing on the conversation here and said, I'm showing just a circle. Once again, this is going to turn into image. We're going to use photo1. And we can also go ahead and say resizable. We're going to say scale to fill, and we're going to clip the shape to be a circle like that. Now we're hard-coding this image. So if you wanted to be a dynamic for Jillian and Matt, you can save model dot current username equals mat. We can go ahead and show photo one. Otherwise we can go ahead and sew photo too. Let's go ahead and run that. And this way we'll have at least one or two different images. So let's go ahead and run that here we should see Julian's photo, which in fact we do. And if we run it on, the other device will see that we have matte photo. So let's actually do that same ternary operator in the chat row. So instead of just using photo one, what we could actually do in here is referenced a model just like we did prior. So here I would say adds in environment object. Bear with me. This is wrap this up here. Environment Object VAR model would be a app state model. We have access to this down here. So we can say in the image itself, if model dot current username equals mat, go ahead and show Julian's photo, which I believe is photo1. And in the other case, go ahead and show Matt's photo. So let's go ahead and give that a run. So over here I can tap into Jillian and boom, there's Jillian. And over here I should be able to tap into matte and there is Matt. So there is our chat app built from scratch in it, swift UI, it is fully real-time. You can go ahead and send as many messages as you want. And what you'll see is that it'll update in real time like it did on the left-hand side over here. So hope you guys enjoyed walking through building this together. I encourage you to take a screenshot or video of the project and share what you built if you built alongside, I want to thank you again for watching every single part if you've gotten this far. And I'll also like to share that all this code is further cleaned up and attached down below. So thanks again for watching. I hope you enjoyed it. I will see you guys hopefully in another course.