Hi everybody! For a while now I’ve been trying to create coding classes that are both satisfying and instructive for people of all profiles. Teaching programming and its concepts to programmers is a relatively easy task, since their profiles help them in understanding the content. As a Computer Engineering student in my eighth semester, it’s more or less natural for me to understand things such as abstraction levels in a project, “design patterns”, and coding architecture. But here at Academy Rio, we have a lot of students from other knowledge areas, and I can see their difficulties with understanding some of these concepts. Another thing that I noticed is the frustration of my colleagues who wanted to express themselves with code and see the results in their apps, but don’t manage to do as much as they would like because they find programming-related subjects difficult to understand Realizing the challenges these students face, I started to search for what were the best practices for transferring these kinds of knowledge to those who do not have a programming background. We found in SwiftUI an excellent opportunity: seeing your code materialize in Preview in real time is a game changer, since it makes abstract ideas and concept have tangible visualizations. It’s magic! But the programming world cannot exist with interfaces alone, right? We understand that de other side of an app is also important: its functionalities An interface without its functions is just a prototype. What if we applied concepts similar to the ones presented in SwiftUI when we were making the persistence layer of a project? In order to make this possible, I created CloudKitMagic. CloudKitMagic has the purpose of making the introduction of concepts relating to remote data persistence an easier task. With a quick starting configuration and in an easy and simple manner, we will present some concepts such as the division of an app in layers (Model and View), communication between layers and synchronous execution. But in order to demonstrate how CloudKitMagic works, we need an inspiration, that is, a project that is engaging. Recently I was talking to Ricardo about how there was a moment in my last Challenge feedback when I felt confronted: it was when I received a feedback that oriented my group towards removing an in-app chat from the solution we were proposing. “We already have too many chats, we can just use an established one”, was what the person who was giving feedback said. While this may be true, even big apps such as Uber and iFood use their own, in-app chats. I asked myself: how costly would it really be to develop a chat? Would the discomfort of having my user need a third-party solution really be a better option than simply making my own in-app chat and handling its time and resource costs? So I suggested this challenge to Ricardo: can we make an entire chat in less than an hour? Challenge accepted! In this workshop, we will try and create a functional chat in less than an hour. In the following tutorial we will touch on the following topics: in part 1, we will briefly review SwiftUI, in order to create the chat interface; then the reasons behind dividing an app in layers like Model and View; in part 2, how to prepare your project for iCloud; how to add iCloud capacity; what is a container; and finally, how to add and use a swift-package; how to read and save data with CloudKitMagic and how to set up “new message” notifications with CloudKitMagic. Let’s go! Ok, so we will start creating a new project in XCode First we choose the “App” option, then we’ll say the name of the project is InstantMagicChat. We want to make a SwiftUI app, so choose “SwiftUI App”. We do not need neither CoreData nor Tests, so just click Next. Save it in your projects folder or wherever else you’d like and that’s it, we now have our project set up. In our interface, we defined that we’ll have a header, with the username (an editable camp); the icon with the username’s first letter; a status message and a button to download new messages In the middle, we’ll have a list containing a “sender”, the content (the body of the message) and the date and time of each message. A footer, with a text field in order for the user to write a new message and “send” button So we’ll start building the View of our message. We’ll start creating a new SwiftUI element – Command+N is a shortcut to create a new file. Let’s call it MessageView Our new View starts with the usual “Hello World”. So the first thing we do is delete it. Like we said before, we will have a colored rectangle in the background. So for us to have these two “layers”, we start off with a ZStack In this ZStack we add a RoundedRectangle, with a set cornerRadius. Let’s put a value of 20 for the cornerRadius? And the foregroundColor as green That lookgs good! After this rectangle, we will have a VStack with three elements The first element will be a
text box, so let’s put the “name” there. Let’s say that it was “Joey” who sent the message The second element is also a text box: the message itself. For example, it can be Joey’s typical “How you doin’?”. For this text box, we’ll have to put a slightly bigger font, right? So let’s set it with the system font of size 20. Ok. Great! Another thing that we have to do is adjust the frame. We set the “.frame” with maxWidth as infinity and a “leading” alignment Wonderful! There’s a catch there, so we’ll do the following: add a false for the horizontal “fixedSize” and true for the vertical. But why? This makes so that the view can adjust itself horizontally by itself, but no vertically. So when we have many messages, one below the other, the View will be ablet o adjust itself according to the size of its message. Ok, got it Well, after that, let’s use an HStack to insert the hour and the date So we’ll have a text box, in which we’ll put today’s date We can also make the font smaller, like the system font with size 12 And we can just repeat it for the hour box – that’s it And we’ll put a Spacer in the middle Exactly. We also need to make a few more adjustments in this screen, and in order for us not to spend too much time on just SwiftUI, let me send you everything you need to edit. You can just paste the code, please. Ok! I’ll press “Resume” to check how it looks now. Done! The alignment is already correct. For us to see how it is actually going to look like, we’ll need to put it into a ScrollView, which is how we’ll show a lot of messages at the same time. We need to put our Preview into a ScrollView. Here, right? A vertical ScrollView and put this View there Done! That’s it: that’s how the message will look inside our ScrollView. So, our message is done, right? But now that we have the MessageView, we need another View, right? That will hold all the messages. Yeah! Please create a new file – this will be the ChatView If I remember correctly, our ChatView will have three parts: the header, the message list and the footer. That’s it! Our header will have a button responsible for receiving new messages So let’s start creating the function receiveMessages We’ll leave it empty for now, since we just need its reference. And later we can change that text for a VSTack, because we’ll have three elements, right? Yeah. I think it’d be nice if we already start putting some comments like “header”, “body” e “footer” Wonderful! The header will be an HStack, right? Yeah, with a button, a text box and a text field. The button to execute the action, a status text and the TextField. Great The button is done. So after the button, we’ll have the status text This status will change, so it would be if we create a variable of the “State” type, with the string “No new messages”, for example Ok! Great, and after this we can put a text field asking what the name of the user is Okay But this “my name” will also need its own variable, right? Exactly. Then we can create the State variable “myName”, for example And what default value do we use? “Joey”! Wonderful! Well, our header is done – let’s see what it looks like In the footer, we’ll use a text field for the new message and a “send” button, right? Yeah So first we need an HStack We’ll create a TextField that receives “newMessageText”. And what we need now is the variable that will hold our message Which is also a State variable. Yep, it is And do we use a default value? No need! Ok Besides that, the footer needs a button, so I’ll use just a default one. This button, like every other one, executes an action, so let’s create a new function called “send” under our “receiveMessages” function. It will also stay empty for now. Let’s set just one parameter: “text” of the String type. The
“send” function will need to receive the text of the new message as input so it can send it. Ok, so we already have the header and the footer. It’s not looking very good, though. In the “body”, for now, we’ll create a Spacer so we can see if it positions itself as we intended Oh, great: look how the header is already correct, as well as the footer in the bottom. Awesome! In order for them to not be too close to the sides of the screen, let’s add horizontal padding to the main VStack. It’s in in line 41. Horizontal padding… done! It’s already looking better. Well, if we have the header and the footer, now we just need to make the body, right? Our message list STOP! Why?! Before we start adding messages, let’s organize our “home” Looking at our app, we can conceptualize in two ways Conceptualize? Yep! We have two complementary ways of explaining our app: one is looking at our data, which will be created and transferred as our app is used, and the other way is looking at our interface and how our user interacts with it. In our example, we’ll start looking at our data. We saw that we have a “message” concept. This message contains attributes such as its creator, its content, and the time and date of its sending. These attributes are always the same regardless of how they’re visually presented Right now, we are not preoccupied with how all this data will be presented on the screen, with its color, its position, etc… just its values, nothing more. In order for us to organize ourselves, let’s separate the project in two layers. But why separate in layers? This is the way we explain the parts of our app without mixing them up. First we have to create the Model layer. And what is the Model layer? The Model layer holds the concepts, that is, the classes or structures that represent our data abstractions Regarding our case, what exactly is a message? Let’s create our Message model. So, you’ve already created the Model group. We’ll create a pure Swift file – not a SwiftUI View – and call it “Message.swift”. Inside it, we’ll create a new structure We’ll start with “struct” Message and inside it we can write the attributes of the message. So, they’ll be “id”, “sender”, the “content” and a “timestamp”. The “id” is a generic identifier – I’ll just use its hashValue. We’ll explain this a little bit later The “sender” variable will hold the name of the user who sent the message, “content” is self-explanatory, and “timestamp”, which is when the message was created. Great! We set a default value for the timestamp, so the system records the time when the message was created. In the “id” field, we can do something similar and take the “hashValue” of the message. But then it needs to conform to the “Hashable” protocol, right? Yup! And the “id” always returns its “hashValue” string This is a simple way of identifying a message Since our app is a message app, our Model layer is complete. Now we’ll go to the View layer. We already have two files that are from the View layer! Let’s create a a new group to organize our “home” and put these two files inside Analogous to the Model, the View is the layer that holds all visual files of our app. In our example, the MessageView that reprents the message, the ChatView that represents the header + the message list + the footer, with the interface functions. That is, those related to the input and output needed to communicate the data to and from the user. In conclusion, the View is the one that handles communicatio with the user What’s interesting to note is that the View literally represents the visualization/view of the concepts in your app, that are described in the Model. While the Model is only worried about concepts and rules, the View only worries about how it should SHOW these concepts and rules to the user Now it’s easier for us to handle and deal with information: a MessageView can visually represent a message, receiving it as input once it’s being built. Returning to our program, we have the concept of Message, so we need to make the MessageView receive, hold and represent a single message, and we update the code in order to do so, we we’ll see now. So instead of having default texts, we’ll use the values inside the “Message” in our MessageView, right? Perfect! Here it’s “message.sender”, here we’ll put the “content”, and finally the timestamp. First we put the “message.timestamp.day”, and then the “time”. Check if everything is okay. Let me see. Oh, we got an error Oh, it’s because you still don’t have a “day” and a “time” in the timestamp, right? That’s it! So what do I need to do? Let’s create an Extension of the Date type, so that you have a “time” and a “day”. Create a new file, which we can call “ModelExtensions”, and then everything related to the Model
that we need to add as an extension, we can just put inside this file Now I want to extend the “Date” type. I think it would be better if you just copy and paste the code that I’ll send you now. Yeah, that’d be easier Now your “Date” has a “time” and a “day”. Let’s go back to the MessageView to check if it is working – just build the project using Command+B It’s missing the “message” parameter, but which message should I use? Yeah, in order for us to show a message list, we just need to have a collection of messages to show inside a ScrollView or a List. Oh, like a mock message list, right? Yup! I’ll send you a “mock messages” file, which we can use for testing purposes. Okay, I’ll add it to the project Put this new “messages” file in the Model layer, so we don’t have a bunch of lost files throughout the project. That makes sense. So, what do we do now? We can use these mock messages So this should get the first one, right? I’ll run a test. It worked! Something you can do now is copy and paste this line and get many messages in order to check how multiple messages will look like in the Preview. Oh, that makes sense. It worked. But Ricardo, the messages all look the same, right? Even if different people sent it. That’s true We need to change the alignment and color depending on if the message is from another user, or if it’s from you. Let’s start creating a concept named “itsMe” in the MessageView. This variable will check the current username, which is held in the “myName” variable of the ChatView We need to add a “Binding” of this “myName” variable in the MessageView Then we just create the “itsMe” concept It’ll verify if the message sender is the current user or not With this change, our preview probably won’t work anymore We need to create a “State” variable in the preview with a default name Ok Now: if it’s me, the message background will be green; otherwise, it’ll be any other color So the rectangle will have a “foregroundColor” that will depend on the “itsMe” variable Let’s run it and see if it works Great! But in order to differentiate between messages, we also need to create a horizontal padding on the ZStack, depending on if the message is mine or not. Let’s also define an “id” for this message, because it’ll be useful in the future. Ok, first I’ll define the “id”, and now I can do the same as I did with the color… “leading: trailing” aaaand done It’s working pretty well, huh? Looks good. With the messages being from the current user or not, we can distinguish each of them visually. But gray and green might not be enough. Let’s try to add more color to the chat? In order to do so, let’s create a color collection. I’ll send you a little collection of Color Literals. Ok, let me put it into the code Another thing we have to do is create a dictionary associating usernames with colors. So create another “static let”/constant, a dictionary And it stays empty? Yup, exactly Finally, I’ll send you a pretty cool function that return a specific color for each user in our chat. Ok, this one? “peopleColor” Exactly. Now we just need to change our “grey” for “peopleColor” and “sender”. Then the app will choose a new color for each of the “senders”, each of the new people in the chat Wonderful! Looks great. It’s working – our Preview is looking pretty colorful Remember to always update the Preview Provider in order for it to show something that’ll help you. Let’s try to a few more messages in the View, since we might have a big list when we use the actual app Let’s go to the Preview provider, and put three more, for example You can just put it by hand Done! Very good The scroll is working perfectly, too And in the end of the list we have “Rachel” with the new color Very interesting So what we’ll do now is add this ScrollView in the ChatView So, where we had put a “Spacer” in the body, we’ll create a ScrollView and a “ForEach” loop that’ll show all the messages in our MessageView Done
Let’s populate a “messages” array up there with our “mockMessages” Let’s create a State variable called “messages”, which will be an array full of messages. And now, in our “receiveMessages”, let’s say that “messages” is equal to our “mockMessages” Done. Now, let’s go to the “send” function and create a new message from whatever text the user wrote, and add it to our message list Ok. So the new message are we missing anything? Wait – isn’t “context” actually “content”? That’s true. Now, build it and let’s see if it works Oh, my mistake Now it should work Oh, I think we’re not actually getting the “mockMessages”, are we? Ok, now we are. NOW it should be working, right? Let me turn on Live Preview and write “Hello” I wonder if it worked? Oh, I have to scroll down in order for the new message to show up. It would be interesting if the ScrollView moved automatically to the last message sent, right? Let’s add that functionality. So, to operate a ScrollView, we need to insert it into a ScrollViewReader. There’s a very useful trick for this: click on “ScrollView” while holding “command”, and embed it into absolutely anything: it can be a List, a VStack, just choose anything Now, trade the “List” for a “ScrollViewReader” And of course, the item for a ScrollView With this trick, it’s pretty easy not to get the brackets mixed up, right? With the ScrollViewReader, we can just jump to the element identified by an “id”. Remember that we added the “id” to our model? Now that we have embedded our ScrollView into a ScrollViewReader, we also add the functionality of it moving to the last message “onChange” and “onAppear” It updated already! So now, try running the app and add a few messages to see what happens There’s even an animation. Great! Well, with the interface working perfectly, let’s go to persistence!