I think one advantage there would be typechecking, which I don't believe you would get with a typealias. What I mean by this is that IIUC using a typealias, a parameter of type "PhotoAlias" would actually accept any String (or whatever the primitive type is). With an inline value class, it's actually a new type, rather than an alias. Inline value classes also offer constructors/initializers for additional semantics, validation etc. In most cases, this is achieved without any overhead, meaning as with a typealias, at runtime you really just have a String (in this example, or whatever primitive type). So tl;dr it's a more robust, powerful and flexible option, and there shouldn't be any performance penalty, so I don't really see any reason NOT to use an Inline Value Class over a typealias, unless I'm missing something (which is quite possible 😅). Happy to learn by being corrected if I'm wrong or missing something. Inline value classes are new and TBH I haven't actually used them yet, but I think they're a great feature and I'm looking forward to using them shortly in a personal project where they are quite applicable. kotlinlang.org/docs/inline-classes.html
@@GrahamFirst typealiases, to me, should only be used locally (so i wouldn't rely on them for the scenario Philipp shows, i tend to agree with you in that sense). i find them great mostly for resolving occasional naming conflicts - eg. you are using kotlin.Result and some custom Result type alongside, and you don't want to always write the fully qualified kotlin.Result or com.myapplication.utils.Result to distinguish between them. creating an inline value class just for that purpose would be an overkill. so - to me - they're not interchangeable. it's apples and oranges. they serve different purposes and aren't meant as replacements for eachother. speaking of kotlin.Result, Result.success is actually an inline value class. so if you've got a failure, it's a specific class, but if you've got Result.success(true) (for example), it's actually just "true" coated with syntax sugar. there are some edge cases where it can lead to surprising results, eg. if you're using Mockito, you're not mocking some kotlin.Result property (so it stays null), but the compiler actually "thinks" it's a success, because it will treat it as Result.success(null).
destructuring declarations are nice, but relying on Pairs a lot is probably an anti-pattern in its own right. eg. if you have two values like (name, id), and if you implement it as a class, then you can always look for all the usages of "ThisClass.name" in the codebase. but if you're using a Pair, you cannot, because it will only find you the occurrences of Pair.first or Pair.second, and that might include lots of unrelated results from irrelevant contexts. or if you've got a Pair to represent name and id, and both are Strings, but then you realize you actually need to revert the order of name and id (id should be "first", name should be "second", because of some Map conversion), you've got no type safety when refactoring. you may forget about some place where it should be reversed, because both are of String type. so the code that will mistakenly use id as a name and vice versa is going to run and compile without any issues. this wouldn't happen if you had a normal class with expliitly named properties.
Hey Philipp, excellent work. I've been learning a lot from your videos, the explanation and implementation is great. I am relatively a beginner and now I am gonna learn network, APis and stuff. I came across your tutorials about Retrofit and Rest API but they are 2 years old now. So are they still relevant?
In Hack 3, I would remove the "val" from context so you don't hold onto it and bring in a memory leak, since context is only used to create the SharedPreferences object. Also, I would maybe put the Context extension function in a companion object of the delegate, but that is probably stylistic.
Don't forget to mention: when you use "by" keyword, compose compiler will recognize state as written, thus function will recompose, even if it just pass state to inner compose function
Typealiases are cool, however, the reader may think PhotoUrl is another class and get confused. Another approach using typealias that I'd prefer is explicitly making PhotoUrl is a typealias of string by PhotoUrl to PhotoUrlStr or better off, write a wrapper class that; data class PhotoUrl(value: String) // You can use another naming for value property tho Of course, those are my opinions
Hey man! Great suggestions, but I got one question regarding the third hack. Following your example, if I used this property delegate for a shared preference inside a view model for example, how would we test this view model? How could we mock the property delegate so the test is unit and we can avoid using roboelectric or something similar?
a ViewModel shouldn't deal with shared preferences directly AT ALL, because they are an Android concept, tied to the platform. they require access to Context. ideally a ViewModel should be unaware and independent of Android SDK.
Sir, Could you please tell me, how long it takes to build an app by learning from scratch using Kotlin and Jetpack compose ?.........Just tell me the time please!....
Hello bro, I used to watch your video from very early. I did a lot of research but still i am not able to find a solution to my query. I found a strechable ui in some of the professional app. For example consider the new playconsole app , it includes q strchable ui which make user feel that this is end of the screen. I saw this behaviour as defult after android 12 but in android 9 or 8 it does not. But still the app shows that strechable ui in android 9. Can you help me how to get so . Is this happening because playconsole is made using flutter ??? Hope you answer my question 😊
I don't think the setter and getter can be a suspend function which would be needed for that. You could keep a runBlocking in that property delegate, but it's not ideal IMO since that would not make it clear that it's a blocking call and should be executed on a thread
@@GrahamFirstbecause exceptions are for exceptional circumstances. Inability to upload a photo is not an exceptional occurrence. Failure is just an alternative success.
Hello Philipp, i got something that i find weird with Unit Tests and Jacoco, using the Result API does not cover me the 100% for some reason (a path o branch to test, but unclear) anyone or you could have an idea why, and maybe explain this?
or naming conflicts, like if you have to use two classes which happen to have the same name, but come from different packages, in the same file/class. typealias is a nicer alternative to using a fully qualified name (including a possibly lengthy package path).
Hack #1 & #2 : Some programmers will do ANYTHING to avoid properly documenting their code. Sigh. Hack #3: Is this *really* easier than using the standard sharedPreferences? Perhaps it IS if you're doing lots and lots of sharedPrefs reading and writing. But wait--why are you doing lots of sharedPrefs reading and writing? That alone is quite inefficient--code that should be re-thought. This has all the code smell of someone showing off all their esoteric knowledge of hidden details of kotlin instead of someone who truly wants their code to be understood by the next batch of programmers to look at it (and as usual, they are almost always the people with the least experience and the least ability to follow such subtle code tricks).
Typealiases are great, but I think using inline value classes would be a better approach
Could You elaborate on that?
I agree
I think one advantage there would be typechecking, which I don't believe you would get with a typealias.
What I mean by this is that IIUC using a typealias, a parameter of type "PhotoAlias" would actually accept any String (or whatever the primitive type is). With an inline value class, it's actually a new type, rather than an alias.
Inline value classes also offer constructors/initializers for additional semantics, validation etc.
In most cases, this is achieved without any overhead, meaning as with a typealias, at runtime you really just have a String (in this example, or whatever primitive type).
So tl;dr it's a more robust, powerful and flexible option, and there shouldn't be any performance penalty, so I don't really see any reason NOT to use an Inline Value Class over a typealias, unless I'm missing something (which is quite possible 😅).
Happy to learn by being corrected if I'm wrong or missing something. Inline value classes are new and TBH I haven't actually used them yet, but I think they're a great feature and I'm looking forward to using them shortly in a personal project where they are quite applicable.
kotlinlang.org/docs/inline-classes.html
@@GrahamFirst typealiases, to me, should only be used locally (so i wouldn't rely on them for the scenario Philipp shows, i tend to agree with you in that sense).
i find them great mostly for resolving occasional naming conflicts - eg. you are using kotlin.Result and some custom Result type alongside, and you don't want to always write the fully qualified kotlin.Result or com.myapplication.utils.Result to distinguish between them.
creating an inline value class just for that purpose would be an overkill.
so - to me - they're not interchangeable. it's apples and oranges. they serve different purposes and aren't meant as replacements for eachother.
speaking of kotlin.Result, Result.success is actually an inline value class. so if you've got a failure, it's a specific class, but if you've got Result.success(true) (for example), it's actually just "true" coated with syntax sugar. there are some edge cases where it can lead to surprising results, eg. if you're using Mockito, you're not mocking some kotlin.Result property (so it stays null), but the compiler actually "thinks" it's a success, because it will treat it as Result.success(null).
@@GrahamFirstgreat explanation!
Didn't know about the partition function, pretty cool, thank you!
The hack 2 was really helpful! I'm really relying most on Pairs, than creating another data class for it. Now it will be more readable
Agreed! And keep in mind you could use even more specific names than key/value for even greater readability.
destructuring declarations are nice, but relying on Pairs a lot is probably an anti-pattern in its own right.
eg. if you have two values like (name, id), and if you implement it as a class, then you can always look for all the usages of "ThisClass.name" in the codebase.
but if you're using a Pair, you cannot, because it will only find you the occurrences of Pair.first or Pair.second, and that might include lots of unrelated results from irrelevant contexts.
or if you've got a Pair to represent name and id, and both are Strings, but then you realize you actually need to revert the order of name and id (id should be "first", name should be "second", because of some Map conversion), you've got no type safety when refactoring. you may forget about some place where it should be reversed, because both are of String type.
so the code that will mistakenly use id as a name and vice versa is going to run and compile without any issues. this wouldn't happen if you had a normal class with expliitly named properties.
i dont like the 2nd hack. it can be achieved in a better way with a simple if else
It really blown my mind. I thought I've known everything. Thank you! 🎉
thank you for useful video. I have reduced a bunch of code in my project by only change sharedpreference
Amazing background, Phillip.
Bitte, bitte - mehr davon. Diese Art von Content ist großartig! (Please, please - more of the same. This kind of content is great)!
Hello, this video is really instructive. Would you mind to do one video going deeper abaout the 'delegates'?
Your channel is awesome, thank you!
Hey Philipp, excellent work. I've been learning a lot from your videos, the explanation and implementation is great. I am relatively a beginner and now I am gonna learn network, APis and stuff. I came across your tutorials about Retrofit and Rest API but they are 2 years old now. So are they still relevant?
@sackhaarspalter Ohh alright, I was actually confused which one to learn, thanks for help.
@sackhaarspalteryes ktor for multi platform, and retrofit for classic android
In Hack 3, I would remove the "val" from context so you don't hold onto it and bring in a memory leak, since context is only used to create the SharedPreferences object. Also, I would maybe put the Context extension function in a companion object of the delegate, but that is probably stylistic.
1. Typealias
2. Destructing
3. Delegates
Loved this one. Thanks again Philipp!
Excellent. Greetings from Brazil.
You are amazing, loved the hack3
Thank you! Would like to see more!
what theme are you using for android studio? looks super clean and that logcat cat is cuteeee
Lol, this hacks exploded my mind, excellent video, thank you!
nice video
the third is a great one
thanks for sharing!
are you spying on me or something?
your videos always point to the exact problems I keep facing in my work.
that's awesome.
Thank you Philipp 🤩
not bad not bad cool tricks. improved readibility
Another great video. Thanks.
good tips, thanks
Don't forget to mention: when you use "by" keyword, compose compiler will recognize state as written, thus function will recompose, even if it just pass state to inner compose function
Love from INDIA ❤
All of them were good hacks. I liked the Getter and Setter idea.
Delegate example is so useful 👌
Wow, thanks a lot for these tips
Typealiases are cool, however, the reader may think PhotoUrl is another class and get confused. Another approach using typealias that I'd prefer is explicitly making PhotoUrl is a typealias of string by PhotoUrl to PhotoUrlStr or better off, write a wrapper class that;
data class PhotoUrl(value: String) // You can use another naming for value property tho
Of course, those are my opinions
Inline classes fall somewhere in between those two
btw it should be val value: String actually ; ) (i know you're just illustrating the idea)
Hey man! Great suggestions, but I got one question regarding the third hack.
Following your example, if I used this property delegate for a shared preference inside a view model for example, how would we test this view model? How could we mock the property delegate so the test is unit and we can avoid using roboelectric or something similar?
Either way, you'd have a separate class which implements your preferences. That class is injected in your VM and can be replaced for testing there
a ViewModel shouldn't deal with shared preferences directly AT ALL, because they are an Android concept, tied to the platform.
they require access to Context.
ideally a ViewModel should be unaware and independent of Android SDK.
Can we get a video about uploading a photo to the back end and returning a url?
Sir, Could you please tell me, how long it takes to build an app by learning from scratch using Kotlin and Jetpack compose
?.........Just tell me the time please!....
it takes 64 days.
Wow!!! typealias is very useful!!!!!
Can we get data from firebase synchronizely in kotlin.can you make videos on it
great stuff!
awesome tips
hey philipp what happened to your newsletter? i suddenly stopped getting emails from you
very good, it's very amazing!!!!!!!!!
How about if return type of first example is an uri?
Third one was helpful
It's very helpful
Hello bro, I used to watch your video from very early. I did a lot of research but still i am not able to find a solution to my query.
I found a strechable ui in some of the professional app. For example consider the new playconsole app , it includes q strchable ui which make user feel that this is end of the screen. I saw this behaviour as defult after android 12 but in android 9 or 8 it does not.
But still the app shows that strechable ui in android 9. Can you help me how to get so .
Is this happening because playconsole is made using flutter ???
Hope you answer my question 😊
What about datastore ? Can we do like this with datastore?
I don't think the setter and getter can be a suspend function which would be needed for that. You could keep a runBlocking in that property delegate, but it's not ideal IMO since that would not make it clear that it's a blocking call and should be executed on a thread
I liked it bro!!!
Why return a Result rather than just a String (in the first example)?
Because Result allows you to return an error if the operation fails.
@@vibovitold Why not just throw an exception?
@@GrahamFirstbecause exceptions are for exceptional circumstances. Inability to upload a photo is not an exceptional occurrence. Failure is just an alternative success.
Thanks
Hello Philipp, i got something that i find weird with Unit Tests and Jacoco, using the Result API does not cover me the 100% for some reason (a path o branch to test, but unclear) anyone or you could have an idea why, and maybe explain this?
Doctor, my tummy is aching for some reason, do you have any idea why?
amazing
only use typealias for long java class names like : class ProjectContractChargingPeriodAccountReferenceVM :D :D
or naming conflicts, like if you have to use two classes which happen to have the same name, but come from different packages, in the same file/class.
typealias is a nicer alternative to using a fully qualified name (including a possibly lengthy package path).
Cool!
Great hacks!
Muito bom !
Humble brag, I knew all of these hacks.
goat
Hack #1 & #2 : Some programmers will do ANYTHING to avoid properly documenting their code. Sigh.
Hack #3: Is this *really* easier than using the standard sharedPreferences? Perhaps it IS if you're doing lots and lots of sharedPrefs reading and writing. But wait--why are you doing lots of sharedPrefs reading and writing? That alone is quite inefficient--code that should be re-thought.
This has all the code smell of someone showing off all their esoteric knowledge of hidden details of kotlin instead of someone who truly wants their code to be understood by the next batch of programmers to look at it (and as usual, they are almost always the people with the least experience and the least ability to follow such subtle code tricks).
nice hack3
Philipp plz build music player with content resolver and clean arch. If anyone think that will be so cool then like my comment 😄😄😄
code push in git
Hack 1 is useless, better to use a separate data class