TOA 9: Adding Hilt To Jetpack Compose Applications

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
happy wednesday everyone hi tracks hi never come hi everyone else who's here thanks grip coming back hope you all had a great week um yeah i don't have any fun announcements or anything to talk about today so i'm just going to move over this coding view and we can get started soon um i kind of have an idea what we're going to do but also not really um i think it makes sense today to continue working on the login screen that we've been working on for like three or four weeks now and in total probably like 10 hours we've spent on it um but i actually think it's aside from a couple design tweaks i want to make it's actually very close uh to what we want to do um let's go over to the code base and we can we could talk about where where everything sits in terms of login so the ui portion is pretty much done um we've seen we have a login screen with inputs and buttons um and over the last few weeks we've been working on the login view model um and that uh can i collapse this right how do i do this uh okay that was kind of close um we've got quite a bit done here already so we've got like our login view state that we expose and we have methods for like email change and password changed and login button clicked and this is actually where we spent a lot of our time last week is we played around with the error scenarios of logging in so like what happens if you have invalid credentials or some unknown error or you have empty credentials um which are actually most of our possible login results the only thing that we have yet to handle is the um success case if we look at login result here like these are kind of our high level everything we can do so we handled all the error scenarios last week the only thing we haven't yet done for our login screen is um the success case now i'm kind of debating whether we want to work on that today um i think that would be cool but it's also kind of hard to handle like a successful login if we don't yet have anything set up to happen after that like we don't have like a home screen not even a stub for it or anything yet um so actually what i think what i really think we should do is um we have not yet done any work to like hook up this login screen with the actual user interface um right now we are only well we're launching like the default main activity um so i actually think what might be good for today is we explore like how do we set up how do we actually create our login screen um and have that show up uh on the ui and then that kind of goes down a rabbit hole of like well how do we you know if we're creating the v model we got to talk a little bit about like dependency injection and i think that's what i want to do today is i want to set up the login view model with the ui um and then uh sorry i don't know if we'll do it today but what i want to do after all that i actually got a really good comment on a youtube video someone wanted uh like a recap of the code so far because we've been doing this for like five or six streams already there's at least eight videos of this app on youtube and someone wanted kind of a recap and i think we could do that but i think an even better way is i'd really love to sort of not so much a recap but document how this app is structured you know we did like an architecture diagram a while ago uh before we dove into the code and i'd actually like to update that and maybe spend some time just documenting how the login screen works and how that's going to be kind of a framework for all of our screens moving forward and i think that only makes sense to do or rather it's most helpful to do that after we've like connected the login screen to the ui because i think there will be some standards there uh that we need to talk about so i don't know if that makes sense i don't know if that sounds like fun i think that's what i want to do today let me know if there are objections otherwise i think i will um probably fill out these tasks in the same way so let me rephrase this um ui so i guess the way i look at this i say hilt i pop this up at the bottom left here um yeah i say hilt because you know many of you who are regulars know that i have like a a hate relationship with hilt but on the other hand while building a tool that i hope people can learn from i think that for certain things it's good to lean on the google recommendation which is hilt um but as i spend more time with it i start to get a little more familiar with it so uh what do you mean by pure di like building it ourselves um because that's an approach that i've also found interesting um that's how compose happened for me it is it is but i actually think that um yeah the the reason i like manual dependency injection too is that there's um like i saw a really good talk from sam edwards on do-it-yourself dependency injection he talked about like the ability to just go into a code base and click find usages on a dependency um and see like where it's coming from is really cool but with hilt you don't have that uh it's just magic because of all the annotation processing and magic's not a bad thing if you understand what's happening but i think that's the argument is that like you don't understand it just by looking at it you have to spend time like just learning how it works and then the rest of the code base kind of makes sense um but i think one of the reasons i kind of want to go with hilt uh actually let me talk about this so um i asked a question on twitter a while ago around uh scoping view models with compose and there was uh hey med how are you doing um someone pointed out a hilt like um that if you use hilts with navigation compose that you have this helpful like hilt view model which like creates a view model from the current backstack entry and i can see you can't read that not now um there's some stuff with hilton compose that i think would be really helpful and so maybe it'd be beneficial to kind of buy into hilt in the first place um so that's kind of my thought process so let's let's go in that direction before we do um i want to stick with the organization here so actually let's let's um write out some issues for what we want to work on today because i don't think i have a ton of these in here so say add hilt integration and i want to add the docs here so those are the hilt compose docs but somewhere is um don't want this um inject view metal objects with hilt maybe let me find the hilt android box even with no hilt you can do it yeah i think the i mean anything hilt can do we can do ourselves um it's just a matter of i guess well do we want to right um i guess this is technically like developer experience um but when we do it we're going to do it for the login screen first it's also yeah that's a good point um there's new issue uh what else are we gonna work on today uh we're going to well what i want to do more specifically i want to move login screen into main activity um we i don't think we'll talk a ton about compose navigation here but just here i'm actually gonna say don't worry about navigation just have the activity launch the login screen so we can test it yeah that's kind of my thought as um it helps this is an excuse for me to practice that library um and then i'm also going to put document app architecture so once the login screen is completed or at least the sign in flow um let's document the app architecture so that new hammers fans usually get up to speed get up these speeds faster okay and that will go in our developer experience project okay so those three issues that we made are what we're going to work on today um and let's start with the hilt integration so this is issue number 49 so what i'm gonna do is go over here um was it through here or maybe i want something to get how did i do this in the past why can't i find it that is i forgot how the ui works cause i always do this i do plan to try the compose destinations library when we get to it but i don't think that will come up tonight um okay so we're going to work on hilt integration so i'll i'll talk through this slowly um not necessarily to help everyone but because i need to talk through it slowly um but let's start with the obvious stuff um we need to add all of our dependencies so first we need the hilt android gradle plugin let's throw that in um build that create a file and yeah let's actually pull this out real quick um built version let's get that emulator out of the way sorry about that so we're adding the gradle plugin here um i've talked in the past i like to split up version numbers up here i also like to put in the release notes so it's easy to find where updates are when they happen um so let's figure out where the hilt releases pages here we go so we're just going to take this link to the hilt uh release change log and drop that in our uh gradle file here um oops sorry just have to keep an eye out on my messages right now um cool and after we add the gradle plugin i think we need to add the app dependencies so we have the hilt library but we also need the annotation processing and then make sure we apply the plugin so let's do all of that we're going to copy and paste our favorite things as developers um here and also because i pulled that out let's replace that hard-coded version with our um what is it our variable um oh and then we need to make sure we add this plugin so let's do that that happens up here and let's do a gradle sync and hopefully i set everything up correctly now hilt for anyone unfamiliar with it or unfamiliar with dependency injection in general um so let's start by explaining like dependency injection in general so it's a complicated word that i think captures a really whoops okay we have an issue with that gradle sink um oh could not find capped the reason for that is because um we also need to make sure we have the kotlin capped uh plug in so let's try that again um so dependency injection is yup that was a great cat chart fail um dependency injection is a complicated word for something that i think we all know like i i saw someone on twitter really break it down as like why do they call it dependency injection when what they really mean is passing things into the constructor and so that's the way i like to present it to people when we talk about dependency injection what we mean in this example is okay our login view model needs a credentials login use case this is a dependency this means that our view model cannot work if it does not have this use case so like that's the dependency right in the injection part as well how do i give that dependency into the viewmodel and so typically out of the box we see this when we like create a viewmodel factory and then we um when we create the viewmodel when we call the constructor we're supplying all these arguments and i think what the dependency injection libraries try to do is they try to eliminate all of that boilerplate and like hilt does that through like dependency or through annotation processing so like an example would be for this one we would basically annotate it hilt view model and then we annotate our constructor with that inject um and then that's all we need to change about the view model so what we're basically saying is that like okay hilt knows how to create the view model because we've annotated this with the like necessary annotations so hilt will generate some code that we don't get to see that creates this view model for us it's like a view model factory behind the scenes but now we have what you might sometimes hear referred to as like your dependency graph is that once you do this things kind of spider web out right like we've handled the first part which is hey we need to tell hiltz to create this viewmodel but in order for it to be able to do that it needs to know how to create the credentials login use case and uh question and chat so sometimes you can't even create the real dependencies like databases so you need fake dependencies that act like the real thing this is mocking up right i'm i'm not really sure what you mean like yes so in like a testing scenario we want to use a fake dependency but in a production case i want to be able to create the real dependency and i'm not sure why i couldn't create it um maybe what you're thinking of is with hilt there's kind of this uh pilt has two concepts for creating dependencies let's look at those in the documentation this might be actually similar to what your question is oh no i'm not talking about tests yet but that was a really good point but you brought up a good segue into an important uh clarification with how things work in hilt which are these two headings on the right so we see inject interface instances with binds and inject instances with provides so let's talk about like what that means so anytime you're supplying an interface um you can use the binds annotation from hilt so let's look at their example let's break it down we have an interface called an analytics interface or analytics service and it has these functions on it and then we have this concrete instance that has also noticed the at inject so let's assume whatever this depends on we've already defined and hilt and so hilt knows how to create this analytic service implementation so then when we want to create this we basically use the binds annotation and we say you know for this interface that we're returning this analytics interface we put as the parameter the actual implementation we want to create so anytime you have an interface and there's a sort of one-to-one mapping between the interface and the implementation you want you want to use the binds method where it gets a little more complicated is well what if it's not this simple what if it's not an interface what if i'm trying to create a retrofit instance and that's where the provides annotation comes in uh let me go back and let me point something out that actually i think might be helpful so this analytics module there's a couple things here there's the module annotation a module is a collection of dependencies then we have this install in annotation which think of this as like the scope so activity component is kind of like scoping it to the activity so if i have a new activity and i need an analytics service it will make a new one for that activity if i want to have one instance across the whole app then i would use like a singleton component um there are other scopes but basically this maps too well how long do these dependencies live it's a way to think about it and then what i want to point out is these are abstract classes this is an abstract class and an abstract function and that's basically necessary so that when hilt does its code generation it will generate like a subclass that overrides those abstract methods and implements them doing what hilt needs to do now in the case that hilt can't automatically figure it out say a third party dependency that's where this provides the annotation comes in and if we look in the example here like if we were providing a retrofit instance then we don't need an abstract class we could just create an object we use the provides annotation instead and then because it's not abstract we have to implement that function body where we then say here's how to create this now this can have its own dependencies if we need it um but yeah basically the difference between binds and provides is you know well mostly is it an interface um if it's an interface mapped to one single implementation you could probably use binds if you're creating something that's like a third party or like an actual instance of something else like that's where provides would come in so we'll actually see i don't know if we'll see both in this project um because we don't really have any retrofit sort of stuff in there yet um but we'll see now there is one other thing that i missed in here um let's go down the docks so we understood modules modules are groups of dependencies we looked at how we define the creation of those dependencies and we looked at with the viewmodel the hiltviewmodel annotation how we tell the viewmodel then it needs to consume certain dependencies there's a little more hilt setup hilt setup we need to do after we add these dependencies we must define an application class that is annotated with at hilt android app and this annotation triggers the code generation including a base class for your application that serves as the application level dependency container so this is actually i guess a benefit of hilt over its i don't know if we want to call it a predecessor but dagger was a previous dependency injection solution um and hilt is basically built on top of dagger but removes even more boilerplate that we used to have to make with dagger so i guess with dagger there was a lot of stuff you had to do in the application class but for hilt we basically just need this one annotation so actually let's add that and why this is interesting is because we don't have an application class yet so let's make one let's put it at the root folder we'll call it toa app basically we can just have this extend application and we don't need to put anything in the class body right now um maybe one day we'll come back and have other initializations but that's all we need to do to set up the app we also now that we've created our custom application class we do need to add that to our android manifest otherwise it's just going to use like a default application so where we do that is on the application tag of the manifest we do android name and then we can do dot toa app the dot is basically saying root directory and then toa is the class name so that's cool um let's see once hill to set up in your application class um the hilt can provide dependencies to other android classes that have the android entry point annotation so um here's an example okay so they're classes that support this so we use hillviewmodel to provide to the viewmodel we use android app on the application and then for all of these if we want to supply dependencies we just use the at android entry point annotation so i think we will need to add this to our main activity because we're eventually going to use that to create a view model so um and if you don't add it i'm pretty sure hilt will yell at you at some point um but let's put it on our main activity just in case we do need it cool so that's that's the basics of just adding the hilt library so let's go ahead and commit this real quick um and then what we'll do is we'll now that we've had this we need to talk about creating our dependencies and as you reuse things throughout the app you know this gets faster uh because you don't need to the idea is you only define your necessary dependencies once um but we haven't done that for anything yet so to do that i think a way i might walk through it is um i want to start at the smallest level and then sort of build our graph up from there and what i mean by that is well if we look at log and view model okay this has one dependency which is the credentials login use case that is an interface wrong annotation on activity aha good catch what is it android entry point great catch tim um so i talked about breaking uh breaking this down so what i meant by that was well sure login view model has one dependency which is this credentials login use case which is an interface but if we look at our prod credentials login use case as an example this is what we will use um this has its own dependencies which are like login repository and token repository now at this point [Music] we don't have any instances of either of these so this is where i would start i would start by creating a module and adding the necessary stuff to create one of these um repositories but in order to do that i need like a concrete instance um which we don't have yet because we don't have any sort of uh data logic yet um so what i might do is create like a i don't know what i would call it like a like i kind of want to call like a demo login repository that is just like provides demo responses for us um where do i want to put that i think i'll put it in this same folder so let's create a demo login repository real quick or well here's a naming convention that i think will fire up chat so typically so this is the login repository right and actually something i typically do is repository is what i made my interfaces and then the classes i suffix with the word service and this might be controversial but i like it so this is a sample login repository that does not interact with any real data source but allows us to quickly modify return values for testing manual testing sync um and then nope log in so like here let's just say return result that success login response can i just return like a default thing here i should be able to why is this yelling at me um token needs a string okay so let's split this up we've got like our default token we've got our default response and then that's what we can return inside success it's our default response okay so this is what our demo service does um i am basically going to do the same thing for the token repository um and so i'm copying that documentation but i'll replace this this will be token repository i spelt interact wrong let me fix that over here too um okay and basically same thing here so i guess for store token we can basically just say like noah and actually here let's just return null but we'll make this compile without failing but it will just return some default stuff here um cool so now that we have this concrete instance we can get back into the hill portion of things i'm going to go into at the root level i have this core folder i do want to like clean this up but um i'm going to put a new package in here called the eye and let's go over this so let's modules i mentioned are like a group of dependencies and because of the code generation there's really no hard rule about what should go into what module um i personally like to group them either by feature or maybe by like their type so what i'm thinking about here is actually like a repository module um and so what we would do here is so we would have an abstract class repository module and we need the module annotation we need to think about the scope um i think for these sorts of classes um i would think that we only need to create them once um i can't imagine a need for this particular thing to have like different use cases different um versions of it so i think i would just put this in singleton component maybe someone in chat has a reason why that's a bad idea but that's what i think i would do um and so for these because we're doing a one-to-one mapping between that concrete implementation and our interface we can do these through the binds method or the binds annotation we could say you know um bind token repository and more specifically this will be like as a parameter we put the concrete instance we want demo token service and then this will but this will return token repository ah choose tracks and we'll do the same thing for the um the login repository demo login service and this will return login repository okay so that's our repository module now the only other dependency that we are concerned with right now is that use case so let's create a use case module now for this one i also think that i can technically use a singleton component but here is where i think i might have it be like scoped to the activity um but each time i launch a new screen i might want like new use cases um i hope if they have stated it's a state of that that's relevant to the whole app yeah that's kind of what i'm thinking so like the token repository i could see that being something that like stores information locally like through preferences and because i'm going to get and read from preferences every time it's fine for that service to be a singleton and used around the app and i think use cases are probably the same especially because for this credentials one because the repos are never going to change the use case should never need to change um but i don't know if i i'm worried that if i put singleton component everywhere that's going to bite me down the line um but you know what let's go with it now and i think the benefit is like then it's created once and it doesn't need to be created again but that could be a problem if it's created once and it's never used again i don't know if making it part of the singleton component means it's going to stick around longer than necessary that could be a concern maybe something we can like look into um if we do like a stream of like performance monitoring or something we could think about that but actually what's nice is even though our so even though our prod credentials thing even though this has dependencies because hilt knows how to create this and knows how to create this it means it knows how to create this so i can still use the binds annotation here find credentials login use case and have this return credentials log in these cases so those are our modules i think i'm just going to run um a clean build and i want to make sure this passes but this might be enough for the hilt integration while it's running let's throw in some documentation so this module is responsible for defining the creation of any repository dependencies used in the application this module is responsible for defining the creation of any use case dependencies in the application note if this gets very large we may want to consider refactoring and making modules feature dependent so something to think about i can see that as this app grows i may not want a use case module um but i'm not going to refactor that yet i don't want to over engineer it uh i would rather rearrange modules in the future when it actually comes up so yeah if this get rid of that i forgot that was there so as long as this passes i'm gonna make a pull request just for this and then we'll do another one for um connecting vm the ui after this um much better for log independent modes but as always now that we're getting started with the stream before i can even i failed interesting um oops let's okay a lot going on here um what went wrong here okay i might need to let this finish and then i can scroll around the terminal and figure out what what went wrong um okay let's take a look let's figure this out um okay i'm starting at this line right here uh oh god why are you letting me scroll to the right is that because it's wrapping text so i don't need to okay so at hill components there's an error missing binding prod credentials log in use case cannot be provided without an inject constructor or an app provides annotated method so here's an example so here's what went wrong it's right here so i showed when we created the view model in the beginning we needed to annotate the constructor to the view model with at inject we need to do the same thing anywhere that we have a constructor of hilt dependencies um this was the i think the only thing they missed there um this is the only dependency uh but it does call out that you know if we don't want to do that or if we can't do that again because it's not our class that's where we would need a provides annotated method but here we could just annotate with that inject and i think that should do it for us and while that's running what i was getting at is we need to keep going with the stream history file so let's and we'll do that by setting up a new um we think what i do two things again so this will be stream six uh let's call this dependency injection and we'll update this a few more in this stream we implement the hilt library and set up dependency injection for the login screen hey this is october 13. um let's throw in built decks that's something that i think will be relevant um i'll add the prs after the fact i played it with street history nice all right that went pretty quick not too bad um we'll get that pull request going and then we can move on to the next thing i never tagged any of those to this ticket but that's fine um library and this is for work number 49 we'll call this check that we'll put this on the developer experience project even though i did work for the login screen with those dependencies i'll tag into developer experience um interesting that failed but that might have been [Music] from the old commit let's push our code that we've actually changed and then then we'll see how github handles this um kind of want to wait to see if it finishes before i move on but i also don't want to spend 10 minutes waiting so let's um interesting why did this fail locally is that one thing let's run it locally because that will be a little faster and i just want to make sure that this is passing as we expect before i move on to the next screen or the next portion of this um which is connecting to the ui that we have this and do i have an ticket for that already um nope i want to look at the onboarding that's still in progress yeah um bye so this we'll move that to in progress we'll do that next uh let's also go back to our dev experience board and we can talk about hilt integration move that to in progress as well okay this failed let's see if we can see why um maybe another issue here demo token service cannot be provided without an inject constructor oh interesting so this is the same problem as our credentials log in use case we talked about how we needed to add this at inject constructor i did not put that on the demo classes because i thought even though it was empty that we wouldn't need it but it seems that uh he'll wants it there no matter what even if it's an empty constructor uh that's fine let's add that um interesting i would have thought that if there was a noir constructor that helped would just generate a noir constructor but i guess that's fine um so hopefully this will pass uh quick plug while we're here um if any of you are not subscribed on youtube i'm gonna drop in a chat go do that we are uh like less than 50 people away from 1 000 subscribers which is absolutely mind-boggling to me we just passed 500 subscribers or 500 followers on twitch this week also another huge milestone um i'm not tweeting about it because last time i did i got distracted i waited months but i do have ideas of like some sort of 1 000 subscriber celebration on youtube when the time comes but i might be late on that one as well but i'm very excited for the ideas that i have well it's taken a little bit oh no this is running tests it's a brand new app that doesn't have a ton of code so a clean build shouldn't take that long and it looks like if it's running tests it's definitely working so let's just commit this and hope for the best injector the patience that remissed and then cool and yeah as long as this builds locally i'll move on to android mascot cosplay if i had a bug droid costume i absolutely would um unfortunately i don't have one i should reach out to google about that um see if they can send that to their their hard-working google developer experts um i always forget to push code happens every time i also might update our danger file to remove this comment because i'm tired of getting emails just from uh streaming what about room what what what happened with room what did room do to hurt you all right let's talk about the ui part here let's do toa 50 um login screen ui all right so uh something i know about before we do this so login content so i thought before i think i did a little bit of this last week but then i didn't get anywhere with it um for login content the reason i call this content is because this is basically what is displayed on the login screen it just consumes the view state and any listeners that it has i need to build type compared to store json that takes multiple objects or go with foreign keys my vote even though it's always more work is always to go with foreign keys i love database normalization but i also understand uh why you wouldn't although i have a youtube video on uh room relationships if you want me to send you that so you can learn more on like managing relationships with data and room i think that's really interesting um but so login content is just what's on the screen so then what i like to do is i like to pull this out into um yeah if you basically search my name it's not on my channel it's on the async android channel um and let me find it uh you all know i love my alliterations so um so of course it has a name of room relationship recap this is it let's happy i want the share icon without it playing because you'll be able to hear it let's get this brought this and chat oh yep there you go nice um yeah i basically walk through the different ways you can relate data in room and how to manage that so so where was i login content this is just what appears on the screen notice this has no nothing to do with the view model and that's on purpose this by having no dependency on the view model is very testable i can provide any state and callbacks i want inside of a composed test which we'll have to look at eventually for this project but we do need the view model eventually and so the way i like to do that is putting that in a different composable that i call the login screen so content is what's on the screen and screen is more to represent like the screen itself like the actual container of it i guess yes this this idea is like state hoisting that's what they call it yeah so here we'll actually consume a login view model so another way to think about it is like content is entirely like stateless or in fact that like it does not maintain any state itself it just consumes what is to be displayed and the login screen is where we're actually going to have that sort of state management so for example we're gonna put you know view state will be viewmodel.viewstate that collect that state and then now that we have this we basically now that we've posted this out i guess now we put in our login content and um so we'll have view state will be you state that value um an email changed we want to practice that over to the view model um and password changed again practicing that over to the view model um um login clicked model login clicks um and sign up clicked view model signup clips so this well we could try to write a test for it that would be really difficult because we'd have to like mock or create a real instance of the log and view model for testing's sake to avoid all that trouble we added this other composable that this becomes very testable because there's nothing in the um you know there's no android dependencies here yeah it's really nice so now that we have this um let's call this insider activity so what i actually think we could do for now because we're not worried about composed navigation um let's get rid of all the templity code and so first we need to get the view model um and how do we do this so we saw um i put the hilt compose libraries over here um so let's talk about this um so let's actually look there's a couple solutions to this so if we're not using navigation i think we can just use this equals view model um the view model function mentioned in the view model section automatically uses the view model that hill constructs um i think the reason we would want to add hilt navigation compose is when we want that to be scoped to the navigation graph um and so i don't know if i want to add that now or if i want to worry about this when we get to navigation and in fact maybe then i'm wondering if i can just do the first part effects that equals view model um i might need to add another dependency in order to get this hold on hilt view model integration so is this sorry hold on i want to make sure i type this right yeah equals view model the view model function mentioned in the view line aha here we go um or for this one it's for just this specifically it's life cycle compose life cycle view model compose um so let's add this i wonder why it's alpha one maybe that's a separate thing but let's um got it so important note here so this won't do the same thing as yeah so that's what i'm wondering if i should just add that even though i'm not using even though i'm not using it yet i'm wondering if i should just add this instead um let's do that but i wonder if then does this require me to put it inside a nav host let's find out let's let's ignore this for now let's go to our build.gradle file and let's add that dependency i prefer double quotes okay let's add this let's follow the docs and see what we get so so okay i don't need to be inside of the nav graph um so this is actually this is this is it um let's see if we can provide this as a default argument to make it even nicer okay i think we can um yep so then here we can basically just say login screen look at that our activity got down to under 30 lines let's run it and let's see what we can get let's while it's running let's go check on our pull request look at past thanks um so we'll squash that we'll squash that we'll go over to developer experience board and we'll move hilton integration to done
Info
Channel: Adam McNeilly
Views: 1,687
Rating: undefined out of 5
Keywords: Android
Id: dvOZep2hCqU
Channel Id: undefined
Length: 55min 54sec (3354 seconds)
Published: Fri Oct 15 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.