https://kotlinlang.org logo
Title
u

ursus

02/26/2023, 10:21 PM
How do you keep your ktor client setup in common while having different engines per platform, while each platform has its config in the
engine { ... }
block? If it were somehow on the Engine object, then I'd get it, I'd just expect actual the Engine factory ...or do I have to expect actual both factory for Engine AND the engine block?
c

CLOVIS

02/27/2023, 8:32 AM
If we're only talking about the client, you don't need to expect-actual anything, the
HttpClient
constructor is in common
u

ursus

02/27/2023, 9:13 AM
but how do I pass it the platform engine, if not for expect actual?
c

CLOVIS

02/27/2023, 9:15 AM
Do you need to?
// commonMain/kotlin/Client

fun createClient() = HttpClient {
    install(Logging) { … }
    install(ContentNegotiation) { … } 
}
This works
u

ursus

02/27/2023, 9:16 AM
hm but what engine does that choose?
u

ursus

02/27/2023, 9:18 AM
right, but how do I then do platform config
say I want okhttp engine, but since reset of my android app already uses okhttp, so I want to share the engine
engine {
   preconfigured = ...
}
I think my issie is with the api, if it were a simple builder pattern, then I'd just expect actual a
actual createEngine(): HttpEngine
c

CLOVIS

02/27/2023, 9:20 AM
Ah, in that case you will need to expect-actual, yes. What you can do is to create an extension function on
HttpClientConfig<*>
which has your common configuration, and call it in your various actuals that add platform-specific config: https://gitlab.com/opensavvy/formulaide/-/blob/main/remote-client/src/commonMain/kotlin/Client.kt#L17
(
HttpClientConfig<*>
is the receiver of the
HttpClient {}
factory)
u

ursus

02/27/2023, 9:24 AM
I see, so the other way around, extracting the common setup and calling it in platform specific code
thanks!
btw any chance you know how to create a hierarchy of ktor client instances?
I could share what you just proposed, and just apply it to multiple instances but that would mean multiple instances of the interceptors etc. is there a way to share them?
(say you have authenticated and unauthenticated apis - with okhttp I'd have 2 clients, first has some common setup, seconds inherits the common setup and adds auth handling)
c

CLOVIS

02/27/2023, 9:34 AM
In the example I showed you what's in the common code is just a factory, if you want to create multiple client instances you can call the factory multiple times
u

ursus

02/27/2023, 9:35 AM
I know but those arent shallow copies of each other? the way
okhttp.newBuilder()
does
it will share the instances of interceptors, threadpools etc
c

CLOVIS

02/27/2023, 9:35 AM
I don't know if that's possible, maybe send a new message in the channel so other people see it (since that's essentially a new question)
u

ursus

02/27/2023, 9:36 AM
I did, no response unfortunately
the idea is basically to have multiple instances, but share stuff so the latter instance is cheap
c

CLOVIS

02/27/2023, 9:37 AM
Not sure what plugins you're using, but I doubt it makes any difference for normal plugins
u

ursus

02/27/2023, 9:39 AM
well, probably true, my interceptors in okhttp are trivial, but main thing is okhttp threadpools and all the internal resources, same things which make you want to cache the ktor httpclient instance, not create new one per call
c

CLOVIS

02/27/2023, 9:40 AM
Didn't you say you're using
preconfigured=
? In that case, you just have to pass the same object for both calls and they share the OkHttp instance
u

ursus

02/27/2023, 9:41 AM
well yes, hmm which means the engine is the heavy part, and I could just share that?
c

CLOVIS

02/27/2023, 9:42 AM
I don't know OkHttp, so maybe there's a limitation on their side, but my understanding of Ktor is that you can, yes
u

ursus

02/27/2023, 9:43 AM
I mean yes it will work, but whats confusing, is that there is already a concept of the shallow copy
you can
install(httpClient)
which in my mean read as apply all settings from the client to this which would then mean
val baseClient = HttpClient {
   install(DefaultRequest)
   install(ContentNegotiation)
}

val authedClient = HttpClient {
   install(baseClient)
   install(Auth)
}
but its the other way around 🙄
which would then scale beautifully, you just ask for the parents instance in the DI and provide a child one to the DI other way around youre hardcoding all the children in the parent...nope