Networking in KMM (KTOR) Part-3

Networking in KMM (KTOR) Part-3

Anuj Sachan's photo
Anuj Sachan
·Jul 30, 2021·

4 min read

Subscribe to my newsletter and never miss my upcoming articles

In this blog, I will share how we did the networking part in the KMM Platform and how we replaced (Retrofit to Ktor). So if you have followed my part 1 and part 2 where I had described how to merge your existing IOS and Android project and move DTO to the KMM folder.

If you have landed here directly and haven’t read the previous blogs, here is the link to it.

IS KMM production-ready? How we migrated our code to Kotlin Multiplatform Mobile! Part 1
I will discuss my thoughts about KMM as we have worked with the production-ready app in
mounty.co!medium.com

Move android code to KMM Shared Module| How we migrated our code to Kotlin Multiplatform Mobile!
Earlier, I wrote a blog about merging the existing iOS and Android App with KMM and share the code between iOS and…
medium.com

Before we start KTOR in our project, let’s talk about it!
KTOR is used for networking, One thing to note bout it is that there are two types of platforms KTOR provides KTOR client and KTOR Server. we are using KTOR Client to connect backend APIs.

KTOR Server is to write backend APIs. I will write soon about it. ;)

You can refer to the official documentation if you need it. Let's Start!
You have to add some dependencies on the shared `build.gradle` file

In Common main-

implementation("io.ktor:ktor-client-core:$ktorVersion")
implementation("io.ktor:ktor-client-logging:$ktorVersion")
implementation("io.ktor:ktor-client-serialization:$ktorVers..")

In Android main-

implementation("io.ktor:ktor-client-android:$ktorVersion")
implementation("io.ktor:ktor-client-okhttp:$ktorVersion")

In iOS main-

implementation("io.ktor:ktor-client-ios:$ktorVersion")

Now that all dependencies are set to go yet there is one last thing to do. Android’s HTTP client is different from the iOS’ HTTP client, In Android, we have the context to get the state of activity whereas in iOS there is no context.

We have to write some actual and expected functions that will resolve all platform’s conflicts in iOS and Android. It calls different HTTP functions in different platforms which you can define platform-specific.

In `commanMain` Platform class, Creates an Expect function :

expect fun httpClient(config: HttpClientConfig<*>.() -> Unit): HttpClient

In `androidMain` Platform class, Create an Actual function :

actual fun httpClient(config: HttpClientConfig<*>.() -> Unit) = HttpClient(OkHttp) { config(this)

engine **{** config **{** retryOnConnectionFailure(false)  
        connectTimeout(30**,** TimeUnit.SECONDS)  
    **}  
}  

}**

In the `iosMain` Platform class creates an Actual function :

actual fun httpClient(config: HttpClientConfig<*>.() -> Unit) = HttpClient(Ios) { config(this)

engine **{** configureRequest **{** setAllowsCellularAccess(true)  
    **}  
}  

}**

Now you can create a common service to make Network Call

class UserService {

private val httpClient = HttpClientBuilder.httpClient              

  suspend fun sendOtp(mob: String): ApiResponse<String> {  
   val url = *BASE\_URL*.plus("/authentication/v1/otp/send")  
   val sendOtpRequest = SendOtpRequest(mob)  
   return httpClient.post(url) **{** body = sendOtpRequest  
   **}** }  

}

Calling the service function from Android-

Coroutines.io { try {
val response = userService().sendOtp(mobileNumber)
data.postValue(Resource.Success(response.data))
} catch (e: Exception) {
data.postValue(Resource.Error(error = e.localizedMessage ?: ""))
}
}

Calling the service function from iOS-

UserService()
.sendOtp(mob: "+91${mUserPhoneNumber}") {
result,error in
if let result = result{
//get result

} else if let error = error {  
  //do for error  
}  

}

That is how you can implement a common networking Service in both Android and IOS App. If you have difficulty in getting this. My Sample App code is provided for beginners.

one thing to note if you want to do Network Error Handling From Server Response -

@Serializable
data class ApiResponse(

@SerialName("error")  
var error: Boolean = false**,** @SerialName("message")  
var message: String**,** @SerialName("data")  
var data: T**,** @SerialName("code")  
var code: Int = 0  

)

now handle requests like below. you can check KTOR error handling documentation.

Coroutines.io { try {
val response = userService.sendOtp(mobileNumber)
if (response.status.value in 200..204) {
data.postValue(Resource.Success(response.receive>().data))
} else {
data.postValue(Resource.Error(error = response.receive().message))
}

} catch (e: Exception) {  
    data.postValue(Resource.Error(error = e.*localizedMessage* ?: ""))  
}  

} return data

In the next blog, I will discuss SQLDelight and Setting Library in KMM for Networking and Session Management.

JetBrains is actively pushing multiple updates and seems to be very rapidly fixing issues.

Follow me:
Blog: http://www.maddeveloper.in
Twitter: https://twitter.com/AnujSachan72

About Mounty

Mounty is an online discovery and booking platform for camps, adventure activities & trips anchored by the largest network of privately owned campsites, adventures, and trips providers. We aim to make the travel experience fun and seamless. We do this through an amazing team of passionate travelers with years of experience and exceptional capabilities.

 
Share this