From be1aa14b94e435488864aa77d895ad8d93865d7c Mon Sep 17 00:00:00 2001 From: Iván Ávalos Date: Sat, 16 Sep 2023 16:13:33 -0600 Subject: - [shared] Downgrade Kotlin to 1.8.22 due to Native issues - [shared] Upgrade Ktor to 2.3.4 and migrate code - [ios] Update view models to use @MainActor and Kotlin migrations --- build.gradle.kts | 4 +-- .../Details/Commands/UnitCommandsViewModel.swift | 2 +- .../Details/Reports/UnitReportsViewModel.swift | 6 ++-- iosApp/iosApp/Devices/DeviceRow.swift | 4 +-- iosApp/iosApp/Devices/DevicesViewModel.swift | 15 +++++--- iosApp/iosApp/Session/AccountViewModel.swift | 7 ++-- iosApp/iosApp/Session/RootViewModel.swift | 7 ++-- iosApp/iosApp/Shared/FlowCollector.swift | 4 +-- iosApp/iosApp/Units/UnitsViewModel.swift | 17 ++++++--- shared/build.gradle.kts | 15 ++++---- .../TrackerMap/client/infrastructure/ApiClient.kt | 41 ++++++++++------------ .../client/infrastructure/HttpClientProvider.kt | 4 +-- .../trackermap/TrackerMap/utils/NSDataByteArray.kt | 2 +- 13 files changed, 71 insertions(+), 57 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 7d1f0e2..5e1ff49 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,8 +6,8 @@ buildscript { maven("https://jitpack.io") } dependencies { - classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.0") - classpath("org.jetbrains.kotlin:kotlin-serialization:1.9.0") + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.22") + classpath("org.jetbrains.kotlin:kotlin-serialization:1.8.22") classpath("com.android.tools.build:gradle:8.1.1") classpath("com.google.gms:google-services:4.3.15") } diff --git a/iosApp/iosApp/Details/Commands/UnitCommandsViewModel.swift b/iosApp/iosApp/Details/Commands/UnitCommandsViewModel.swift index 8e1da00..82e9dd7 100644 --- a/iosApp/iosApp/Details/Commands/UnitCommandsViewModel.swift +++ b/iosApp/iosApp/Details/Commands/UnitCommandsViewModel.swift @@ -53,7 +53,7 @@ class UnitCommandsViewModel: ObservableObject { func sendCommand() { if let command = selected { - commandsController.sendCommand(command: command) { _, error in + commandsController.sendCommand(command: command) { error in if let error = error { print("There is a fucking error") print(error.localizedDescription) diff --git a/iosApp/iosApp/Details/Reports/UnitReportsViewModel.swift b/iosApp/iosApp/Details/Reports/UnitReportsViewModel.swift index 5cdf21e..0132685 100644 --- a/iosApp/iosApp/Details/Reports/UnitReportsViewModel.swift +++ b/iosApp/iosApp/Details/Reports/UnitReportsViewModel.swift @@ -129,10 +129,10 @@ class UnitReportsViewModel: ObservableObject { init(deviceId id: Int32?) { deviceId = id let collector = Collector(callback: setReport) - reportController.reportFlow.collect(collector: collector) { _, _ in } + reportController.reportFlow.collect(collector: collector) { _ in } let geofencesCollector = Collector<[Int: Geofence]>(callback: setGeofences) - geofencesController.geofencesFlow.collect(collector: geofencesCollector) { unit, error in } + geofencesController.geofencesFlow.collect(collector: geofencesCollector) { _ in } } func setReport (report: ReportController.Report) { @@ -164,7 +164,7 @@ class UnitReportsViewModel: ObservableObject { .textMessage, .driverChanged, .unknown - ]) { _, _ in } + ]) { _ in } } } diff --git a/iosApp/iosApp/Devices/DeviceRow.swift b/iosApp/iosApp/Devices/DeviceRow.swift index 8a826ca..75f8c08 100644 --- a/iosApp/iosApp/Devices/DeviceRow.swift +++ b/iosApp/iosApp/Devices/DeviceRow.swift @@ -151,8 +151,8 @@ struct DeviceRow: View { } /* MARK: - Hourmeter */ - if let hourmeter = Int64(truncating: unit.getHourmeter() ?? 0), - hourmeter >= 60 * 60 * 1000 { + let hourmeter = Int64(truncating: unit.getHourmeter() ?? 0) + if hourmeter >= 60 * 60 * 1000 { HStack { Label(Formatter.companion.formatHours(millis: hourmeter), systemImage: "timer") diff --git a/iosApp/iosApp/Devices/DevicesViewModel.swift b/iosApp/iosApp/Devices/DevicesViewModel.swift index 76cf9a2..f252ad2 100644 --- a/iosApp/iosApp/Devices/DevicesViewModel.swift +++ b/iosApp/iosApp/Devices/DevicesViewModel.swift @@ -18,6 +18,7 @@ import Foundation import shared +@MainActor class DevicesViewModel: ObservableObject { var unitsViewModel: UnitsViewModel var id: Int32? = nil @@ -35,14 +36,18 @@ class DevicesViewModel: ObservableObject { } func select(unit: UnitInformation) { - unitsViewModel.searchQuery = "" - unitsViewModel.isEditing = false - unitsViewModel.unitsDisplayMode = .map - unitsViewModel.selectedUnit = unit + Task { @MainActor in + unitsViewModel.searchQuery = "" + unitsViewModel.isEditing = false + unitsViewModel.unitsDisplayMode = .map + unitsViewModel.selectedUnit = unit + } } func show(action: DeviceRow.Action, for unit: UnitInformation) { - unitsViewModel.show(action: action, for: unit) + Task { @MainActor in + unitsViewModel.show(action: action, for: unit) + } } } diff --git a/iosApp/iosApp/Session/AccountViewModel.swift b/iosApp/iosApp/Session/AccountViewModel.swift index 7953265..0558268 100644 --- a/iosApp/iosApp/Session/AccountViewModel.swift +++ b/iosApp/iosApp/Session/AccountViewModel.swift @@ -9,6 +9,7 @@ import Foundation import shared +@MainActor class AccountViewModel: ObservableObject { @Inject var sessionController: SessionController @@ -16,11 +17,13 @@ class AccountViewModel: ObservableObject { init() { let userCollector = Collector(callback: setUser) - sessionController.userFlow.collect(collector: userCollector) {_, _ in } + sessionController.userFlow.collect(collector: userCollector) { _ in } } func setUser(user: User?) { - self.user = user + Task { @MainActor in + self.user = user + } } func fetchUserInfo() { diff --git a/iosApp/iosApp/Session/RootViewModel.swift b/iosApp/iosApp/Session/RootViewModel.swift index 682deee..ec103ba 100644 --- a/iosApp/iosApp/Session/RootViewModel.swift +++ b/iosApp/iosApp/Session/RootViewModel.swift @@ -18,6 +18,7 @@ import Foundation import shared +@MainActor class RootViewModel: ObservableObject { @Inject private var sessionController: SessionController @@ -25,13 +26,15 @@ class RootViewModel: ObservableObject { init() { let collector = Collector(callback: setLoginState) - sessionController.loginStateFlow.collect(collector: collector) { (unit, error) in } + sessionController.loginStateFlow.collect(collector: collector) { _ in } restoreSession() } func setLoginState(state: SessionController.LoginState?) { print("State is: \(state?.debugDescription ?? "")") - self.loginState = state ?? SessionController.LoginStateNothing() + Task { @MainActor in + self.loginState = state ?? SessionController.LoginStateNothing() + } } func restoreSession() { diff --git a/iosApp/iosApp/Shared/FlowCollector.swift b/iosApp/iosApp/Shared/FlowCollector.swift index 628c0cd..8570490 100644 --- a/iosApp/iosApp/Shared/FlowCollector.swift +++ b/iosApp/iosApp/Shared/FlowCollector.swift @@ -27,9 +27,9 @@ class Collector: Kotlinx_coroutines_coreFlowCollector { self.callback = callback } - func emit(value: Any?, completionHandler: @escaping (KotlinUnit?, Error?) -> Void) { + func emit(value: Any?, completionHandler: @escaping (Error?) -> Void) { callback(value as! T) - completionHandler(KotlinUnit(), nil) + completionHandler(nil) } } diff --git a/iosApp/iosApp/Units/UnitsViewModel.swift b/iosApp/iosApp/Units/UnitsViewModel.swift index 6a3ce19..7fcc80e 100644 --- a/iosApp/iosApp/Units/UnitsViewModel.swift +++ b/iosApp/iosApp/Units/UnitsViewModel.swift @@ -19,6 +19,7 @@ import Foundation import WhirlyGlobe import shared +@MainActor class UnitsViewModel: ObservableObject { @Inject var unitsController: UnitsController @Inject var geofenceController: GeofencesController @@ -45,7 +46,9 @@ class UnitsViewModel: ObservableObject { @Published var unitsDisplayMode: UnitsDisplayMode = .map @Published var units: [UnitInformation] = [] { didSet { - markers = units.compactMap(Marker.companion.fromUnit) + Task { @MainActor in + markers = units.compactMap(Marker.companion.fromUnit) + } } } @Published var markers: [Marker] = [] @@ -81,20 +84,24 @@ class UnitsViewModel: ObservableObject { private func setupObservers() { let unitsCollector = Collector<[UnitInformation]>(callback: setUnits) - unitsController.displayedUnitsFlow.collect(collector: unitsCollector) { unit, error in } + unitsController.displayedUnitsFlow.collect(collector: unitsCollector) { _ in } let geofencesCollector = Collector<[Int: Geofence]>(callback: setGeofences) - geofenceController.geofencesFlow.collect(collector: geofencesCollector) { unit, error in } + geofenceController.geofencesFlow.collect(collector: geofencesCollector) { _ in } } private func setUnits(units: [UnitInformation]) { print("Positions") - self.units = units + Task { @MainActor in + self.units = units + } updateSelectedUnit() } private func setGeofences(geofences: [Int: Geofence]) { - self.geofences = geofences + Task { @MainActor in + self.geofences = geofences + } } func selectUnit(unit: UnitInformation?, switchToMap: Bool = true) { diff --git a/shared/build.gradle.kts b/shared/build.gradle.kts index 94b8f09..67acdb8 100644 --- a/shared/build.gradle.kts +++ b/shared/build.gradle.kts @@ -5,17 +5,17 @@ plugins { } kotlin { - val ktor_version = "1.6.6" + val ktor_version = "2.3.4" val settings_version = "0.8.1" - androidTarget() + android() jvmToolchain(17) listOf( iosX64(), iosArm64(), - //iosSimulatorArm64() sure all ios dependencies support this target + iosSimulatorArm64() ).forEach { it.binaries.framework { baseName = "shared" @@ -27,7 +27,8 @@ kotlin { dependencies { implementation("io.ktor:ktor-client-core:$ktor_version") implementation("io.ktor:ktor-client-logging:$ktor_version") - implementation("io.ktor:ktor-client-serialization:$ktor_version") + implementation("io.ktor:ktor-serialization-kotlinx-json:$ktor_version") + implementation("io.ktor:ktor-client-content-negotiation:$ktor_version") implementation("io.insert-koin:koin-core:3.1.4") implementation("ch.qos.logback:logback-classic:1.2.6") @@ -44,14 +45,14 @@ kotlin { } val iosX64Main by getting val iosArm64Main by getting - //val iosSimulatorArm64Main by getting + val iosSimulatorArm64Main by getting val iosMain by creating { dependsOn(commonMain) iosX64Main.dependsOn(this) iosArm64Main.dependsOn(this) - //iosSimulatorArm64Main.dependsOn(this) + iosSimulatorArm64Main.dependsOn(this) dependencies { - implementation("io.ktor:ktor-client-ios:$ktor_version") + implementation("io.ktor:ktor-client-darwin:$ktor_version") } } } diff --git a/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/client/infrastructure/ApiClient.kt b/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/client/infrastructure/ApiClient.kt index e9865e8..937b2dd 100644 --- a/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/client/infrastructure/ApiClient.kt +++ b/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/client/infrastructure/ApiClient.kt @@ -19,17 +19,14 @@ package mx.trackermap.TrackerMap.client.infrastructure import io.ktor.client.* import io.ktor.client.call.* -import io.ktor.client.features.* -import io.ktor.client.features.json.* -import io.ktor.client.features.json.serializer.KotlinxSerializer -import io.ktor.client.features.logging.DEFAULT -import io.ktor.client.features.logging.LogLevel -import io.ktor.client.features.logging.Logger -import io.ktor.client.features.logging.Logging +import io.ktor.client.plugins.* +import io.ktor.client.plugins.contentnegotiation.* +import io.ktor.client.plugins.logging.* import io.ktor.client.request.* -import io.ktor.client.request.forms.FormDataContent +import io.ktor.client.request.forms.* import io.ktor.client.statement.* import io.ktor.http.* +import io.ktor.serialization.kotlinx.json.* import io.ktor.util.* import kotlinx.serialization.json.Json as KotlinJson @@ -49,13 +46,11 @@ open class ApiClient( connectTimeoutMillis = 20_000 requestTimeoutMillis = 20_000 } - install(JsonFeature) { - serializer = KotlinxSerializer( - KotlinJson { - ignoreUnknownKeys = true - useAlternativeNames = false - } - ) + install(ContentNegotiation) { + json(KotlinJson { + ignoreUnknownKeys = true + useAlternativeNames = false + }) } install(Logging) { logger = Logger.DEFAULT @@ -85,12 +80,12 @@ open class ApiClient( } parametersBuilder.build() requestBuilder.contentType(ContentType.MultiPart.FormData) - requestBuilder.body = parametersBuilder + requestBuilder.setBody(parametersBuilder) } mediaType == ApiJsonMediaType -> { requestBuilder.contentType(ContentType.Application.Json) if (content != null) { - requestBuilder.body = content + requestBuilder.setBody(content) } } mediaType == ApiFormURLType && content is Map<*, *> -> { @@ -98,7 +93,7 @@ open class ApiClient( content.forEach { item -> parametersBuilder[item.key as String] = item.value as String } - requestBuilder.body = FormDataContent(parametersBuilder.build()) + requestBuilder.setBody(FormDataContent(parametersBuilder.build())) } mediaType == ApiXmlMediaType -> TODO("xml not currently supported.") @@ -119,7 +114,7 @@ open class ApiClient( } val urlBuilder = URLBuilder(httpUrl) - .path("${httpUrl.encodedPath.trimStart('/')}${requestConfig.path}") + urlBuilder.path("${httpUrl.encodedPath.trimStart('/')}${requestConfig.path}") requestConfig.query.forEach { query -> query.value.forEach { queryValue -> @@ -190,21 +185,21 @@ open class ApiClient( response.headers.toMap() ) in 200..299 -> return Success( - response.receive(), + response.body(), response.status.value, response.headers.toMap() ) in 400..499 -> return ClientError( - response.receive(), + response.body(), response.status.value, response.headers.toMap() ) else -> return ServerError( null, - response.receive(), + response.body(), response.status.value, response.headers.toMap() ) } } -} \ No newline at end of file +} diff --git a/shared/src/iosMain/kotlin/mx/trackermap/TrackerMap/client/infrastructure/HttpClientProvider.kt b/shared/src/iosMain/kotlin/mx/trackermap/TrackerMap/client/infrastructure/HttpClientProvider.kt index e95e964..7f72392 100644 --- a/shared/src/iosMain/kotlin/mx/trackermap/TrackerMap/client/infrastructure/HttpClientProvider.kt +++ b/shared/src/iosMain/kotlin/mx/trackermap/TrackerMap/client/infrastructure/HttpClientProvider.kt @@ -18,11 +18,11 @@ package mx.trackermap.TrackerMap.client.infrastructure import io.ktor.client.* -import io.ktor.client.engine.ios.* +import io.ktor.client.engine.darwin.* actual class HttpClientProvider { actual fun getHttpClient(): HttpClient { - return HttpClient(Ios) { + return HttpClient(Darwin) { engine { configureSession { HTTPCookieStorage = null diff --git a/shared/src/iosMain/kotlin/mx/trackermap/TrackerMap/utils/NSDataByteArray.kt b/shared/src/iosMain/kotlin/mx/trackermap/TrackerMap/utils/NSDataByteArray.kt index 807c63f..9d23296 100644 --- a/shared/src/iosMain/kotlin/mx/trackermap/TrackerMap/utils/NSDataByteArray.kt +++ b/shared/src/iosMain/kotlin/mx/trackermap/TrackerMap/utils/NSDataByteArray.kt @@ -37,4 +37,4 @@ fun NSData.toByteArray(): ByteArray = ByteArray(this@toByteArray.length.toInt()) usePinned { memcpy(it.addressOf(0), this@toByteArray.bytes, this@toByteArray.length) } -} \ No newline at end of file +} -- cgit v1.2.3