Hello guys! In the previous post we displayed a very simple list of animals according to the MVVM model.
In this article, we will raise the difficulty by requesting data from the real server, namely the github server. And this article I will write a layer layer for the network that you can take on your project too.
This time we will display a list of Repositories – Github's source code and display it on our app. The image is as follows:
Take a sip of coffee and 🤓
The first source code is still the link 1:
https://github.com/codetoanbug/MVVMSample.git
However, I recommend playing with the Terminal of the MacBook for it pro. Because if you download vegetarian using the browser, you do not see where the source code is :v
Play with simple terminal as follows:
- You typed the cd command into the source code the previous day you downloaded:
cd TableviewSample
2. Next is to type fetch to get my latest source code about:
git fetch
3. Next you show the whole branch on my repo with the command:
git branch -a
Here you will see the following branches:
bai1
* bai2
Master
For example, I put all the source in the branch called bai2. You switch to the source code 2 as follows:
git checkout bai2
After branch bai2 is boldly colored, you've already succeeded. And if it doesn't show you're wrong, do it again!
It hasn't run yet. If you want to run, you have to install a pod for it. If your computer doesn't have a pod installed, type the following command:
sudo gem install cocoapods
Enter your password and click enter. If you have already installed, ignore the above command and type the command:
pod install
If you go to the folder and see the following means that it has succeeded:
And now open the TableviewSample.xcworkspace file and proceed to soak 🥳
Run the project up and you will see that it will be different from lesson 1 with the interface as follows:
I created an extra screen, with 2 buttons. The Local Animals List button will reload the example of the article 1 day ago. The Remote repositories lists button will load the example of today's post. When you click this button it will show off the list screen as I presented at the beginning of the article. OK, basically this screen I did the following:
- I created table view like lesson 1
- I created a network layer to call API
- I created a service to write an API to get data about the show
- I write the model view using the above service to get data to pour into the model view
- I shot all the data into the view to show up
When you separate separate jobs like this, the view will not care if you get data from local or from the server. It only cares that the model view will return to it. And it doesn't process any data.
That's the style of MVVM. Now let's find out about the moya network first.
What does Moya mean?
First, we need to understand some basic knowledge about the internet and how to call the API from the server. The applications we use every day such as social networks, most will use the mechanism to call the APIs and get the results returned. That mechanism could be Restful, GraphQL. Here we will study Restful, and the GraphQL is temporarily learning other lessons ha, facebook it also plays with those types of GraphQL, writes the API once, online realtime, but now is not the time for us to learn these, to win the next time I write another article on this topic.
So what is Restful? Say it's the mechanism for sending a request, getting a json back. Then our application must read this json and turn it into a model to use in our app. Can use get, post, put… To understand it better, let's take a look at the API that we are about to request.
First you download to me the Postman application, before code anything on the IOS side, then you will have an API from the backend sent to you (or you do it), and run it to see how the results are. Postman downloads here:
https://www.postman.com/downloads/
When you finish uploading you installed on the machine, create a get request and copy the link as pictured:
https://api.github.com/search/repositories?q=language:swift&sort=stars&order=desc
OK if you see this means success:
You notice, I use get to get data. Here is an API of github, used to request repositories with a programming language of swift. In many cases later with the application you do, the upload method is post. Post or get, depending on the way the backend defines you. OK, so we're going to write code to handle this API. You can click here to view Moya's source code:
In Moya's readme, it writes in great detail in English how it works. Moya writes the way I give you these parameters to call an API, you just fill in the blanks for me, the rest I do. What are you filling in the method (get or post..), the path, path, params you send up, what is the file to test.. Then the rest of me's worried about you. It's moya. Very sexy, not 😘 OK and specifically how it is read.
And now it's time to start writing the following extremely important layer network.
- Create a base layer to request a network
You see the base code folder as follows:
And let's take a look at each component. Now let's take a look at the big or make some coffee to relax your mind and continue 🤣
The first is the Configs file as pictured:
Here I use the technique of automatically switching test or production servers thanks to the simple flag of Xcode. You notice that the Network struct is in configs struct, that is, if you later need to fix the config values for it, you can create more structs such as Color, Dimention … Here you pay attention to the macro #if DEBUG #else. This statement means that if you are running Debug mode on Xcode, the server path will be the upper line, and if you run Release mode (when uploading to the Appstore), the line below. This means that the Xcode compiler will automatically change the baseUrl for you depending on whether you are running a debug or pushing the app to the store.
I use the same two lines because I am lazy, but in fact if you do real projects, you will be provided with 2 dev and release servers, then you have to know how to config like me. The light pay is high for the lazy. In addition, running Debug mode, the default Xcode it selects. You can adjust the release as follows:
So why these two modes? New friends will wonder, I simply say that if running debug, Xcode adds many hidden code to the program for debugging purposes, so the program is heavier. And when you fix all the bugs, when uploading, there is no need to have those hidden code, so you will be born release mode. By default, other IDE's handled the same. Like android studio, visual studio..
It's simple, isn't it? Ok next we go to the BaseError file as pictured:
This file is 1 enum for me to handle errors returned from the server. The server we will get a basic error:
- One is a false request error. For example, the backend told you to send it up, but you deliberately posted. Or it says you transmit 2 param up to get (like username, password to log in) then you deliberately transmit 1 up, it is also called the wrong request. Or as disconnected and you request is also called wrong. The wrong kind is many types, you just understand that for it is simple.
- Next is the request, the server returns the result. However, when you translate the json file of the server, it is somehow wrong. It is possible that the return is empty, returns null, returns none and we deliberately decode it.
There's a friend here who asks me, what are you talking about? This you understand is that it is an object of key and value respectively. If you want more details, please wait for me to write another article or google.
In the postman you do above the json also returns and you can look deep into it and read each key and value accordingly.
Then, because I just described above, I created the enum as I said. It consists of 2 corresponding cases. Each case I have a title to display errors, and a description describes the error in detail.
OK, come here if you don't understand what I'm talking about, you probably have to take a little relaxing break and watch it again, because the following is the most important. The NetworkProvider section is in the center of this article. You view the NetworkProvider file as follows:
In line 9 I started using the Moya library to rewriting this provider class. Provider means to provide dentistry, which means I want to refactor moya a little easier to use (although it is also easy to use). Line 11 I changed my name a little for it.
Lines 14 to 18, I use the debug technique as above, I only show the logs of moya when running debug. If you find it annoying that many inscriptions are printed on the Xcode console screen, you can remove false in one mode.
Lines 20 to 25, I created a protocol aimed at:
- If I run defaultNetworking() mode, I call the API directly to the server and get the result returned.
- If I run stubbingNetworking(), that is, I call the API from the app always, and return the result through a json file below the app. Therefore, you will not need a server when running this mode. It is often used to test the API or when the server is busy holding his wife or his girlfriend has not written in time, you are still ok to write the API running under the app without depending on it, as long as it gives you a json file return.
The above code is the function that gives the provider, a lot of Moya's parameters, and I don't want to explain in detail what it does. But you keep writing exactly the same after having time to go to the Moya library to see it explained. There is an online variable aimed at checking if the network is working well. But in fact, in this project I do not have processing, you can code yourself.
API request function:
The request function above I choose mutil target mode, because according to my code experience, when choosing Mutil target, you have many APIs, each API 1 cluster such as the app you have the following phrases: login, logout), Home including feed, hot feature … Then when processing mutil target in Moya, you will split many separate API files for easy coding and unit test. Target here you understand it is the server you are going to call, it will consist of the following components:
If you read English, it's easy to understand, isn't it? Otherwise, I'm going to translate like this. Want the API call you need:
- Know what a base URL it is
- What is Path? for example, if we https://api.github.com/search/repositories, the base URL is https://api.github.com/ and the path is search/repositories
- Method is get, post, put, download…
- Sample data is something that I say you do not need a server, just a json file at the bottom of the app can write unit test for your API. Moya ready-made for you to write tests or run the app under local
- Task can be a request in the form of a json sent, or parameter, or plain. This is a call server rule, so you just need to understand if the server guy told my friend to tell you to call up by post, you choose the jsonEncoding style for me. And if you call it get, you choose parameterEncoding for me. I made it available to you here:
In many cases if you choose wrong, your code will return an error that cannot be called (send get instead of post, jsonEncoding instead of parameterEncoding)… And then I lost the whole session and I didn't know where I was wrong. Pay attention to the correct configuration otherwise do not blame the sea water for 🥲
Back to the provider above, Moya returns 2 cases, 1 case success and 1 case is failed. when the call api succeeds, it means response.statusCode == 200, I proceed to translate that json into my codeable object.
if response.statusCode == 200 {
guard let results = try? JSONDecoder().decode(T.self, from: response.data) elsewhere {
Decode error
completion(.failure(BaseError.parseResponseDataFalse(title: target.path)))
return
}
DispatchQueue.main.async {
completion(.success(results))
}
}
The code above me try decode, the purpose is to assume that if you write a model to translate the response to its object is wrong, it will shoot out the decode error, and return it too.
Note that I return the completion to the main thread to ensure that the UI of the view when there is a result from the server will handle on the main thread. By default Apple only allows UI processing on the main thread. Where the case failed is as follows:
Here it means that the API call is wrong as I said above (loss of life, wrong call, post, or wrong encode …) then it shoots here. I've failed the same way. English means finished, here's the completion of the api 😂
The code from line 86 you see is also the above code, only different returns. In the API request above, 1 codeable object is returned. And the request below is to return the array. I write for you if you want to handle arrays.
The processing code runs the real server or the fake server 😍
As I mentioned 2 times above, this paragraph will help you handle real API calls or local calls. How to use it, slowly read on. Basically there will be things that are so important.
2. Create GithubAPI for github api
Next we will write GithubAPI for this github. Later if you have other API clusters such as authen (including Login api and logout,…), you will have to write separately to another file.
Lines 11 to 13, you review Postman, you see that the API consists of 3 parameters passed up:
So I'm lazy, I'm naming the same name as postman. Your name and param should be placed close to the API. Kind of like it's all string.
Next from line 15 3, I started the description for this API.
- The first is the base URL by obtaining from the presented struct config.
- – path is search/repositories(also presented above).
- Method is .get
- sampleData is the read of the SearchRepositoriesResponse.json file in the local as I presented. You call below local, it will take this file fake server returns the result. The SearchRepositoriesResponse.json file has exactly the same content as your backend response. You can go to that file and see it.
- Next is headers, usually like line 59. Lines 62 to 70 will be the description of params transmitted to the server:
So you've finished writing the API for github.
3. Create a service to call the github API
Next we have to write a service file to call the API. You go to githubSearchService file:
This file is simply inherited from BaseService. it will use the BaseService provider to call github's api. If you use MutilTarget to call, you'll be able to separate api clusters separately. The above example is the GithubAPI cluster, the latter may be AuthenAPI… When calling a request you need to transmit to:
- The target you want to call. Here is GithubAPI.searchRepositories
- The model type that you want decode to return. Here is GithubSearchResponse. This model I created later.
In BaseService code as follows:
Here I default the test mode to false. If you create a service where set isTest is true, it will never call up the server but completely take your json file below the local return the result. It's for API testing purposes or you don't want to wait for the backend guy to finish writing the API.
Then now after completing the service file, you continue to write the model. Sounds complicated, doesn't it? write long lines of code. You'll probably complain to me like that. But when you make a big team, breaking it down will make sense for the team work, and getting used to it is addictive.
4. Create a model to get data from the server
You go to the SearchRepositoriesResponse.json file, you will see a lot of keys and value. Here if you are lazy you can go to this page to create a model 1 quickly:
You copy that json and parse it. Make sure it's a model, too. However, since I do not need all the data in it but just the name and path to display, I create as simple as the following (see GithubSearchResponse file):
After having the model, you create a model view!
5. Create a model view to get data from the service
Go to GithubViewModel to track the code as follows:
- Line 12 I use GithubSearchService. In fact, the project is a real view model can use many services.
- Line 14 is a closure variable for the purpose of callback to view, notifying the view reload table when results from the server.
- Line 15 is similar but reports an error and displays it on the screen.
- Lines 26 to 42, I write the API call function from the view model, through the service. There are 2 cases, if there is a result, I save to the model located in the viewmodel and callback to the view to reload the table. If I fail, I'll show you an error. It's very clear, isn't 😘
Then now it's simple, the last thing from the view calls the model view.
6. Use viewmodel in view
You go to the GithubViewController file, but focus the above code on me.
- Line 30 is to create a viewmodel.
- Line 31 to 22 is call back when there is a result from the server, then I reload the table to display the result.
- Lines 35 to 37 are when the server reports an error, I display the error on the controller view.
So you have completed the process of writing a program in the MVVM architecture to call the real api. In this application, I also often use reality in company or personal projects. Missing an important item is unit test for it but stop, because the test is too long so we leave it later. If you see a good article, share it for yourself. If there is a mistake, please comment below.
OK, I sum up the main items for you to master when writing the actual MVVM:
- Create a provider to call API
- Create a file that describes the API's API
- Create services to call API
- Create a model to get data from the server
- Create a model view to use services
- Finally, use the model view in the view to display the results
Hopefully, the article will help new people get used to easier and more professional in writing MVVM code in your project.