I'm working on my first library (its an android li...
# library-development
c
I'm working on my first library (its an android library). It's definitely been a bit of a journey to get it "production" ready. on of my release readiness steps is trying to comb over all of my dependencies and whether or not they should be defined as api vs implementation. are their any hard/fast rules regarding api vs impl. should i just make every dep api?
j
Make everything
implementation
by default. Only expose
api
dependencies if you use declarations from these dependencies in your own public API (return types, function parameters, supertypes, etc.)
Basically you don't want to pollute your consumer's compile classpath by default
👍 1
👍🏾 1
💯 1
❤️ 1
o
What Joffrey said, plus: Use an API dependency for stuff that your users must use or will most certainly need in conjunction with your library. Assume responsibility for those API dependencies and document that they are part of your offering. Use implementation dependencies for everything else.
true 1
❤️ 1
m
d
API dependencies are also great when you want to split your project into multiple optional modules that are published separately. You can then create an aggregator module with API dependencies to all the other modules for users that want everything so they don't need to add a separate dependency for each of your modules. Immutable Arrays are published this way (see publishing/aggregator): https://github.com/daniel-rusu/pods4k/tree/main
❤️ 1
c
Interestingly. I changed a few `api`'s over to implementation and I had no issues running my sample app that exercises my library... BUT then I reverted all of my changes and I did run the gradle dependency-analysis plugin and 1. the `api`s that i had defined... it did not complain about them to change them to implementation 2. it changed kotlinx.coroutines to runtimeOnly from implementation 3. it changed two third party deps that i use in my lib from implementation to api... anyone have any ideas on those 3? not sure what i should do. listen to the plugin... or just take the approach of making everything implementation and only changing that when needed.
m
For 1. and 3. most likely you expose those symbols in your public API. You can enable KGP abiValidation and look at the dump files to investigate for any public symbol exposing transitive dependencies
For 2. this is very unexpected but hard to tell without more details. Did it give any additional context?
c
For 1 and 3... thanks. I definitely now see that i should probably get a "snapshot" of what my public api surface area is and that would probably show me where I'm going wrong. let me try to enable kgp abiValidation!
would i enable abiValidation on my library module directly or on the whole project? i guess just the lib
okay. i added
Copy code
kotlin {
    @OptIn(org.jetbrains.kotlin.gradle.dsl.abi.ExperimentalAbiValidation::class)
    abiValidation {
        // Use the set() function to ensure compatibility with older Gradle versions
        enabled.set(true)
    }
}
to
coollib
module. Then I ran
./gradlew coollib:updateLegacyAbi
and it generated a new file in coollib/api/coollib.api but the file is empty. hmm. im using kotlin 2.2.20 (worth mentioning because it says its experimental as of 2.2.0)
e
there's few legitimate cases for
compileOnly
and it's hard to imagine a case where it would make sense for kotlinx.coroutines (aside from weird cases like https://github.com/junit-team/junit-framework/issues/1914 where I disagree with their approach anyway)
oh
runtimeOnly
. that sounds like you don't actually need it, why is it a dependency?
e
Too lazy to look it up right now, but IIRC it recommends
runtimeOnly
for coroutines-android because it just uses a service loader (or something like that) to set up the main dispatcher.
💡 1
c
interesting. i guess i'll keep the rec for runtimeOnly. I'll keep trying to get abiValidation working. Weird that it only generates an empty file. On a whim... I tried
<https://github.com/Kotlin/binary-compatibility-validator>
and that worked. if anyone has any ideas on why the new one genreates blank... let me know!
maybe im hitting this
e
ah if you're using
Dispatchers.Main
then you will use
-android
at runtime on Android. that doesn't seem like a common thing for a library to need though, and definitely not great for multiplatform since there isn't a main dispatcher on native or jvm (outside of UI)
c
okay. figured out that i couldn't use the abiValidation thing built into kotlin because of this issue https://youtrack.jetbrains.com/projects/KT/issues/KT-78625/Kotlins-built-in-BCV-generates-empty-.api-files and if i upgrade to kotlin 2.3.0-beta01 then it's fixed. Onto my last issue!!! the runtimeOnly/Dispatchers.Main... It seems like from what @eygraber said, that using runtimeOnly is actually the correct approach here. Let me try to look up what benefits that actually has over using implementation. I've never used it before. And to ephemients point... let me look to see maybe I'm using Dispatchers.Main incorrectly. my library has some compose UI aspects to it, so maybe someone on my team wrote some Dispatchers.Main code
e
it's not necessarily incorrect that a library uses the Main dispatcher, but there should be no need in Compose itself
c
Note that
runtimeOnly
is not transitive, so if you're creating a library, your users will not have the library, even at runtime, unless they specify it themselves too.
today i learned 2
👀 1
m
That sounds... like a bug?
j
I'm also surprised 🤔 But yeah if it translates to Maven's
provided
that makes sense (and clearly the semantics is not the same, so it probably shouldn't translate to that)
c
Just checked locally and it is transitive at least within a single build, so I could be wrong about this, or it was a bug when I learned it and it was since fixed.
compileOnly
is not transitive though.
m
Yea,
compileOnly
makes sense since dependents do not compile against this
j
Yeah it makes total sense for
compileOnly
not to be transitive, because consumers don't compile your code
👍 1
e
runtimeOnly
translates to the
runtime
scope in Maven which has the same meaning