How to Make a Clean Architecture Cryptocurrency App (MVVM, Use Cases, Compose) - Android Studio

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hey guys and welcome back to a new project video this video will probably be one of the most important videos you can watch as an android developer if you actually want to succeed in your career because it will be about making a clean architecture mvvm app with use cases that that whole clean architecture approach and i will just show you all of that in this video i will explain the structure of the project i will explain use cases i will explain everything that belongs to it and this is so important to know as an android developer because if you don't know how to structure your applications then that means you can't build any apps that go beyond like two or three thousand lines of code and pretty much every app goes beyond that therefore please don't skip this video get yourself a cup of coffee enjoy it but take yourself that time and learned it's free i decided to not make this a paid video this could easily be a course for like 50 or 100 euros but i give it you for free because i think this is something every android android developer should know and therefore i decided to put it on youtube are there any prerequisites here before you can start with that well yeah you should be somehow familiar with android you should know like basics of dependency injection with dagger hilt you should know retrofit making network requests and the basics of jetpack compose so we will be using jetpack compose here and what we will build is we will build a cryptocurrency app so this app will just display pretty much all cryptocurrencies here that there are those come from an api and if we click on one currency like bitcoin we will get this detail view we have another api request we get a description um some tags that belong to that currency and the members who actually founded that currency and all that will be done with the mvvm clean architecture approach like from uncle bob in case you don't know that it's basically uncle bob who came up with this approach which just extends the typical architecture that i use in my project which is just plain mvvm with some more things that help you to make your app more testable more extendable better understandable especially that yeah just make sure that your app scales that is the goal here so i will explain everything we do but um those things that don't directly have something to do with clean architecture i will go through them quicker and i will actually focus on explaining the structure of the project i will focus on explaining use cases i will focus on explaining for example why we use dependency injection and just how that helps us with a good a good architecture actually so enough talking let's actually jump right into it and you actually need to get the initial product here it doesn't contain a lot just the typical stuff so it's an empty composed project project as you can see all that i really have included is a theme so we don't need to worry about colors and uh for example textiles here you can include that i mean you can just go to the link down below that says initial project you will then see this page that is the github repository here you want to be sure that you're on the master branch which is the initial project you can click on code copy this url go back to android studio file new project from version control that is what you want to do and here you can simply paste that url click on clone and android studio will include my project in your android studio that is just the easiest way to include that you can also just go to download zip and import it like that but i prefer using git for that if you struggle somewhere in this course and you want to see what my code actually does you can also visit this repository click on master and then there will be an app branch so that app branch will contain the source codes the final source code of this project so when that is done next up i want to show you the api that we use and that will be coin paprika that is a free api it doesn't require any kind of um authentication so we can just access it without any further further setup so it's basically just an api for cryptocurrencies exactly what we want then we have two api requests here that we will need in our project which is on the one hand this api coinpreproca.com1 slash coins which gives us the list of all coins that are out there or that are in that api and that is a lot this is like a 700 kilobyte json response that we will get in our app and you will find all these links by the way down here in this description so this api link you will find the other api link here so for a specific coin to get the details we get this for example for bitcoin and that is actually it one last thing before we actually get started is if you like these clean architecture concepts and you want want to learn more of these for free then you can check this video's description because i now have an email newsletter and i will send you regular advice on android development on kotlin on getting a better developer and i will especially focus on more advanced concepts there like like this one here clean architecture and just specific things because right now i have instagram posts i have youtube videos which is all visual and auditive content but you might also like to see some written content by me and that is what this newsletter is for so go down below and there you can subscribe to this newsletter and then you will receive regular android advice right into your inbox but that is now really it let's go to android studio and jump right into it so what the heck is actually clean architecture for while i thought that it's just mbvm and android mvvm is an architectural design pattern that helps us to structure our code and scale our projects however clean architecture kind of extends this it actually includes an additional layer in mvvm so in mvm we have model view and view model and in clean architecture we have one more one more layer which are use cases and these use cases contain our business logic so in mvvm we just put all of our business logic into our view models and the problem with that is that the bigger your project gets then you start to have these typical guard view models a view model that just does everything and this is one thing this clean architecture approach tries to avoid and actually does that quite successfully because we now use these so-called use cases so what is a use case a use case is basically kind of a part of a feature of your app so let's first understand what is a feature a feature is just a set of screens together that are somehow related so for example the profile feature that could be you have a profile screen and you have an added profile screen and these two screens together make up a single feature the use cases would now be single actions we could do in these features for example get user profile data update user profile picture all these would be use cases so single actions we can do within a single feature the big advantage with use cases is that they on the one hand are very reusable so if we have an additional layer which other use cases here then we can reuse that so for example if we have a use case get profile data and we need that on multiple places in our application in different view models if we wouldn't have that in a use case and would put the logic for that in the view model itself then if we have two view models that need to access the same data we would have the same code in two view models if we have one use case class in which we put that code then we can simply use that use case class and use it in both of our view models so it removes code duplication and advantage number two is that it leads to what a block article actually called screaming architecture and i like that term screaming architecture is basically that you take a look at your classes at your project hierarchy and you and it really helps you to understand what the project is about because well what you will see is you will have package for use cases in that you will have for example a feature profile and that contains get profile data use case get profile or rather update profile picture use case um whatever delete profile use case and you you don't even need to open that class you know what's in there you know that it's used to update a profile you know that it's used to update a profile picture so that is what we mean with a screaming architecture it screams at you hey this is this is what i'm for so far so good about use cases um there's something else you need to know before we can actually jump into practice here and that is that we basically now separate our project into three layers on the one and we have presentation so the name kind of says it it's used for ui so we present something to the user so in the presentation layer we put all of our composables if you use xml for example your views your view models actually also belong in there then we have a domain layer the domain layer contains our models so the actual entities that contain data it contains repository definitions for example so the interfaces for repositories and it contains our business logic so our use cases and finally we then have the data layer that well the name the name says it it contains our data so it contains our api interface a database if we have one we don't have one here but if we had one it would be in there it contains repository implementations for example and all that stuff i will actually jump into it now because it will get clearer when we implement that so what i will do first here is i will set up our package structure because it will be quite quite huge and it seems like it's a bit overkill here for this project and it actually is because it's a very small project we have here and we use an architecture that is made for um apps that should scale but to to make you understand how it works i will do that i will use the same package structure as i would use in a big app so as i said we have three layers in our app let's create a package in our root package for presentation so our ui package basically and we can take our ui package here and main activity move it inside of that presentation package then that's a little bit buggy here um it will disappear soon when we create another one let's go to our root package create another package for domain and one more for data so these are the three layers we now have in our app that will lead to a separation of concerns so that helps us to just test these layers in isolation and that they don't really affect each other so if a test case actually detects that business logic fails then we want to be sure that the issue is also in the business logic layer so in the domain layer what shouldn't happen is that if we test our app let's say we test a use case and the test case fails because of some error in our data layer that shouldn't happen and the separation of constants helps with that i will create one more package in our rule package which is called common and well we will put everything in there that is that that all of our other three layers have in common so for example a file for constants let's create that constants here which is an object yes let's add that and i will also paste one more file which i use in all of my project playlists so i i won't write that from scratch here but i will explain you what that is for it's called resource file it's basically just a generic wrapper class if you've never seen this this looks super complicated but it actually isn't so we just can put this class around any type of object which will in the end be our network response and this wrapper class just contains information about the actual data and the potential error message so if something went wrong we can just emit such an error we can emit success if it was successful and loading when we're actually loading so that will just help us for ui state later on if you don't know that you will understand that later then let's further build our package structure starting in our presentation layer i like to have a package here on the one hand for coin list so the main screen for every single screen i will now have another package and we have one for coin detail so that is the detail screen and usually you would also use a multi-module architecture here that you structure your project with multiple modules but that would definitely be too much for a single youtube video um so i will use this approach here and just having three different packages and that is also the easiest approach then in these two packages i will create another package which is called components so that will just contain composables that we need inside of that package here as well okay but that is it for the presentation package structure let's hit further to the domain package structure this will contain a package for models it's a model package it will contain a package for repositories so repository package and finally one for use cases so use case package in that use case package we will have our two use cases in this app on the one hand to get coins and on the other hand to get a single coin so to get the coin details these are the two major use cases we have here but they don't need to be related to getting data a use case can also be something like searching in a list for example um yeah but usually it involves some kind of database or api but it doesn't need to be so let's create our two packages here get coins plural and get coin singular so those are the two use cases we have that is for a specific coin and that is for all coins in the list then let's go on with our data package and that will also contain a package for repositories and the differences between these two we will explain that when we get to that point we have another package for um actually in remote for our remote data source and that will have a package called dto so what does dto stand for that stands for data transfer object and it's basically the object that our api returns but very often we don't need all the data that our api returns so we actually want to have lighter models that only contain the data that we actually show in our ui these lighter models come into our domain model package and the dtos that come directly from our api come inside of the data package and that is also already it for the data package one very last package we need is in our root package a di package for dependency injection and that is now really it for the package structure so that is how i would approach such a project and how i would structure it and also if you choose a multi-modal module approach then you will find this type of structure here over and over again so you always need to know the differences between data domain and presentation and what are use cases and all of that stuff that will be the same in multi-module projects so let's get to the actual coding part finally i want to start with implementing our api so i will structure this kind of from the top to the bottom so we will implement the ui the last because i think the rest is more interesting here and that is also why you watch this so let's start with the api then we implement repositories use cases view models and stuff like that so we already have the data when we implement our ui to display that in our ui so in remote we create a new kotlin class or file which will be an interface that will be our retrofit api interface called coin paprika api if you're new to retrofit in this interface you basically just define the different functions and routes we want to access from our api so we will have two functions here on the one hand to get coins get all coins and on the other hand to get details about a specific coin given that coins id so what do we have here we have two get requests we want to get data we don't want to post anything so we annotate this with add get and the route to that will be slash v1 coins so that is everything that comes after the base url that will be a suspend function so we execute it in a curtain asynchronously and it's called getcoins we don't need any parameters here we just want to get all coins and well what will this now actually return it will obviously return a list of coins but we don't have the objects for that yet and for that let's head back to google chrome to the page i actually showed you where you can access the api this one here this slash coins that gives us the list of coins in form of a json json file and we now need a data class that represents such a coin so we simply want to copy one of these back to android studio go to dto because that is now a data transfer object right click and we say kotlin data class file from json if you don't have that that comes from a plug-in you need to install that you press ctrl alt and s so your settings open you go to plugins marketplace and you search for kotlin to json class here this is the plugin you want restart into studio and then this option will appear for you so right click gto new carton data class file from json here you paste the json text and we need to give it a class name so how this class this data class should be called and i will call it coin dto and then we can click generate if you now take a look in coindto that is what was generated so it automatically also attached serialized name here so that is the name it has in the api and that is the name that we should use because it's a naming convention to name variables like that and not like that so that is now what is called a dto this is the object that we get from our api however in our app we don't want to display all that data here for example we're not interested in is new we don't care about that we're not interested in the type so we should rather use an object that doesn't contain these types of things and that is what i will do i will go to model now in domain so that will contain the data we actually access in we actually display in our ui create a new kotlin data class and i will just call this coin now and this coin will contain everything of this coin dto except for the type and is new and we can also remove the serialized name annotations so this is the data class that we will just use to display specific coin data in our list now we get this from our api but we want to use this in our code in our ui so we need a way to kind of map these we didn't wait through transfer uh transform a coin dto to a coin object and that is what we usually do when we have coin dtos that we write a function that kind of maps that it's a function coin dto dot two coin that will return a normal coin here and we just say we churn a coin here from our model package and we just assign the data as we want it to have it's basically the same data just without some extra entries is active this is active name is name rank is rank and symbol is symbol and then we can later easily call this function to convert a coin dto to a normal coin let's head back to our coin paper k api interface and this function now returns a list of these coin dtos and then the rest and the parsing will automatically be done by adjacent so let's implement the other function we have here which is also a get request and that gets data of a specific coin so also slash v1 slash coins and that now takes the coin id in the url as a path parameter so we need to write it like that so retrofit will replace that later it says pen function get coin by id here we have that path parameter you need to give it the same name as we have up there and we call the variable coin id as well which is a string and well what what will this return we don't have that yet so we need to do the same process again we need to go to chrome this time the second api request for example this this one here that gives us details about bitcoin hit ctrl a to select everything copy that go to android studio go to dto again right click new kotlin data class file from json paste this stuff in here and now that will be the coin detail dto so that just contains data and details about one specific coin like bitcoin for example and we click generate and that will generate a lot more classes as you can see here and that is really the cool thing about this plugin so we don't really need to do anything but what we need to do is we again need kind of a class that we represent in our in our ui so we don't want to pass such a coin detail dto which is a huge class here we don't want to pass that to our composables to display that it contains a lot more data that we would like to have so let's actually fix that first of all one thing you will notice that down here it has a list of teams and we display that in our app so that is this team members list here i like to rename that because it's actually not a list of teams it's a list of team members so this list of team members makes up a team so that's fine but this one isn't fine so let's simply press shift f6 here and rename this to team member and we don't want to rename this so don't check this box click ok and there we go so now we create a model class for that that represents the data we display in our ui in our model package here so new kotlin data class called coin in detail and well what will this contain it will contain the coin id we need that it will contain the name of the coin it will contain the description the symbol it will contain the rank which is an integer this time it will contain if the coin is active or not so boolean it will contain the tags that we want to display here in our coin detail so these tags here which is a list of strings and it will contain the team which is a list of team member so that is all the data we need in our in our detail screen here so what we now do is we go back to coin detail dto and if you watch this carefully you know what we're going to do now we'll write a function to convert such a coin detail dto object to such a coin detail object so let's scroll down function coin detail dto that to coin detail returns a coin detail and here we say return coin detail coin id is equal to i think it's just id name is equal to name description equal to description symbol is equal to symbol rank is rank is active is is active the tags are equal to the text but this is a list of tags a lot of strings so we actually only we're only interested in the tags as a string so we can say that map and we map that just to it.name so the name of the tag is the actual string and we have the team which is just the team here okay cool so we have our mapper function for that as well so we can head back to coin pepper kpi and make sure we have the right return type here which is just our coin detail dto here then we can now go to our domain package in the repository package because next i want to create our repository and now you might have already wondered why we have a repository package in our business model domain layer and why we have that in our data layer what's the difference the difference is that in our domain layer we cannot only define our repositories so we create an interface here let's do that new interface called let's say coin repository and in here we define the functions this coin repository should have so that should of course just have one function for each api request we have if you would for example add some caching then this would also have functions to insert in a database and stuff like that we don't have that so we only have that for the api functionality suspend function get coins and that returns a list of coin dto objects and another suspend function to get a coin by its id with the coin id which is a string and that returns a coin detail dto so these are the two functions we actually have let's go to our other repository package now and in the data package the repository package now contains the implementation of our repository because the actual implementation also contains the direct access to our data just this interface definition we have here has hasn't much to do with getting something from an api getting something from a database because we just say hey these are the functions we have and the reason why we have an interface here at all is that is very helpful for test cases let's say you actually want to test a use case later that uses this repository then you don't want to run this test case for the use case with your actual repository implementation that uses the api because that would just take way too long you you use your api quota maybe your firebase crawler whatever you use you of course don't want that in a test case it takes a lot of time instead what you do for tests is you create fake versions of these repositories so you just have a fake repository that simulates the behavior of an api in this case and then this fake repository can also implement this coin repository interface so in the end our use case doesn't care what kind of repository it gets it just knows okay whatever i get it will have these two functions and that's all the use case really needs to know so let's go to our repository package in the data package create a coin repository implementation here and that will now have an inject constructor so we inject dependencies here which is simply our api instance so our paprika paprika coin paprika api actually this will now implement the coin coin repository of course we can press control i implement those two functions and here we can simply now return api that get coins and apr return api get coin by id and we pass the coin id that is already everything for the repository looks like a lot of boilerplate code here and here it is actually boilerplate code but the bigger your project gets the more helpful these repositories will be especially if you also have a database and you have some logic in there then these really make sense and i want to show you how i would actually structure a bigger app here so now we have our coin repository implementation what comes next next let's implement our actual use cases and these use cases we'll use that repository to access our api and basically forward that information to the view models so let's go to domain use case and let's start with getcoins so we create a new kotlin class called get coins use case that is kind of what i always call my use cases so first whatever they do and then just use case so we just need to look at it and know oh that's a use case press enter we inject our repository here so private valve repository of tap coin repository and take care that you now don't inject the coin repository implementation you want to inject the interface because that allows it to be easily replaceable and now one thing i like to do for use cases is to override the invoke operator function so first of all a use case should only have one public function and that is the function to execute that use case in this case to to get the coins in the other case to get the coin details to perform the search to update the user profile there's always one major feature that you expose to the view models that that a use case should contain so even though we could we we shouldn't put both our logics here instead of this single use case so to get coin details and to get um all coins that is bad because then the the purpose of a use case isn't fulfilled anymore so what i will do is i will have a function actually an operator function invoke and this won't take any parameters but it will give us a flow from curiens of type resource off type list of type coin and that looks very complex now it actually isn't so complex so let's declutter this we override the operator function invoke this is optional some people like to call it i don't know execute or so but with this if we override the invoke function we can basically call this use case as if it was a function and i like it then we return a flow because we want to emit multiple values over a period of time so first we want to admit that we are actually now loading use cases so we can display the progress bar we want to emit if it's successful and we want to attach our data our list of coins and if an error happens we want to emit an error here and that is what this resource will be helpful for so here we can emit success error loading one step at a time in our use case and the data that we would emit if we have data would be of type list of coins so if it was successful we just attach a list of coins that's the whole magic here so we can say that's equal to a flow we want to put that in try and catch so in case something goes wrong we want to on the one hand to catch http exceptions which will happen if we get a non um if we get a response code an http response code that doesn't start with a two so that usually means it was not successful and we're going to catch i o exceptions um this one this happens if if um our repository or api can't even really talk to our to the actual remote api for example if no internet connection or whatever so here in in the try block the first thing we want to do is we want to emit loading status because when we call this invoke function that means hey we want to execute that use case now we only get the coins and the first thing that should happen is we display our progress bar so we say emit resource that loading and that doesn't need any parameters so we will then receive that in our viewmodel and then in our in our ui and we can display the progress bar we will then have our list of coins that we get from our repository get coins and remember that is now a list of coin dtos we return a list of coins so if the of the object we actually display in our ui so if if that line did crash we know it was successful so we can say emit resource.success and here we now need to attach the data that we want to forward to our review model that contains the use case so this data will be our coins but as you can see we get an error because we pass a list of coin dtos and it expects a list of coins so what you want to do is we want to call a dot to uh rather we want to call that to map so we map all single coins in that coin dto list to a coin and then the error is gone then in here in the catch block we want to emit an error obviously resist that error and the message for that error will be e dot localized message and if that returns a null we can say an unexpected error occurred so that will just attach the exception message or the the error code and if that doesn't exist we just display this and if an io exception happened we say resource.error that usually means that there was no internet connection it could also mean that the server is offline but usually it means there is no internet connection um so we just say couldn't reach server check your internet connection and that is it for our use case so now we can reuse that wherever we like in all of our v models that need to get coins and we don't need to rewrite this logic all the time and if now let's say we work in a team and we get a new team member then all the team member needs to to do is take a look in the use case package see oh get coins use case and the team member will automatically know the logic that is in here will just deal with getting coins it's just a very clear naming and very clear structure to understand this project so i will now actually copy this use case paste it in our get coin use case and we will do the same just for getting a single coin so let's remove the s here and what do we need to change well we also want to return a flow with a resource but not with a list of coins instead with a coin detail we also want to emit the loading resource when we start making that api call we want to get the single coin using repository get coin by id and well which idea do we want to get here we need to pass that so we need to pass that id as a parameter to this invoke function pass this here and call that two coin detail so that is our ui object again and here we then pass the coin and the errors are basically the same and that is already it for our two use cases that we have here in our app next up we will implement the dependency injection setup with dagger hilt so what's actually missing we're missing the penalty injection we're missing the view models our two view models for each screen and then we're obviously missing the ui so let's jump into our di package and create our app module in case you're not familiar with the penalty injection why do we even do this the whole purpose of the penalty injection is it helps us to make our dependencies replaceable so dependencies in the end just an object in our app for example a database instance an api instance like we have here um whatever there can be a lot like a glide instance um croutine dispatchers all that are dependencies and the thing we want to avoid is hard-coding these dependencies into our objects so what that means let's say we open our get coins use case and we would do something like like this and we set this to a coin repository implementation and here we pass our api that we could also create in here this would be one of the worst things you could do in the end our use case would work because we have access to our repository but what if you want to test this this is not replaceable at all you can't replace this with a test repository with a fake coin repository because you hard code this in this class that it must be a coin repository implementation and that is what we want to avoid and to avoid that we use dependency injection so we inject the dependency from the outside from the module we will create now and in there we can easily swap this out we can say okay for this test case we want to use the fake coin repository and for the production app we use our code repository implementation so let's actually do that so as i said in the app module we define the dependencies we have and we tell daggerfield hey this is how you can create these we annotate this with module and install in we install it in the singleton component so that will just determine how long these dependencies will live installing it in the singleton component means these live as long as our application so these are all singletons then we need to create functions here that create our dependencies so we say it provides because the function provides something it provides a dependency and we annotate it with singleton so that will make sure that we only have a single instance of whatever the function returns so don't confuse it with singleton component that means all the dependencies in the module live as long as the application does and this makes sure that there is only a single instance throughout the whole lifetime of our app and we call this provides paprika api which returns an instance of our coin paprika api let's return retrofit dot builder so we now just return we tell dagger hilt hey this is how you can create such a coin paprika api we set the base url to something we don't have yet let's open our constants and create that here base url is https api coin paprika dot com and don't forget the last slash so we can assign that here constants.base url we can say at converter factory so we attach our g's and convert a factory.create we just make sure hey please use json to serialize and deserialize the json data we call that build oops and create so now we define the api interface that we want to create here which is coin paper kpi double colon class of java and that is it for the api next up we will have one more function that will provide our repository so again provides singleton function provide coin repository and well what do we need to create a coin repository we need our api coin paprika api and it will return a coin repository so now we can just say we return a coin repository implementation with our given api and because daggerfield knows how to create our api it will automatically pass that here as a parameter so we can easily create the coin repository implementation and here it's actually now fine to create that using this implementation class because we are inside of our production production app if we would be in our test cases in our test source set we would simply create a fake version of our coin repository and have a test module and in the test module it would simply replace the dependency that we have here and there we could then return the coin repository fake in that function so that is what dependency injection is is for and why it's so useful it's one of the best patterns you can use for any type of software project next up let's create an application class which we also need for daggerhild that will be in our root package and called coin application just a normal class that will inherit from application and this is basically just to give dagger hills the information about our application so dagger hilt also has access to the the application context if we need that for dependencies so we simply say hilt android app and that is everything we really need to do here we only also need to add this to our manifest let's open that in the application tag we say name coin application and this is um an issue because i copied stuff over here we can simply replace this with cryptocurrency app so without youtube and here as well because i just copied this over from my other project and this is how the app was named there it doesn't matter here just replace it close this close this and now let's create our view models so where are actually our view modes located um so initially new models contained business logic and they still technically do but they don't belong to the domain layer in the domain layer we only have the models and use cases and repository and possibly other definitions and the view models belong in the presentation layer because they are directly coupled to our views to our composables so here in our coin list screen we actually now create a view model which will be called coin list view model select file and i actually made a live template here for a hilt view model which you'd most likely don't have unless you saw the video from me about that just type this off we create a view model here called coin a list view model and we inject dependencies here if we inject penalties in the view model we need the special annotation as well and we need one dependency and guess what that is that is not our repository now we did that initially without clean architecture but since we now have our use cases we want to inject that instead so get coins use case of type get coins use case cool so now we have that and with this get coins use case we can easily now get the coins so we we don't have that in our repository anymore so what we want to do let's let's think about what do our view models now contain because while we moved most of our business logic from the view models to the use cases so why do we even have our view models now the main purpose of our review models is to just um maintain our state so we have your models because they of course keep the state the ui state even when we rotate the screen when a configuration change happens when we change the language whatever and that is still their job they just now contain less business logic they still contain business logic but less than before and for our single screen i want to create one state object that contains all the information we we actually need so do we want to show progress bar is there in our what is the coins list and that's basically it so let's create the object for that in our coins list package called coins coin list state i would say which will be a data class and that contains a val is loading which is a boolean and false initially it contains our coins which is a list of coin so this one here from our model package which is an empty list by default and it contains a potential error so a string which is equal to an empty string by default so we don't have any errors at all then in our view model we can create a state that contains such a coinless state which we will then expose to the composables for that i also have a live template which is called viewmodel state which creates a private and public version of that so that will just be called state coin list state and the initial value is just an empty coin list state with the default values the reason why i choose to have a private and a public version of that is that we actually own don't want to be able to modify the content of this state in in our composables so that is why we can only access this mutable state in the viewmodel and we exposed this immutable state to our composables just to make sure that only the viewmodel touches this because otherwise this would be not so good then it wouldn't be a separation of consonants anymore now we do want to have a function that actually calls our get coins use case and provides the data or rather puts the data inside of our state object to display that in our ui so private function get coins and what happens in here well we can simply call our get coins use case like a function you know that is a class but because we override it the the invoke function if we remember we have an operator function invoke we override that operator function and that allows us to call this use case like a function and i just like this so that now returns the flow that emits the resource values over time so we're going to call on each so on each element that this flow emits on each resource object we want to now do something i call this result and we say when result is a success from our resource we do this if it is an error we do this and finally if it is loading we do this let's start with loading in that case we just want to assign a value to our state we want to set it to um a coin list state with the default values but we set is loading to true if we get an error we set the stata value to coin list state um error is equal to resulted message and if that's null we say let's do that here and unexpected error occurred format that a bit and finally if there is a success case we say stated value is coin list state and we set the coins list to result dot data so a list of coins and if that's now just an empty list so all that function does is it executes our use case and puts the result simply into our state object that we will have for composables to just have a very readable code so we just see okay if it's loading we do this in our ui we show the progress bar if there's an hour we do this if it's successful we display the list right now nothing happens if we execute on each because it's a flow we also need to launch that in a curtain because flows are asynchronous so we say launch in and relaunch it in the view model scope and then let's execute this function simply here in init and just say get coins it's really that easy and that is everything we need to do now for our current listview model let's copy this via model and paste it in our coin detail package because now we're going to work on the coin detail view model so coin detail view model and well we need to change some things here first of all the use case we don't need the get coins use case anymore but if we would need it we could simply also inject it here so that is how we can reuse use cases but we need the get coin use case we just want a single coin and this one let's also copy our state here our coinless state and paste it in our coin detail package call it coin detail state it will also contain is loading and error but this will just be a single coin won't be a list of coins instead just a coin detail object and by default just a coin detail object with default values oh no we don't have default values here so let's let's just make it nullable and say is equal to null and then in here instead of a coin list state in our coin detail view model we have a coin detail state copy this paste it here here here and here replace this with coin is equal to result.data and this with get coin use case and this now takes a parameter which is the coin id how do we get that in here well this is where a super cool feature now comes into play and that is using the so-called saved state handle so i will write this here and then i'll explain it we have a private valve saved state handle of type saved state handle so the safe state handle basically is basically a bundle and it contains information about the the saved state so we can use that to restore our app from process death for example and the cool thing is it will also contain the navigation parameters so what we will do is when we navigate here when we click on bitcoin we will pass the id of bitcoin as a parameter to our details screen and in our details screen we will load that coin from that api with that id because we passed that as a navigation parameter that that id is contained in that saved instance state already a saved save state handle and that's really cool because now we don't really need to pass our parameter to anywhere we can just access it in our review model and load the corresponding coin here so we can simply rename this pass in the coin id here of tab string here as well and in our init block we say save state handle that get so we can now get a specific key it is of type string so or coin id and let's create a constant for that constval param coin id it's just coin id and then say constance that parent coin id we check if it's not equal to null give it the name of coin id and if it's not equal to null we simply say get coin and pass our coin id and that will work that is really really cool and we don't even need this here so we can just leave it like that all right guys now we actually have a state in our coin detail view model that will contain the corresponding data from our api when we visit the screen and we have the same state i mean a different state in in our current listview model that will also contain the data when it is actually available or the error or the loading status so now all that's missing is our ui so let's implement that next so the first thing we're going to implement from our ui is a coin list item which will be such an item here so just a composable for that and then we can create a list out of that very easily so in our coin list package components so since that is just a composable that doesn't make up a single screen it will be put in the components package and we will call it coin list item select file create a composable called coin list item and let's use two parameters on the one hand our nav controller to be able to navigate when we click on an item or i don't know let's not pass enough controller here we will actually just have a function that on a lambda function that gets called when we click on it and we can navigate one layer above so instead we need we definitely need the coin to display data of type coin and then we need on item click which gives us this coin as a parameter okay so we just have a row here i won't explain all that ui stuff here in detail i think if you watch this video you will know the basics of compose and if not i have a playlist about that so we'll just apply a modifier fill max with we say clickable in which we say on item click without coin and we say we give it a bit of padding let's say 20 dp then we set the horizontal arrangement to space between so just to make sure these two entries here these texts are pushed to the sides of our item and yeah and here we just have two texts the first one contains first of all the coin rank then the dot space then we have the coin name space open parentheses we have the coin symbol and close the parentheses so the coin symbol is for example btc for bitcoin we can attach a style here from our theme typography and that will be body one and we actually want to set the overflow to ellipsis so if the text gets too long we will just cut it off and then let's copy this now let's not copy this let's create another text here and this text will be if the coin is active it will be active and else inactive the color of this text will also be dependent on that if the coin is active we say material actually not material theme we just say color green and else colored red the font style will be italic so yeah it's just italic style and the text align with the end let's set the style to body two so it's a bit smaller and what's missing a modifier is missing that we actually align this vertically align center vertically and input that cool um that should be it for for the coin list item let's go to the coin list package create coinless screen corner screen and that will now take the nav controller and it will also take our coin list view model and we can we can get an instance for that using hilt v model and that will automatically just give us this viewmodel instance it's very easy so let's think about what do we actually have on that screen well it looks like we just have our list but we have a bit more in the end we just have a big box and if it is actually successful if it's a successful response we show the list if it is not we show an error just error text at the center for screen with a bit more setup you could also show snack bars and stuff like that of course but i keep it simple and of course also progress bar in the center of our screen which we can see very very shortly here if we load something it's very quick so let's actually say we first want to get a reference to the state from the view model your models take that value and then we have a box modifier fill max size let's open that we have a lazy column in which we display the list of items so modifier is modifier that film exercise as well and in here we say items and we import these items that give us a list here so we can just pass our state dot coins so for each single coin we can now map that to a composable to our coin a list item pass the coin here and on item click here um i will put that into separate lines when we click on a coin we want to what do we want to do we want to navigate nav controller navigate to our route which we don't have yet of our second screen so what i will do in let's let's do it in our presentation package i'll create a screen class a sealed class um called screen and this just helps us to deal with routes so just a route in the constructor so that is how we deal with navigation in jetpack compose and in here we have an object for our coin list screen inherits from screen and we pass coin list and a coin detail screen um coin detail let's actually call it screen and then we can actually pass that here so we want to navigate to screen coin detail screen that route and because it takes a parameter we need to append something here which is a slash and the coin um the coin id so let's just say coin.id this is it's basically just like a url so we just append a path parameter here to have access to the coin that we actually want to load in our coin detail screen then below this lazy column i want to check if the state that error is not blank so if it actually contains an error then we want to show a text this text will contain the error and the color will be material theme dot colors.error text align center and let's attach a modifier of modifier that fill max with padding of let's say 20 20 dp and only horizontally and we say a line in the center that should do it like this so that's it for the error that we display let's also do the progress bar so if the state is loading and you see just how readable that now is having such a state object with these single values in here if it's loading we say circular progress indicator and we make sure to align this in our center of the box and yeah that's already it for the screen so we can actually copy this coin list screen that's not copied yet we first want to write some components for a coin detail screen first of all i want to write a coin tag so composable for such attack and an item for a team member so let's start with a tag new coin tag which is a file composable coin tag what does that take it takes the tag in form of a string and that's already it here we can just create a box set the modifier to modifier um let's do it like this modifier dot border when i have a border here with 1dp color is material theme colors primary and the shape is a rounded corner shape of a very large dp amount and then we say we have like 10 db of padding and in that box we do have a text and that says just our tag then we have a color for that text which is our primary color we say text align center um like this and we say we have the style material theme typography.body2. so that's it for the coin tag let's create a component here for the team list item team list item which will contain the team member and an optional modifier here so we will essentially just have a column here what we're going to do is just such an item here just to text on top of each other basically the column will have our modifier as a parameter it will have vertical arrangement center and then we have two texts in here this one will be teammember.name the style will be material theme typography h4 have a little a little bit of space in between these two texts let's say 40p copy this text paste it down below this will show the positions of the founder blockchain developer whatever replace the style with body2 and i want to have this font style of italic again cool so that is it for the team list item now we can copy the the coin list screen let's just do it and paste it in coin detail call it coin detail screen of course and now adjust this this will now of course take the coin detail view model the nav controller i think isn't needed because we don't really navigate to anywhere from the screen and to get back we can press our back button we still get the state here and now let's think about it we have some fixed items here so it's not really a list of one type of item but it should still be scrollable in case it's it's a long list here of team members or whatever yeah we will have a lazy column here definitely so instead of that box we say state that coin which is a nullable coin detail element we want to make sure that it is actually not null get the information of that coin here put this in here so our lazy column and here let's let's delete that for now first of all let's build this top section here so this uh text and this active thing we can just use a single item block here to do that it's a row we did something similar here already but we will have a larger text modifier fill max width and horizontal arrangement of space between i also want to give our lazy column a bit of padding so we can say padding content padding is padding values of 20dp and then let's go inside of our row we essentially have the same type of text again as we had on the list so coin dot rank a dot space dot coin.name space open parentheses coin dot is not is active coin.symbol and close parentheses this time it's actually larger so let's set the style to material theme h2 and i'll attach a modifier actually a weight so this this won't really overlap our active text and the weight will be 8f then let's have the active text which is if is coin active it says active and else inactive color if coin is active color that green else color that red font style italic again text align will be end and a modifier to center this vertically and apply some weight align center vertically nope not this one this one import and attach a weight of 2f here so that will just be the top section here this bitcoin text and this active now we have a little bit of space and the description and another space so let's have another of these item blocks i think we can actually just put everything inside of a single item block let's do that so that is automatically treated like a column here so we have a row and below that a bit of space let's say height is 15 dp so now we have our description text text is coin description style material theme typography body two oops same space again and then let's have a text for the the text so text is equal to text let's set the style to material theme typography h3 i will choose here another spacer then i will use something called the flow row which comes from a dependency i included this one here from the accompanist library so flow row is basically a row that will wrap the elements if they exceed the bounds so all of our tags will be put inside of this row next to each other but if one tag gets too too long to fit on our screen it will just be put into the next line that is what the flow row does and that's not containing compose by default so we need a library i want to set the main access spacing to 10dp and the cross access spacing to 10dp so just the the spacing so how much space we have between items on the main axis and cross axis and a modifier of modifier fill max width and in here we can then put all of our tags we actually have so coin dot tags for each tag and for each tag we just have a coin tag and passing the attack as a value then let's have another spacer below this flow row and what comes next the team members text so we can basically just copy this text text because it's the same same styling call this team members have another spacer and now the the list for the team members won't be put in here because that is that could be multiple items so we go below this items block and have a real items block with multiple ones import the the one with a list again and for each team member um a coin actually has so let's say team member and yeah perfect for each of these we actually just have a team list item where we pass the team member and we're going to pass a modifier of modifier fill a max width and padding 10 dp and then i will rearrange that and below each team list item i'll just put a divider so we have these lines here and down here we have the same logic for our error and our loading bar so that is actually everything for this coin detail screen now the very last thing we need to do is to set up the navigation which is very quick so where do we put this i will create a components package in the presentation package so that will contain components that all of our screens could possibly share i'll put in navigation here let's let's not do it let's just put in the direction the navigation directly in main activity so here in our surface we have our nav controller which is remember nav controller and then we have navigation actually.navigation nav host pass in the nav controller and as a second parameter the start destination which is um simply our list screen screen coin list screen dot route we open this up and yeah if you're new to this for each single screen we basically have such a composable block we define the route which is some what is that the first route is screen coin listscreen.route in here we then just put the composable that the screen represents which is coin list screen that is already it let's copy this for the other screen replace it with coin detail screen coin detail screen um didn't i rename this no i didn't so go to coindetailscreen.kt rename this to coin detail screen and then we can say coin detail screen here and we import this coinless screen again this time from our here from our uh presentation coin list package and this will actually also take the nav controller here this screen needs a parameter and a navigation argument which is the coin id so we need to define that here we simply append a slash curly braces and in that the name of that parameter so write it exactly as i do here and one more error is an import error we can simply press ctrl alt o to fix that so if i'm not wrong that should be everything no not yet we need to annotate this activity with android entry point which just allows tiger hill to inject dependencies into this activity or in sub-composables which we need for the view model and i think that should now be it for our app no one thing i remember is we need internet permission let's go to manifest before i forget this user's permission internet for retrofit um yeah i would say we just launched this try it out i will launch it on my other device because for some reason i can't i can't enable airplane mode here on this device i mean i can but it still always uses wi-fi and for some reason i can't turn off a wi-fi here so i will launch my other device and i'll see you back when that is launched and successfully hopefully successfully built and also after we tried this out if everything works i will i will go through all of what we did again um just a quick recap so you you just don't forget or in case you you don't know what i did at some point i will just explain the rough structure again and why we do things like we do but yeah then that is pretty much it and i'm back we actually get an error here not not enough information to invert type variable t um it seems like it can't really recognize that in our code but what we can do is um we need to specify the type here of that resource so coin detail i think this should be enough if we put this here it says we don't need this but i think we actually do and it can also be that we need to do this in our other use case yeah still we also get this in our other use case so now we get coins use case let's do a list of coin here and let's copy this here here and here so it's it's just not really sure about the types here but then this should actually be fine could reach java check unit connection which this is unexpected because we do have internet connection um couldn't reach server maybe maybe the base url is wrong what did i type here api coin paprika.com good question what is the issue here let's let's relaunch that it will probably give us the same error i i will look into this and then i will tell you okay so i logged into it i added a logging interceptor here to retrofit and you see the request failed because of socket exception e-perm that is a common exception i don't know why it happens it's very easy to fix um we just need to uninstall and reinstall our application and then from then on it will work so that that just sometimes happens with retrofit when it doesn't work and then we change something and then this can be the reason so all we need to do is go to our emulator uninstall this app and reinstall it and launch it and then that should work so let's see there's our loading bar you can see and now we have the list of coins here in our app it's a little bit laggy in the emulator but on a real device it's not and no it's it's not laggy cool so what happens if we click on bitcoin then we get the data of bitcoin so it seems like everything is working let's choose maybe a longer coin um this one here you can see then it's simply wrapped so it doesn't overlap the active text cool cool cool so i hope you really enjoyed this i will now give you a recap of what what we actually did here and by the way let's also test the error case um so we've we already saw that with the error here but i turned off turned on airplane mode launching this and yeah this now gives us the error check internet connection could reach server so that works perfect perfect perfect so as a recap clean architecture consists of presentation layer that contains the ui and view models here the domain layer contains the business logic in form of use cases and model classes and interface definitions and we have the data layer which contains the repository implementations and the actual kind of data objects like a database like an api data transfer objects that could also be something like a model that you save in your in your db you would also put that in the data package and yeah then we have a common package with just shared resources here for our whole app di package for modules um we have these dtos data transfer objects which are the objects we directly get from our api so directly mapped from the response the json response to a data class we then have a mapper function to to map these two objects we actually show in our ui so these are quite huge we don't want to have that in our ui as an object instead we only want to have a lighter version of that the coin detail one and for that we simply map that to such a coin detail object then here in our use cases we overwrite the invoke function so we can execute it like a function in that case it now returns a flow it doesn't always need to return a flow it can also do something else you could also do the flow stuff in the repository so then you would just call a repository function in that use case but it could also be let's say you log you have a login use case then it's you can do the validation in here so you check if the fields are not empty you check if the password contains like at least eight characters and special characters and stuff like that this stuff would be done in a use case and then yeah there's not much more we just put in our ui in the presentation package in our viewmodels we just call the the corresponding use cases here that we inject in the constructor check what kind of resource that is and depending on that we update our state that will then be exposed to the composables to display that and that is it for this course i hope you really enjoyed this please please please let me know that below in the comments if this helped you um that would make me happy and you know that pushed this video this took quite a lot of work to make this easily one work day or even two work days to to make this app on this video here so that would be really appreciated if you could give this a like and comment and even share this with your android friends and because you actually watch this till the end there's quite a good amount of support you give me because the watch time is really um it really decides about the success of a video i will give you an exclusive 10 discount for my more advanced premium courses on my website so do check this out this the discount code is clean underscore architecture with that you will get permanent 10 off i don't publish this discount anywhere else so yeah because you're serious with android development you can also get my paid courses cheaper where you can also learn things like these and also of course if you want to learn more advanced things for free then just subscribe to my newsletter as i said in the beginning you will just get regular text content about android about kotlin development and architecture so really don't miss out on that and apart from that thanks for watching i wish an excellent day and i see you in the next video again bye bye
Info
Channel: Philipp Lackner
Views: 48,916
Rating: 4.9795222 out of 5
Keywords: android, tutorial, philip, philipp, filipp, filip, fillip, fillipp, phillipp, phillip, lackener, leckener, leckner, lackner, kotlin, mobile, use case, jetpack compose, mvvm, clean architecture, api
Id: EF33KmyprEQ
Channel Id: undefined
Length: 89min 44sec (5384 seconds)
Published: Mon Sep 06 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.