How to use kotlin.system with Kotlin Native? - android

I'd like to use system functions like getTimeMillis() which should be a part of kotlin.system: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.system/index.html
But the compiler says such module cannot be imported. The gradle configuration is like this (kotlin multiplatform project):
commonMain.dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-common:1.3.10"
implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.10.0"
implementation "io.ktor:ktor-client:1.0.0"
implementation "io.ktor:ktor-client-logging:1.1.0"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core-common:1.1.0"
}
Also I cannot find any example of usage or this module.

getTimeMillis() is only available for JVM and Native not for Common and JS.
If you but the call to getTimeMillis() in the source directory of the Native module the compiler can find the function.
If you need the call to be in Common you have to implement a Common wrapper function on your own and implement the wrapper on each platform yourself.
In order to do this create a stub function and a function which uses it in your common module . For example:
expect fun getSystemTimeInMillis(): Long
fun printSystemTimeMillis() {
println("System time in millis: ${getSystemTimeInMillis()}")
}
Then implement that function your platform specific modules. For example in a JVM module like:
actual fun getSystemTimeInMillis() = System.currentTimeMillis()
Or in a native module like:
actual fun getSystemTimeInMillis() = getTimeMillis()
See also: https://github.com/eggeral/kotlin-native-system-package

Related

Undesired R.java values when including Gradle modules via androidTestImplementation

In pursuit of "write once, run anywhere" integration tests, I am trying to move my test helpers into a Gradle module that can be included via testImplementation or androidTestImplementation. This helper module contains Espresso actions and assertions, which depend on View IDs and String resource identifiers from my app.
The problem -- When my helper module is included via androidTestImplementation, the module's resource references are only resolvable with the test context InstrumentationRegistry.context, rather than my app's InstrumentationRegistry.targetContext.
Is this expected? How else could I share resource reference-containing code between test and androidTest contexts, such that references always resolve to the target app context?
Sample code:
// :sharedtest Gradle module script
// Imports features in order to pull in their resource IDs
implementation(project(":features1"))
implementation(project(":features2"))
...
// Kotlin helpers inside :sharedtest Gradle module
import com.myapp.sharedtest.R
fun clickInsights() {
onView(withId(R.id.insights_tool)).perform(click())
}
...
// Integration test inside :app which
// includes :sharedtest module via androidTestImplementation
#Test
fun verifyClickInsights() {
// fails because com.myapp.sharedtest.R.id.insights_tool is not resolved as expected
clickInsights()
}
...
I explored Test Fixtures to house my test helpers, but seems AGP doesn't support them for Android Kotlin code at this time. I also considered source sets, but those have been deprecated in favor of Gradle modules.
I found success in changing my sharedtest helper imports to be child-module relative, i.e. com.myapp.feature1.R works, but com.myapp.sharedtest.R doesn't. But I'd like to
A) understand why this works and
B) avoid it if possible, since using sharedtest references everywhere is simplest codewise.

Room database.withTransaction { } block not Resolved

