How to cast String to Input<String> - Kotlin - android

I am using GraphQL Apollo client call and it generates files. So as a result I got this
val storeNumber: Input<String> = Input.absent()
Instead of regular string. So how can I cast parameter to Input<String> to avoid this error

I don't use Apollo, but found the source code of Input. It depends what version of this library you're using. If you're using an older version, to wrap (not "cast"!) a String as an Input, use Input.Present:
storeNumber = Input.Present(storeNumber)
Note, the term "cast" means promising the compiler that your existing instance is also an instance of something else. That is very different from converting or wrapping an instance of something.
If you're using a newer version of the library, you shouldn't be using the Input class at all. It's been replaced with the Optional class, in which case you would use Optional.Present(storeNumber).
If you want to figure this kind of thing out on your own in the future, try Ctrl+Clicking the function you're working with to jump to its source code. In turn you can Ctrl+Click the types of the function parameters. That would take you to the source code of Input so you could see how to create an instance.

Related

Is there a way to convert a string into literal code that you can use in Kotlin?

This might be a very silly question, but I am logging the methods that are triggered in my app as strings. When an issue is submitted, I would like to automatically input the text of the strings as parameters for methods. E.g:
For method:
fun assignPot(potType: PotType, ball: DomainBall, action: PotAction) {...}
I'd like to somehow call method:
assignPot(FOUL(2, BLUE(5), SWITCH))
From String:
"FOUL(2, BLUE(5), SWITCH)"
The only workaround I can think of is to split the string and create a when -> then function to get actual classes from strings, but I wondered if there's a more concise way for this.
This is not what you want to do. You should design your app in a way that prevents users from providing input similar to actual code.
However, you can achieve this. Complex parsings like this oftenly use regex-based approaches.
As you said, you should map your string part to class. If your PotType is enum, you can do something like
val re = Regex("[^A-Za-z\s]")
val reNumbers = Regex("[^0-9\s]")
// get classes
val classNames = originalString.replace(re, "").split(" ")
// get their constructor numerical arguments
val classArgs = originalString.replace(reNumbers, "").split(" ")
After that you can implement mapping with when expression. You probably will use some collection of Any type.
As you see, this sadly leads you to parsing code by code. Concise way to solve is to implement your own script compiler/interpreter and use it in your application :) That will later lead you to dealing with security breaches and so on.
If you are logging problematic method calls and want to repeat them immediately after issue is submitted, you probably want to programatically save the calls to lambdas and call them when you receive an issue log.

How to use Kotlin Symbol Processing (KSP) to populate an existing list

I've been playing recently with KSP and have managed to develop interesting capabilities (such as automatic recyclerview and view holder generators based on their layout id), and so far all is well.
However, I am now trying to do something different than creating files based on the annotations I design. Instead of creating files, I would only want to populate a list with the classes/objects annotated by me.
Example:
ClassA.kt
#MyAnnotation
class ClassA(context: Context): SomeBaseClass(context) {
override fun baseClassFunction() {
// custom code goes here
}
}
ClassB.kt
#MyAnnotation
class ClassB(context: Context): SomeBaseClass(context) {
override fun baseClassFunction() {
// custom code goes here
}
}
MyListAgregator.kt
object MyListAgregator {
const val classList: List<SomeBaseClass> = mutableListOf()
}
Where my custom KSP would do the following
Collect all classes/objects (the usual) that are annotated by my
#MyAnnotation
Create an instance of them with the appropriate
parameters (in this case just a context)
Add each one to the classList in the MyListAgregator
I can always get to the point of the class collection (step 1) but the rest is a complete mystery to me, and feels like KSP always expects to create code, not execute it? Perhaps I am incorrect on the later one, but I could not find anything related to that explained in the available documentation.
Another alternative I considered, but would rather avoid out of concern for any negative hit (performance, for example) is to actually modify that MyListAgregator file directly to include all the instances in the list, as if I had written them myself. However, I would still prefer to go with the previous option instead if it is at all possible.
First, you need to establish a ruleset that will be applied to the classes annotated with your annotation (symbol in KSP glossary). For example, they must contain one argument, that argument must be a member property and of type Context and must be subclass of SomeBaseClass. I suggest first look up for correct inheritance then look up for argument count and type.
You are still within reading and exploring all files with this symbol. Filtering based on this ruleset you will land with a set of classes at point 2.
Here, KSP can provide you with the interface to generate your code. However, KSP will not let you edit the source file, but generate new one based on your conditions. Here you have to write your implementation for the overriden function, by visiting it
You can preserve the output (newly generated classes at step 2) and generate your MyListAggregator object.

Android Studio 4.2.2 - Why debbuger doesn't evaluate parameter in a inner funcion?

