How to set Retrofit URL string from Resources - android

I've refactored some parts of my code which is written in Kotlin and tend to put url's in strings.xml, but when I want to point to the string in strings.xml file in annotation part of the Retrofit, I get the following error.
An annotation argument must be a compile-time constant
Here is my code:
interface SampleApiService {
#GET(Resources.getSystem().getString(R.string.sample_url))
fun getSamples(){
}
}
Could anyone please tell me what is wrong?
Found the answer in the following post.

As documentation states it compiles your application resources at build time. and Annotation processing takes place at compile time. So you see you cannot pass resources string to a annotation
The Android SDK tools compile your application's resources into the
application binary at build time.
instead create static string variable and pass it in annotation

Problem solved,
Tnx to Retrofit 2 - Dynamic URL
I had to use another annotation mark of retrofit.
New: #Url parameter annotation allows passing a complete URL for an endpoint.
Here is the result:
interface SampleApiService {
#GET
fun getSamples(
#Url url: String =
Resources.getSystem().getString(R.string.sample_url)
)
: Deferred<ArrayList<Sample>>
}

Based upon the example provided - one could as well just use a static URL. It would need to be annotated alike this, in order not to use any run-time values and be able to change it at run-time:
#GET("{path}")
fun getSamples(#Path("path") path: String) {}
Then one can load whatever String path from string resources, at run-time. When the base URL shall be changed, one may need to reconfigure the client. That is because this interface definition is being used by the annotation processor to generate the abstraction layer from it - at compile time already, not at run-time... when taking the complaint literal, it would have to look alike this:
#GET(Constants.SOME_PATH)
fun getSamples() {}
but there is little advance over just hard-coding that String, because it cannot be changed later.

Related

Set property type from specific class as parameter type

I have this sample data class:
data class Car ( var id: String )
Now I can create a fun as this:
fun doWhatever(id: String){}
My problem is that if my customer then tells me that Id is an int, I have to change it in both places.
So what I want is to set Car.id type as refence in fun doWhatever, something like this:
fun doWhatever(id: propertyTypeOfCarId){}
So I if the customer changes type, I only have to change it in the class.
I read all kind of posts, but I wasnt able to find the answer. Any idea how to achieve it?
If this isn't something you expect to be doing regularly, consider just using the refactoring tools the IDE provides. You code to handle a specific set of data, and if the structure of that data changes, you have to adapt the code to fit it. Baking in a bunch of "what if" functionality can add complexity, compared to just saying a String is a String and changing it if it ever needs changing, using the tools provided to make that as quick and easy as possible.
But sometimes you need to do this kind of thing, and Kotlin has some nice language features it can be worth using, like type aliases:
typealias CarId = String
data class Car(var id: CarId)
fun doWhatever(id: CarId){}
Two benefits here: the actual type is only defined in one place, so you can change that String to an Int without needing to change anything else - except stuff that relied on the ID being a String specifically of course
The other benefit is you're actually adding some semantic information by using that very specific type. That function isn't supposed to just take any old String - it's specifically meant to handle CarIds. It tells you what it's for, and that can make your code a lot easier to understand
(The function will accept Strings, because CarId is just an alias - an alternative name for a String - so it's not a way to enforce structure on your code, just a way to make it nicer to read and write. You can't define a new type that is a String in Kotlin unfortunately)
If the number of id types you support is limited, you can simply use method overloading:
fun doWhatever(id: String){}
fun doWhatever(id: Int){}
// etc.
Alternatively, you can use a reified generic parameter in your method to support any number of types:
inline fun <reified T> doWhatever(id: T) {
when (T::class) {
Int::class -> {}
String::class -> {}
}
}

How to cast String to Input<String> - Kotlin

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.

Best way to integrate MoShi and Retrofit 2 in an Android Studio project

I would like to know what is the best way to integrate Retrofit with MoShi on my Android Studio project.
First of all, I use the moshi converter of retrofit :
implementation 'com.squareup.retrofit2:converter-moshi:2.9.0'
I use the popular plugin "Json to Kotlin class" for the generation of my POJO :
https://plugins.jetbrains.com/plugin/9960-json-to-kotlin-class-jsontokotlinclass-
When I create a new POJO, I use the annotation "MoShi (Reflect)" :
It generate me a Pojo with fields like this one :
#Json(name = "image_url")
val imageUrl: String?
The problem is Retrofit2 seem don't like underscore in my fields name because I get null results with the underscored names.
Searching in the Github issues, I found a solution. It work if I use this annotation :
#field:Json(name = "image_url")
val imageUrl: String?
So my questions are :
Using the #field:Json annotation is the best way to use MoShi with Retrofit ?
If yes, how to generate easily a Kotlin class with this annotation (using the same plugin or an other) ?
As apparent from this part of Moshi's documentation: "There’s no field naming strategy, versioning, instance creators, or long serialization policy. Instead of naming a field visibleCards and using a policy class to convert that to visible_cards, Moshi wants you to just name the field visible_cards as it appears in the JSON."
The preferred way is for you to not use the annotation, but instead name the fields the same way as in the JSON. With Kotlin you can use backticks for names that wouldn't otherwise be valid, e.g.val `image-url`: String?
Of course you wouldn't want to be working with such names in other parts of your code, and for that you should have separate objects for describing the data as it appears on the back-end, and another object for how it should appear in your application. We call those DTOs (Data-Transfer Objects.)

AndroidStudio refuses to acknowledge inheritance hierarchy

This is with implementation 'org.json:json:20180813'
So I have a User class that allows itself to be translated to a json string for persistence:
data class User(override val name:String, override val creds:ICredentials) : IUser, isEncodable<IUser>{
override val isLoggedIn by lazy { creds is Credentials.Valid }
override fun encode():String{
val credsEncoding = creds.encode()
return JSONStringer().withObject { it
.key("type").value(this::class.java.name)
.key("name").value(name)
.key("creds").value(credsEncoding)
}.toString()
}
}
where withObject is an extension function:
fun JSONWriter.withObject(content:(JSONWriter)->JSONWriter) = content(`object`() as JSONWriter).endObject()
This seems to compile and work perfectly fine.
However, AndroidStudio marks it red and claims
Unresolved reference
None of the following candidates is applicable because of receiver type mismatch
public fun JSONWriter.withObject(content:(JSONWriter)->JSONWriter):JSONWriter! defined in [...]`
If I try to "cheat" and write it as
(JSONStringer() as JSONWriter).withObject { it
...
}
the error becomes a warning that
This cast can never succeed.
But apparently, it does.
Why is that? And how do I get rid of this "error"?
This is with implementation 'org.json:json:20180813'
That's unlikely to work well.
Why is that?
For the past 11 years, Android has a copy of org.json classes in the Android SDK. This includes JSONStringer. You can't have two classes with the same fully-qualified name, and the firmware always wins. Your copy of the conflicting class will not be used, with the firmware one used instead. And the firmware's API has not changed much in those past 11 years.
And how do I get rid of this "error"?
Remove implementation 'org.json:json:20180813'. Either:
Use the Android SDK's built-in org.json classes, or
Use the Android SDK's JsonReader and JsonWriter classes, or
Use a different JSON parser (e.g., Gson, Jackson, Moshi)

Is there better way for handle kotlinx serialization?

I use kotlinx.serialization on Kotlin native project, I a defined Super class for my models and all of the models extends from it.
I defined a function to called toJSON() for serialize variables and fields inside model that all of class models have it.
#Serializable
open class Model {
fun toJSON(): String = JSON.stringify(this);
}
And I created a subclass
class Me : Model() {
var name:String = "Jack";
}
but when I invoke JSON.stringify(this), IDE get a Warning to me:
This declaration is experimental and its usage must be marked with '#kotlinx.serialization.ImplicitReflectionSerializer' or '#UseExperimental(kotlinx.serialization.ImplicitReflectionSerializer::class)'
I paid attention and I used #ImplicitReflectionSerializer annotation while not worked.
Where is my problem?
This is discussed here. It's the particular overload you're using which is still experimental. So your options are either to use the other overload (which takes in a serializer) or to use one of the annotations mentioned in the error message. If you look at the answer to the question I linked (and the comments following it), you'll see it talks about using #UseExperimental and where it should be used.

Categories

Resources