I am following this tutorial about RemoteMediator and everything is fine until I run into a weird error when using database.withTransaction{} to allow database operations.
Unfortunately, the IDE doesn't seem (or is refusing) to recognize this very genuine and
legit block. I have checked that I defined ROOM Database abstract class correctly and declared these ROOM and Paging 3 libs in build.gradle(app) file.
// Room
implementation "androidx.room:room-runtime:2.4.3"
kapt "androidx.room:room-compiler:2.4.3"
// Room-paging artifact
implementation 'androidx.room:room-paging:2.4.3'
// Paging 3.0
implementation 'androidx.paging:paging-compose:1.0.0-alpha16'
So I decided to ignore the error and went ahead to call a dao function inside the withTransaction{} block but I get Suspension functions can be called only within a coroutine body.
This is a bit confusing since RemoteMediator's load() override function is already a suspend function.
Anyone with insights on this issue please help as I don't seem to be getting anywhere around this.
Thanks to #ianhanniballake day-saving comment above, if you run into this situation just change the room-runtime flavor from:
implementation "androidx.room:room-runtime:2.4.3"
kapt "androidx.room:room-compiler:2.4.3"
to room-ktx flavor:
implementation "androidx.room:room-ktx:2.4.3"
kapt "androidx.room:room-compiler:2.4.3"
As per Developers Docs ktx-libs provide Kotlin Extensions and Coroutines support which is exactly what database.withTransaction{} block is - it is an extension function on RoomDatabase as shown on this line from the source code.
public suspend fun <R> RoomDatabase.withTransaction(block: suspend () -> R): R {
Android KTX is a set of Kotlin extensions that are included with
Android Jetpack and other Android libraries. KTX extensions provide
concise, idiomatic Kotlin to Jetpack, Android platform, and other
APIs. To do so, these extensions leverage several Kotlin language
features, including the following:
Extension functions
Extension properties
Lambdas
Named parameters
Parameter default values
Coroutines

Android: Add maven-publish configuration in a separate kotlin dsl script

I've written a .gradle script named publish.gradle which configures publishing {} for releasing my artifact.
Why on a separate script? I have multiple modules and by doing this every releasable module simply defines some variables.
Module build.gradle.kts:
// Module's blah blah
apply(from = "../publish.gradle")
publish.gradle:
apply plugin: 'maven-publish'
publishing {
publications {
// configure release process
}
}
I've recently decided to migrate to Gradle Kotlin DSL. However, there's an issue:
Adding publication {} like this:
plugins {
`maven-publish`
}
publication {
}
Lead to this error:
Expression 'publishing' cannot be invoked as a function. The function 'invoke()' is not found
Unresolved reference. None of the following candidates is applicable because of receiver type mismatch:
public val PluginDependenciesSpec.publishing: PluginDependencySpec defined in org.gradle.kotlin.ds
Which is summarized to
PluginDependenciesSpec is not present as a receiver
What is the difference?
TL; DR
I've added publishing {} config to a separate script which works when in .gradle groovy format but I can not convert to .gradle.kts kotlin format. The publishing is extension of PluginDependenciesSpec class which is not present in the script.
Here's what worked for me:
plugins {
id("maven-publish")
}
configure<PublishingExtension> {
publications.create<MavenPublication>("myPlugin") {
groupId = "com.myCompany.android"
artifactId = "MyPlugin"
version = "1.0.0"
pom.packaging = "jar"
artifact("$buildDir/libs/MyPlugin.jar")
}
repositories {
mavenLocal()
}
}
I understand where you're coming from, converting from groovy to kotlin script is not a simple one to one translation, and most of us, including myself, code by example. In other words, you just need to see a simple example and you can figure out the rest. This works great when examples are readily available. What you need to do when you don't find an example is to turn to the API document. For example, https://docs.gradle.org/current/dsl/org.gradle.api.publish.PublishingExtension.html shows you the available properties for the PublishingExtension and you can keep drilling in to see what properties you have at the next level. This is especially important when examples may be working with an older version and may no longer be applicable. I will say that it wasn't as obvious is that for accessing extensions in kotlin script, requires the configure block. That's a big difference, but I like that approach, because it makes it clearer what the extension properties are a part of. And by the way, kotlin wants double quote, single quotes are no longer acceptable.

Koin - Ensure that all "by inject" are valid

I work on an Android project using multiple Project Flavors, and we use Koin to inject the appropriate dependencies based on the current flavor.
We already use the checkModules Gradle task (described here : https://start.insert-koin.io/#/getting-started/testing?id=checking-your-modules) in order to ensure that our dependency tree is valid.
However, it seems that there is a use case missing.
Let's say I want to inject an InterfaceA in my Activity. I would write the following code :
class MyActivity : Activity() {
private val interfaceA_Impl: InterfaceA by inject()
...
}
Koin requires the implementation of InterfaceA to be provided in a module, as such :
val myModule = module {
single<InterfaceA> { MyInterfaceImpl() }
}
In my project, each implementation is "flavor-specific".
My question is :
Is there a way to ensure that ALL by inject targets are valid ? In other words, to ensure that all interfaces that I am trying to inject have valid implementations ? Currently, if an implementation is forgotten, the app crashes during runtime, and I would like to know about it sooner (maybe during unit tests, at the same time checkModules is ran ?)
Thanks a lot !

Implementation of Compiled code is hidden Kotlin Android

I've been trying to write a library in kotlin and once the library build is generated as aar file when opening it in Android Studio it is not showing me how my class exactly looks and how the methods are implemented in that.
// IntelliJ API Decompiler stub source generated from a class file
// Implementation of methods is not available
package com.mypackage.dexter
public final class Test private constructor() {
public final fun testMethod(): kotlin.Unit { /* compiled code */ }
}
I've tried Java decompiler plugin in android studio, Kotlin Byte code (This also doesn't show it in the same format that we get if the same code is written in Java). The ultimate thing that I wanted to achieve by looking at the code is that I want to use debugger in the library and evaluate things while running my application where this library is integrated.

Categories

Resources