Android Studio 4.2.2 evaluates a local and global variable, but doesn't evaluate parameter funcion when is inside a inner function.
Until the previous version this worked perfectly.
fun a(p:param) {
fun b(){
var v = p+1 // Here
}
}
Suppose that one try to evaluate the parameter p in the line with comment
// Here with Alt F8
The message in evaluate window is
Cannot find the local variable 'p' with type
This hurts a lot because it forces you to replicate the parameter as a local variable in each routine to be visible in the debugger.
var p = p
Has anyone noticed this? Is there any workaround?
Notice that Variables windows display a parameter with $ prefix, but it also doesn't work in evaluate window.
I've posted this issue in JetBrains.
First things first:
Is there any workaround?
Yes, bind the parameter p to a local variable inside b:
fun a(p: Param) {
fun b() {
val p = p
var v = p + 1
}
}
and it will all work as expected.
The root cause is slightly more convoluted.
Kotlin grew up on a language tool chain tied very closely to the IntelliJ language plug-in for Kotlin. The original JVM compiler was a code generator that consumed the data structures of semantic information used by the IntelliJ language plug-in for diagnostics, quick fixes, and so on.
This architecture was very good for providing language support to an IDE, but less so for building a fast batch compiler.
With Kotlin 1.5, the "IR backend" was enabled as the default code generator for JVM. It uses a more traditional compilation approach of gradually translating an abstract syntax tree (AST) to progressively simpler intermediate languages before outputting JVM byte code.
With this change, a number of new compilation strategies were implemented. In particular, in the old strategy, local functions were treated as lambdas bound to a local variable. Their free variables were recorded in the state of the allocated Function object at the declaration site. When the local function is called, it translates to a call to invoke on that function object. End of story.
In the new approach, local functions are lifted into private static functions on the same class as the outer function. Free variables in the local function are closed by parameters to that lifted function, and instead of recording them at the declaration site in a lambda object, they are passed at the call site as arguments.
In your example. the p is free in the inner function b, so an additional parameter $p is added to b.
While this works for "release builds", the surrounding tooling has not caught up until recently. The "Evaluate Expression..." mechanism has been hit particularly hard, as it's quite sensitive to the layout and shape of the resulting JVM byte code.
In this specific case, it was a matter of adjusting the mechanism in the debugger that maps free variables of the fragment to local variables at the breakpoint. With this change aimed at 2022.3, you should hopefully stop noticing this specific bug, and a host of other improvements as a new and revised version of the evaluation mechanism ships.

None of the arguments can be called with the agruments supplied with Gson

I have started working with a Kotlin Multiplatform library, and I make use of Gson on the android side to do some processes. Since I know iOS does not have support for Gson, I decided to create an expected function called convertToJson which has a return type of a class. However when I have the actual implementation, I get the following error:
None of the arguments can be called with the arguments supplied
This occurs at this line of code:
actual fun convertFromJson(json:String): ClassName {
return Gson().fromJson(json, ClassName::class) // this is where the error lies
}
I also have an actual implementation of a function called convertToJson which does not give me an error, so I am wondering why the above mentioned convertFromJson function gives me an error.
Any help or advice will be highly appreciated.
Gson is implemented in java and its fromJson accepts an instance of java.lang.Class as the argument, so you should replace ClassName::class with ClassName::class.java, because the former gives you an instance of kotlin.reflect.KClass.

Inversion Of Control New Object Creation

I am getting into Inversion of Control, specifically using Guice and RoboGuice for Android and I have a question.
I have a method call that returns a Resource (which is essentially an XML or JSON String).
public Resource getResource(){
// Some implementation details that call a web service and throw the result in a string...
String resource = ........
}
The Resource class is really just a wrapped String, so I figured it made sense to pass it in in the constructor, since it is an essential part of a Resource object.
public class Resource{
Resource(String theXMLorJSON){
...
}
}
A couple of questions:
How do I construct a new Resource in the getResource call? I would think that I want to use IoC and not call new in the method.
If another class takes a Resource in the constructor, how can I use the Guice container to construct it when I need a dynamic String at construction time? I just asked a similar question and believe there may be a specific way to handle this using Guice.
Thanks so much!
I think you may be misunderstanding something about dependency injection. You don't need to try to avoid using new in all cases... you primarily want to avoid using new to create anything that you might want to be able to mock out for testing, and it's generally best to allow the container to wire up any class that depends on such an object.
Your Resource class, though, sounds like a simple value object that you can easily create manually in any testing you do. It also doesn't depend on any kind of services... it just contains a String. So there's no reason to try to have the container create it.
The class containing the getResource() method, on the other hand, you definitely want the container to create, because you'd like to be able to use something that depends on that class in testing without having to actually call a web service.
Note that if you have a class with a constructor that takes both dependencies you want injected by the container and parameters that are only known at runtime, you need to create an intermediate factory of some kind with a method that only takes the runtime parameters. With Guice you can automatically create such a factory from an interface using the Assisted Inject (not sure if that works with RoboGuice, but it's easy to create such a factory implementation manually too).

Categories

Resources