hey guys with multi-module being the go-to organiz...
# library-development
b
hey guys with multi-module being the go-to organization structure more and more widely used, is there a way to create a single aar for Android that depends on other internal modules - essentially the "fat aar" concept? iOS makes this nice with the binaryTarget's with XCFrameworks
b
Curious to know what your use case is.
b
We have various internal modules that the desired “core” maven artifact depends on. Trying to publish one artifact for Android just like on iOS
b
Why do you prefer not publishing all the artifacts? For the consumer, transitive dependencies are transparent.
j
@bod i'm working with Brandon on the above, and we essentially don't want people to have access to the "internal" modules. We just have a multi module setup for build speed.
b
Interesting! But if you make a "fat aar", well, your internal bits will be inside too? It's basically the same thing, except split into several files rather than one.
j
Hm, not sure I follow. As an example, let's say we have a module called
sdk
. This is the root module that we want to publish to maven central.
sdk
has an
implementation
dependency on another module called
customer
.
customer
has a a public class. for example:
Copy code
public class CustomerRepository {
  public suspend fun fetchCustomer()
}
In this case, if we publish the `customer`module to maven central, anyone can use
CustomerRepository
, but, we only want our
sdk
module to be able to use it.
So i guess the difference is fat aar doesn't expose the apis of the internal modules through the sdk.
let me know if i'm totally wrong here 😃
b
if we publish the `customer`module to maven central, anyone can use
CustomerRepository
That will be true only if they explicitly depend on these internal artifacts, in addition to depending on your main one.
fat aar doesn't expose the apis of the internal modules
For this to be true you'd need to make the classes in question
internal
- I suppose this is possible. But then again, if they really really want to use
CustomerRepository
they'll be able too even if it's internal, via reflection (I think?) 😅
😅 1
j
For this to be true you'd need to make the classes in question
internal
- I suppose this is possible.
Well, if my
sdk
module has an
implementation
dependency on
customer
, then it won't leak the APIs of
customer
through to the consumer. So even if a class is public, it won't be expoed through. The only way to expose it would be if I had an
api
dependency on
customer
. At least, that's my understanding
To your point though, you can still access via reflection ha
@bod curious though - have you dealt with publishing a mutli module library to maven central?
b
Well, if my
sdk
module has an
implementation
dependency on
customer
, then it won't leak the APIs of
customer
through to the consumer. So even if a class is public, it won't be expoed through.
That's true if you don't make a fat aar. If you do, these Gradle considerations are lost. You have only one dependency and everything that's public inside will be visible to the app depending on it. That's why you'd have to make these classes internal when building it.
have you dealt with publishing a mutli module library to maven central?
Yes (working on Apollo Kotlin which has a bunch), although none of the modules are considered "internal".
👍 1
j
ahh you're absolutely right. and got it, since they're not considered "internal", that makes a lot of sense.
👍 1
c
In the end though, you cannot make something 'internal'. If your code depends on it, it has to be bundled (one way or another), and is therefore accessible by the client. That's true no matter which bundling strategy you use. For example, in Java, if you have a package-private visibility, anyone could create a class in the same package in their own JAR. And anyway, everything is always available via reflection. The only thing you can do is keep the module separate, use
implementation
to communicate that you don't want the internal API published to users, and document the internal module to tell users that they shouldn't use it directly. If you really wanted, you could use
@RequiresOptIn
in the internal module to make it even more explicit. This way, no one can access it accidentally. But if you try to fight the build tool, it will "fight back". There's a reason things are this way. For example, if you bundled your internal module into two of your libraries,
lib1
and
lib2
, a project that imported both would never be able to compile due to duplicate stuff in the classpath.
💯 3
b
@RequiresOptIn
is a good one 👍 Having a package named like
yourlib.something.internal
is also something we see a lot - while it doesn't prevent usage, it's relatively clear that importing it is a mistake
👍 2
j
Ah yeah, great suggestion around
@RequiresOptIn
- will definitely add that as another safe guard. Thanks for the input @CLOVIS!
k
Good timing of this thread. I’m in a very similar situation and was just coming here to ask a similar question, we are converting some of our code to an SDK for our client and wanted to know the best practice to “hide” the sub module contents from the client so they have to use our interface so it’s cleaner to use. Seems like that’s not so straightforward with multiple modules.
b
that’s not so straightforward with multiple modules
if your clients only depend on your main artifact, they won't be able to reference symbols from your other artifacts
j
But, the other artifacts would still be available on maven central, so technically they could add them as dependencies
1
If you don’t go the fat aar route
@Kev Thompson we just ended up publishing all of our artifacts to maven central but our docs only specific to add the root one as the dependency. 🤷
👍 1
b
Our docs only specific to add the root one as the dependency.
I'd say that's the way to go 👍
👍 2
k
@bod, "if your clients only depend on your main artifact, they won't be able to reference symbols from your other artifacts" Thanks for the feedback. Currently i am developing the SDK and the output is just the AAR's from the build process. I move those to the test projects library folder and i am importing the aar's from there. I have to include all 3 aar's ( one for each module ) which is resulting in being able to see all the artifacts for each module. if I dont include all 3 there are missing dependancies causing crashes.
Copy code
implementation files('libs/main-module.aar')
implementation files('libs/sub-module1.aar')
implementation files('libs/sub-module2.aar')
Are you saying this changes if when you host the library properly and I'd only have to implement the main-module. we're just investigating where we want to host while im developing the SDK its self.
b
You should test by publishing your sdk to local maven. Yes the client should only need to depend on the main module.
👍 1
k
Thanks, will take a look into getting a local maven set up.
c
implementation
means "this module has a dependency, but it doesn't want to expose it to its consumers".
api
means "this module has a dependency that it considers to be part of its own API". As long as you use
implementation
, your users cannot access it accidentally. They can, however, always use it if they really want to, no matter your packaging strategy.
👍 1