I'm trying to create my first KMM project ( using Ktor to get a simple query ). I've managed to run it in Android, but each time I try in iOS i got an exception when i use Ktor.
This is my gradle "Shared"
plugins {
kotlin("multiplatform")
kotlin("plugin.serialization")
id("com.android.library")
id("com.squareup.sqldelight")
}
kotlin {
android()
listOf(
iosX64(),
iosArm64(),
iosSimulatorArm64()
).forEach {
it.binaries.framework {
baseName = "shared"
}
}
sourceSets {
val commonMain by getting {
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.1")
implementation("io.ktor:ktor-client-core:1.6.6")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:1.3.2")
implementation("io.ktor:ktor-client-serialization:1.6.5")
implementation("com.squareup.sqldelight:runtime:1.5.3")
}
}
val commonTest by getting {
dependencies {
implementation(kotlin("test"))
}
}
val androidMain by getting {
dependencies {
implementation("io.ktor:ktor-client-android:1.6.5")
implementation("com.squareup.sqldelight:android-driver:1.5.3")
}
}
val androidTest by getting
val iosX64Main by getting
val iosArm64Main by getting
val iosSimulatorArm64Main by getting
val iosMain by creating {
dependsOn(commonMain)
iosX64Main.dependsOn(this)
iosArm64Main.dependsOn(this)
iosSimulatorArm64Main.dependsOn(this)
dependencies {
implementation("io.ktor:ktor-client-ios:1.6.5")
implementation("com.squareup.sqldelight:native-driver:1.5.3")
}
}
val iosX64Test by getting
val iosArm64Test by getting
val iosSimulatorArm64Test by getting
val iosTest by creating {
dependsOn(commonTest)
iosX64Test.dependsOn(this)
iosArm64Test.dependsOn(this)
iosSimulatorArm64Test.dependsOn(this)
}
}
}
android {
compileSdk = 32
sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
defaultConfig {
minSdk = 21
targetSdk = 32
}
}
sqldelight {
database("AppDatabase") {
packageName = "com.example.basekmm_003"
}
}
This is the base gradle
buildscript {
repositories {
gradlePluginPortal()
google()
mavenCentral()
}
dependencies {
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.21")
classpath("com.android.tools.build:gradle:7.2.0")
classpath("com.squareup.sqldelight:gradle-plugin:1.5.3")
classpath("org.jetbrains.kotlin:kotlin-serialization:1.6.21")
}
}
allprojects {
repositories {
google()
mavenCentral()
}
}
tasks.register("clean", Delete::class) {
delete(rootProject.buildDir)
}
The Android Studio version I'm using is: Chipmunk / 2021.2.1
The code for my api is:
class JsonApi() {
suspend fun getLatestMovies(): List<JsonMessage>? =
KtorClient.httpClient.get {
url("https://gitcdn.link/cdn/KaterinaPetrova/greeting/7d47a42fc8d28820387ac7f4aaf36d69e434adc1/greetings.json")
}
}
@Serializable
data class JsonMessage(val string: String?, val lang: String?)
object KtorClient {
private val json = Json {
encodeDefaults = true
ignoreUnknownKeys = true
}
val httpClient = HttpClient {
install(JsonFeature) { serializer = KotlinxSerializer(json) }
install(HttpTimeout) {
socketTimeoutMillis = 30_000
requestTimeoutMillis = 30_000
connectTimeoutMillis = 30_000
}
defaultRequest {
contentType(ContentType.Application.Json)
accept(ContentType.Application.Json)
}
}
}
And the iOS Code is
@main
struct iOSApp: App {
let sdk = MovieSDK(databaseDriverFactory: DatabaseDriverFactory())
var body: some Scene {
WindowGroup {
ContentView(viewModel: .init(sdk: sdk))
}
}
}
struct RocketLaunchRow: View {
var jsonMessage: JsonMessage
var body: some View {
HStack() {
VStack(alignment: .leading, spacing: 10.0) {
Text("Launch name: \(jsonMessage)")
}
Spacer()
}
}
}
struct ContentView: View {
@ObservedObject private(set) var viewModel: ViewModel
enum LoadableLaunches {
case loading
case result([JsonMessage])
case error(String)
}
var body: some View {
NavigationView {
listView()
.navigationBarTitle("SpaceX Launches")
.navigationBarItems(trailing:
Button("Reload") {
self.viewModel.loadLaunches()
}
)
}
}
private func listView() -> AnyView {
switch viewModel.launches {
case .loading:
return AnyView(Text("Loading...").multilineTextAlignment(.center))
case .result(_):
return AnyView(
Text("Succes")
)
case .error(let description):
return AnyView(Text(description).multilineTextAlignment(.center))
}
}
class ViewModel: ObservableObject {
let sdk: MovieSDK
@Published var launches = LoadableLaunches.loading
init(sdk: MovieSDK) {
self.sdk = sdk
self.loadLaunches()
}
func loadLaunches() {
self.launches = .loading
sdk.getLatestMovies(completionHandler: { launches, error in
if let launches = launches {
self.launches = .result(launches)
} else {
self.launches = .error(error?.localizedDescription ?? "error")
}
}
)
}
}
}
What I'm doing wrong?, I've tried several approaches with the same result ( event with different urls )