Why extension function is not visible in another module? - android

My Android project has two modules:
app
common
In settings.gradle:
rootProject.name='My project'
include ':app'
include ':common'
In my build.gradle:
implementation project(':common')
In common package I has StringUtil.kt with the next extension function:
fun String.isEmailValid(): Boolean {
return !TextUtils.isEmpty(this) && android.util.Patterns.EMAIL_ADDRESS.matcher(this).matches()
}
And in this class I can use extension function like this:
val str = ""
str.isEmailValid()
But in app module I has class
class RegistrationViewModel(application: Application) : AndroidViewModel(application) {
fun doClickRegistration(email: String?, password: String?, retypePassword: String?) {
val str = ""
str.isEmailValid()
}
}
But now I get compile error:
Unresolved reference: isEmailValid

If you do not specify any visibility modifier, public is used by default, which means that your declarations will be visible everywhere; (Source)
Since you didn't add any visibility modifier to isEmailValid it is regarded as public.
Please note that extension functions have to be imported.
import com.your.package.path.isEmailValid

In your app build.gradle add this:
implementation project(':common')

Related

Conflicting import, imported name 'NavArgs' is ambiguous - Jetpack Compose Navigation Raam Costa

I have two screen composable with destination annotations (PlaceListingScreen and PlaceInfoScreen with arguments), when I added new destination for AddEditPlaceScreen with arguments. This make cause error message Conflicting import, imported name 'NavArgs' is ambiguous. I have tried without arguments for AddEditPlaceScreen then work normally. In Previous I use version 1.1.2-beta. the app run normally with arguments of AddEditPlaceScreen but when I update the version to 1.5.11-beta it causing error.
Error in NavArgsGetter.kt (under build folder generated)
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.lifecycle.SavedStateHandle
import com.example.medisyclinics.presentation.destinations.AddEditPlaceScreenDestination
import com.example.medisyclinics.presentation.destinations.AddEditPlaceScreenDestination.NavArgs
import com.example.medisyclinics.presentation.destinations.PlaceInfoScreenDestination
import com.example.medisyclinics.presentation.destinations.PlaceInfoScreenDestination.NavArgs
#ExperimentalMaterial3Api
inline fun <reified T> SavedStateHandle.navArgs(): T {
return navArgs(T::class.java, this)
}
#Suppress("UNCHECKED_CAST")
#ExperimentalMaterial3Api
fun <T> navArgs(argsClass: Class<T>, savedStateHandle: SavedStateHandle): T {
return when (argsClass) {
AddEditPlaceScreenDestination.NavArgs::class.java -> AddEditPlaceScreenDestination.argsFrom(savedStateHandle) as T
PlaceInfoScreenDestination.NavArgs::class.java -> PlaceInfoScreenDestination.argsFrom(savedStateHandle) as T
else -> error("Class ${argsClass} is not a navigation arguments class!")
}
}
PlaceInfoScreen.kt
#Destination
#Composable
fun PlaceInfoScreen(
placeId: Int,
navigator: DestinationsNavigator,
viewModel: PlaceInfoViewModel = hiltViewModel(),
) {
..... the rest of codes
AddEditPlaceScreen.kt
#Destination
#Composable
fun AddEditPlaceScreen(
isEditMode: Boolean = false,
place: PlaceListing? = null
viewModel: AddEditPlaceViewModel = hiltViewModel()
) {
... the rest of codes
I solved this with data class as NavArgs and using params navArgsDelegate in Destination annotations for AddEditPlaceScreen. After I read the updated documentation and Sample code application I missed something new . Sorry...
data class AddEditPlaceNavArgs(
val isEditMode: Boolean = false,
val place: PlaceListing? = null
)
#Destination(navArgsDelegate = AddEditPlaceNavArgs::class)
#Composable
fun AddEditPlaceScreen(
val navArgs: AddEditPlaceNavArgs,
viewModel: AddEditPlaceViewModel = hiltViewModel()
) {
... the rest of codes

How to use interface in swift with Kotlin multiplatform moblie

I am trying to use interface in swift, but it unable to find the property
commainMain
interface ApplicationToken {
val accessToken: String
val refreshToken: String
}
iosMain
Platform.kt
lateinit var tokenProvider: ApplicationToken
HttpClient.kt
actual fun httpClient(config: HttpClientConfig<*>.() -> Unit) = HttpClient(Darwin) {
config(this)
engine {
configureRequest {
setAllowsCellularAccess(true)
}
}
install(Auth) {
bearer {
loadTokens {
BearerTokens(tokenProvider.accessToken, "")
}
}
}
}
Now when I am to access tokenProvider in my swift code. It cannot find. I am adding image please have a look.
I tried another options to create class and implement my interface and call the class
class getToken : ApplicationToken {
let accessToken: String = ""
let refreshToken: String = ""
}
_ = getToken()
But it gives me the error. I cannot paste it because I don't understand in xcode.
When generating code for extensions (e.g. fun SomeClass.extension()) and for global variables, like in your case, Kotlin creates kind of a namespace, related to the filename.
In your case your property should be under PlatformKt.tokenProvider.
Usually when it's hard to find how your kotlin code is visible on iOS side, it's useful to check out framework header file. It should be places somewhere around here(replace shared with your framework name):
shared/build/cocoapods/framework/shared.framework/Headers/shared.h
I prefer adding this file by reference in my Xcode project, so I can have fast access all the time:
Then you can easily search through this file for your variable/class/etc name.

Calling a Composable function from an android module in the same project

I have an android module (ComposeLib) as part of the same project as the app. It's just to test out using Compose from a library. In the Project Structure dialog I added ComposeLib as an implementation dependency of app.
build.gradle (:app) contains...
dependencies {
...
implementation "androidx.compose.ui:ui:$compose_version"
implementation "androidx.compose.material:material:$compose_version"
implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'
implementation 'androidx.activity:activity-compose:1.4.0-rc01'
implementation "com.google.accompanist:accompanist-appcompat-theme:0.16.0"
implementation project(path: ':ComposeLib')
...
}
Atoms.kt in ComposeLib consists of...
class Atoms {
#Composable
fun CounterButton(count: Int, updateCount: (Int) -> Unit) {
Button( onClick = {updateCount(count+1)},
modifier = Modifier
.background(MaterialTheme.colors.secondary)){
Text("Clicked $count times")
}
}
}
Then in MainActivity.kt I am trying to use CounterButton...
import com.example.composelib.Atoms
class MainActivity : AppCompatActivity() {
lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
val myComposeView = findViewById<ComposeView>(R.id.composeView)
myComposeView.setContent {
val counter = remember{ mutableStateOf(0) }
AppCompatTheme {
CounterButton( // <== Unresolved Reference!?
count = counter.value,
updateCount = {newCount -> counter.value = newCount})
}
}
}
}
As you can see in the lower left of the screenshot the app cant find CounterButton from ComposeLib.Atoms. Any idea why?
This code works if I put CounterButton() in the app in MainActivity, so it's not a Jetpack problem it's a build configuration problem.
I also tried qualifying the call to CounterButton every way I could think of (Atoms.CounterButton, public.example.composelib.Atoms.CounterButton, etc). Even code completion doesn't recognize it.
How do I reference a #Composable function from another module in the same project?
You've defined your Composable inside class Atoms for some reason, so this function should be called on a class instance.
It's totally fine to define composable functions without any classes, just like
#Composable
fun CounterButton(count: Int, updateCount: (Int) -> Unit) {
}
It's already in some package so I don't think any container is much needed. But in case you wanna add some kind of modularity, you can replace class with object, in this case you'll be able to call it as Atoms.CounterButton

Dependency Injection on Arrow KT

In Arrow Kt Documentation on Dependency Injection, the dependency is defined at the "Edge of the World" or in Android could be an Activity or a Fragment. So the given example is as follow:
import Api.*
class SettingsActivity: Activity {
val deps = FetcherDependencies(Either.monadError(), ActivityApiService(this))
override fun onResume() {
val id = deps.createId("1234")
user.text =
id.fix().map { it.toString() }.getOrElse { "" }
friends.text =
deps.getUserFriends(id).fix().getOrElse { emptyList() }.joinToString()
}
}
But now I'm thinking how could the SettingsActivity in the example could be unit tested? Since the dependency is created within the activity, it could no longer be changed for testing?
When using some other Dependency Injection library, this dependency definition is create outside of the class it will be used on. For example in Dagger, a Module class is created to define how the objects (dependencies) are created and an #Inject is used to "inject" the dependency defined inside the module. So now when unit testing the Activity, I just have to define a different module or manually set the value of the dependency to a mock object.
In Dagger you would create a Mock or Test class that you would #Inject instead of ActivityApiService. It is the same here.
Instead of:
class ActivityApiService(val ctx: Context) {
fun createId(): String = doOtherThing(ctx)
}
You do
interface ActivityApiService {
fun createId(): String
}
and now you have 2 implementations, one for prod
class ActivityApiServiceImpl(val ctx: Context): ActivityApiService {
override fun createId(): Unit = doOtherThing(ctx)
}
and another for testing
fun testBla() {
val api = object: ActivityApiService {
override fun createId(): String = "4321"
}
val deps = FetcherDependencies(Either.monadError(), api)
deps.createId("1234") shouldBe "4321"
}
or even use Mockito or a similar tool to create an ActivityApiService.
I have a couple of articles on how to decouple and unitest outside the Android framework that aren't Arrow-related. Check 'Headless development in Fully Reactive Apps' and the related project https://github.com/pakoito/FunctionalAndroidReference.
If your dependency graph becomes too entangled and you'd like some compile-time magic to create those dependencies, you can always create a local class in tests and #Inject the constructor there. The point is to decouple from things that aren't unitestable, like the whole Android framework :D

How to use TextInputLayout & TextInputEditText in Kotlin anko

Error:(63, 13) Unresolved reference: textInputLayout
Error:(64, 17) Unresolved reference: textInputEditText
I'm getting above error message when trying to add textInputLayout & textInputEditText in kotlin anko.
Below is my code -
private fun test(context: Context): View{
return with(context){
verticalLayout {
textInputLayout {
textInputEditText{}
}
}
}
}
Create a kotlin code file anywhere you wish. Put below code without any class declaration.
inline fun ViewManager.textInputEditText() = textInputEditText {}
inline fun ViewManager.textInputEditText(theme: Int = 0, init: TextInputEditText.() -> Unit) = ankoView({ TextInputEditText(it) }, theme, init)
inline fun ViewManager.textInputLayout() = textInputLayout {}
inline fun ViewManager.textInputLayout(theme: Int = 0, init: TextInputLayout.() -> Unit) = ankoView({ TextInputLayout(it) }, theme, init)
Hope this helps
EDIT : based on the comment of #A Boschman, anko is very new and there is almost no documentation. When I had the same problem and I search a lot all over internet but didn't find any documentation.
My Solution is based on below mention thread on GitHub - (there also no theoritical explanation)
https://github.com/Kotlin/anko/issues/205
https://github.com/Kotlin/anko/issues/264
TextInputLayout is in the Design Support Library, so you need an additional Anko dependency that gives you bindings for that library (you can find a list of all the different Anko packages here):
compile "org.jetbrains.anko:anko-design:$anko_version"
You'll also need the Design Support Library itself if you don't have it yet:
compile 'com.android.support:design:25.4.0'

Categories

Resources