When you're building a library (non android librar...
# library-development
c
When you're building a library (non android library) and you want to include values in the build at compile time, what's the "goto" way to do that? Is it just a properties file or env vars? Basically... im a long time android dev (just getting into library dev) and I'm used to using
BuildConfig
to insert api keys and other values into my build to be able to access programatically. But in a plain' ol kotlin/java library there is no BuildConfig. I have seen some other libraries or plugins that attempt to be BuildConfig but in the regular library world but that just seems... off?
e
the usual JVM solution would be properties in resources, but that's not an option for multiplatform. what are you targeting?
c
not doing kmp actually. in this case I am doing an android library, but I don't want to use buildConfig since it gets exposed as part of my public api
e
m
gmazzo's plugin has an option to generate BuildConfig as internal but you might as well put your apikey in code since your lib is distributed anyway
c
yeah. i realize i have to put it in my code. i just dont want it to be part of the public api. BUT then like i might want an easy way (via cmd line?) to put in different api keys. so for when testing internally i have one api key, but then for my actual releases i want another api key
e
If you have a Context available in the library you can use a resource.
e
if you have an Android Context then you can use https://developer.android.com/guide/topics/manifest/meta-data-element for simple keys, and override it in your own test variant or app
c
my use case is that I have a library with release and debug build types. no other product flavors or whatever. when i release builds internally, I want to have api key A, but when i generate the artifacts for the public I want api key B. let me see if i can rig up meta-data-element for that use case. ideally I think what I want is to be able to specify in CI,
./gradlew assembleRelease --with-api-key B
and then locally (or by default) I'd have api-key A hardcoded.
e
I don't think that's a good idea, makes it too easy to publish the wrong type. IMO safer add an additional variant dimension for internal, and don't publish it externally
c
yep. but then that takes me back to using androids variant features. so while i know i can use that, for the sake of my curiosity i want to know how you would do this in a "regular" library where buildConfig or build variants aren't available.
c
gotcha. i will look into "properties in resources". thanks all for the pointers
just want to make sure im doing things the "right" way 😅
e
it's the right way for JVM libraries, but has some pretty terrible side-effects on old Android, that's why I linked to that Jackson issue (and is also the whole reason the joda-time-android fork existed)
👀 1
c
ah okay. now that link makes sense. i didn't fully understand it the first time. FWIW, the TL on my team has submitted a PR for this where he's using system props i.e.
val API_KEY = System.getProperty("apiKey", "default")
and then when we build via cmd line we would do
/gradlew mylib:assembleRelease -DapiKey="prod api key"
still going to take some time on monday to read into the options above
or actually. its vice versa, where it always defaults to prod, but you can override with the sandbox api key
e
that's not going to work, the build time system properties have no relation to the runtime ones
c
hm. it seems to work. there is some gradle-fu going on (im not on my work laptop) but something along the lines of tasks.withType { if (System.getProperty("apiKey") != null) { systemProperty("apiKey", System.getProperty("apiKey")) } }
e
ah. sure, if you only need it for a local unit test. but if it's a unit test then I'd just inject the API key, no need to have production code that has a fallback you never want to actually use in production
c
Got some time tonight to experiment a bit more just on my own. I was able to get this working as an option and its pretty straight-forward
Copy code
defaultConfig {
    val localProps = Properties().apply {
      val file = rootProject.file("local.properties")
      if (file.exists()) load(file.inputStream())
    }
    val apiKeyFromProps: String = localProps.getProperty("myApiKey") ?: "fallback-key"

    resValue("string", "my_library_api_key", apiKeyFromProps)
  }
then in my library code i can access it if i get a context passed into my sdk
context.getString(<http://R.string.my|R.string.my>_library_api_key)
er. i suppose that still sorta makes my api key part of my public api since the string resource will be available to the consumer app (i think)?
e
you can make resources "private" https://developer.android.com/studio/projects/android-library.html#PrivateResources but ultimately if it's bundled in your library, it's accessible to the consumer app
👍 1