This question is specific for extension function of Kotlin used in Android development.
So Kotlin provides us capability to add certain extension behavior in to a class to extend the based class behavior.
Example: (take from my current Android project, for viewAssertion in testing with Espresso)
fun Int.viewInteraction(): ViewInteraction {
return onView(CoreMatchers.allOf(ViewMatchers.withId(this), ViewMatchers.isDisplayed()))
}
In my usecase, I can use it like:
R.id.password_text.viewInteraction().perform(typeText(PASSWORD_PLAIN_TEXT), pressDone())
All good, except this extension function gives the extended behavior to all Int objects, not just the View IDs in Android, which is not good at all.
The question is if there is any way to give context for this Int, like in Android we have #IdRes in Android support annotation for such given case above?
You can't differentiate between an Int from the resources and a normal Int. It is the same class and you are adding an extension to all the classes of the Int type.
An alternative could be to create your own wrapper of Int:
class IntResource(val resource: Int) {
fun viewInteraction(): ViewInteraction {
return onView(CoreMatchers.allOf(ViewMatchers.withId(resource), ViewMatchers.isDisplayed()))
}
}
And then us it like this:
IntResource(R.id.password_text).viewInteraction().perform(typeText(PASSWORD_PLAIN_TEXT), pressDone())
Related
I mean the method from the class View that allows to put a tag to an object of that class like this:
view.setTag(1)
Which would be the equivalent in Swift?
You can set tag for view this way.
view.tag = 1
I may be late, but the actual answer given by #Muhammad Afzal is not exactly equivalent to .setTag() in Android.
Swift .tag only allows the use of Int values, while Android .setTag() allows the use of any Object value.
To get an equivalent functionality, one should create a simple custom class like the following:
public class UITagView:UIView{
var Tag:Any?
init() {
super.init(frame: .zero)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setTag(_ tag:Any){
self.Tag = tag
}
func getTag()->Any{
return self.Tag!
}
}
If view is an instance of the class, then view.setTag() would be the the same in Android and Swift.
However, if the tags that you need are always Integers, it is easier to use view.tag, as #Muhammad Afzal said.
Note: As in Android, when recovering an object with the .getTag() method, it should be parsed to the class it belongs to (view.getTag() as! SomeClass).
Hello to all Android Developers, I need to clarify a doubt in relation to the management of dynamic resources in Android applications.
I need my application to use the translations returned by my backend depending on the language configured on the phone.
I wanted to implement it in an elegant way working on a custom LayoutInflater that applies a ViewTransformer depending on the type of graphic component.
Each ViewTransformer will only collect the identifier (for example #id/landing_welcome_text) and make the next call:
val value = attrs.getAttributeValue(index)
if (value != null && value.startsWith("#")) {
val text = view.context.resources.getString(attrs.getAttributeResourceValue(index, 0))
setTextForView(view, text)
}
A ContextWrapper has been implemented that returns my custom LayoutInflater and a Resource implementation
override fun getSystemService(name: String): Any {
return if (Context.LAYOUT_INFLATER_SERVICE == name)
CustomLayoutInflater(
LayoutInflater.from(baseContext),
this,
viewTransformerManager
)
else
super.getSystemService(name)
}
override fun getResources(): Resources = customResources
The problem is that overwriting the behavior of the Resources class is considered a deprecated strategy.
As the documentation says:
This constructor is deprecated. Resources should not be constructed by
apps. See Context.createConfigurationContext(Configuration).
class CustomResourcesWrapper constructor(
res: Resources,
private val languageStringRepo: ILanguageStringRepo
): Resources(res.assets, res.displayMetrics, res.configuration) {
#Throws(Resources.NotFoundException::class)
override fun getString(id: Int): String {
val value = getStringFromRepository(id)
return value ?: super.getString(id)
}
}
Does anyone know how I can get the same functionality without overwriting the Resources class?
Thank you very much for your help :)
I was looking into the same thing some time ago, in the end our team decided to go with Lokalise SDK.
From what I found out, overriding resources is the only way to do it. And even then it still doesn't cover all the cases, like mentioned in Lokalise documentation:
Some views are not supported when inflating from XML (Action bar, Menu
items, Android preferences, may be others), but you can still get the
latest translations via getString(), getText(), getQuantityString()
and other system methods, and set the content of these views
programmatically.
I saw a similar implementation in this library https://github.com/hamidness/restring although it wasn't nearly as complete as Lokalise. You can see how Lokalise is implemented if you include their library and switch to Project view in Android Studio, expand External Libraries and find com.lokalise.android, then you can see the decompiled .class files:
As for the constructor being deprecated - they deprecated it for the purpose of recreating the Resources when you need them for a different Configuration. But Context.createConfigurationContext doesn't let you override the source of the strings provided by resources, so I don't see any alternative.
In my Kotlin Multiplatform project, I'm trying to access Kotlin types defined in kotlin-stdlib from Swift.
TL;DR: StdLib types/methods seem not to result in header definitions, I'd like a solution that doesn't involve writing lots of boilerplate code
My scenario
I have an interface defined in Kotlin ...
interface MyKotlinInterface {
fun run() : Sequence<String>
}
... and implemented this interface in Swift ...
class MySwiftClass : MyKotlinInterface {
func run() -> KotlinSequence {
// return sequenceOf("foo")
}
}
... there I'm trying to create a Sequence but there are no methods from the kotlin.sequences package available (e.g. generateSequence).
Is it actually possible to access Kotlin framework types or methods beyond what I define in my code -- if yes, how? Furthermore, how can this be achieved without writing boilerplate code?
Further details
Having a look into the generated Objective-C header file, I see definitions for my class (obviously) and basic Kotlin types. What's missing is basically everything from the standard library functionality (I care for everything Sequence-related).
My build.gradle.kts looks like:
plugins {
kotlin("multiplatform") version "1.3.0"
}
kotlin {
targets { /* ... */ }
sourceSets {
getByName("commonMain") {
dependencies {
api("org.jetbrains.kotlin:kotlin-stdlib-common")
}
}
// ...
getByName("iosMain") {
dependencies {
api("org.jetbrains.kotlin:kotlin-stdlib")
}
}
}
}
Having the kotlin-stdlib defined as a dependency for the iOS target, I would expect those to become actually available from Swift.
Minimal working example
https://github.com/panzerfahrer/so-mwe-kotlin-mpp-swift
Current solution approach
The only solution I came up with, is writing the desired function for the iOS target:
fun <T : kotlin.Any> generateSequence(nextFunction: () -> T?): kotlin.sequences.Sequence<T> = kotlin.sequences.generateSequence(nextFunction)
This works ok-ish but is highly unsatisfying as it requires lots of boilerplate code. Additionally, extension functions cannot be made available this way and would require more boilerplate code or even rewriting parts of the standard library.
Desired solution
I like to avoid writing boilerplate code as much as possible. What I actually only care about, is to have (in my case) Sequence fully accessible from Swift. My feeling is, it would be sufficient to make the compiler generate selected or all header definitions for the standard library functionality.
Do you really need lazy computation (aka Sequence) in your Kotlin code?
If no, I would recommend using List<T> instead (and it maps to Swift directly).
For Sequence implementation, a workaround could be to export a factory function from your Kotlin library, e.g. you may declare a function like
fun <T : kotlin.Any> generateSequence(nextFunction: () -> T?)
= kotlin.sequences.generateSequence(nextFunction)
You may select any other factory function for Sequence, that matches your use-case.
In general, there are too many functions in the Kotlin standard library. Exporting them all to Swift will create too many useless symbols in the binary and increase the compilation time.
I'm a beginner of Kotlin, the Code A get a int value from resource file.
I hope to use an extension functions to do it, and invoke it just like this.getInteger(R.integer.ActivityEditBackup)
But Code B I made is incorrect, how can I fix it?
Code A
mContext.resources.getInteger(R.integer.ActivityEditBackup))
Code B
inline fun <reified T : Activity>Context.getInteger(int id): int {
return T.resources.getInteger(id)
}
You're overcomplicating it a bit.
You won't use the specific type of Context in any way, you don't need to make your extension generic.
In the parameter list, the name of the parameter comes first, and the type after.
The integer type's name in Kotlin is Int, with a capital I.
You can refer to your Context inside the extension function with this.
You can use support annotations to specify that your parameter is always an integer resource ID.
Overall, with these changes:
inline fun Context.getInteger(#IntegerRes id: Int): Int {
return this.resources.getInteger(id)
}
There was also some general confusion about syntax, you should look into the documentation for functions and then extensions.
Additionally, you can convert the function to an expression body and omit the explicit this:
inline fun Context.getInteger(#IntegerRes id: Int) = resources.getInteger(id)
I have been using Kotlin over Android for quite intensively. It does make programming fun again.
Still, in some cases (mostly util classes where the name should be short and handy), when automatically converting Java to Kotlin, I would love to have an option to use #JvmStatic on static methods rather than converting callers to MyClass.Companion.Bar.
That is, in some specific cases, it would be nice to have
public static foo(Barian bar)
converted to
#JvmStatic
fun foo(bar:Barian)
so I can maintain the short calling syntax from Java:
MyClass.foo(bar)
rather than
MyClass.Companion.foo(bar)
Obviously, in most cases I agree it's bad manners for many reasons such as future compatibility, non-Kotlin spirit and many more, but in a few cases it can keep Java code (that uses my classes) shorter.
You don't need to specify the Companion-namespace explicitly, when you decalre your "static" method like this:
class MyClass {
companion object {
fun foo() {}
}
}
In this case you still can call it via:
MyClass.foo()
But nevertheless having static methods is not a Kotlin-idiomic way and should be avoided by using other features of this language.