From 8723045e5e10b032f1b78ef0716a6c4209fcba6c Mon Sep 17 00:00:00 2001 From: KaylaBrady <31781298+KaylaBrady@users.noreply.github.com> Date: Thu, 17 Oct 2024 16:16:57 -0400 Subject: [PATCH 1/2] feat(ErrorBanner): offline banner --- androidApp/src/main/AndroidManifest.xml | 2 + iosApp/iosApp.xcodeproj/project.pbxproj | 4 ++ .../iosApp/ComponentViews/ErrorBanner.swift | 6 +++ iosApp/iosApp/ComponentViews/ErrorCard.swift | 8 ++-- iosApp/iosApp/Localizable.xcstrings | 6 +++ .../ViewModels/ErrorBannerViewModel.swift | 15 +++++-- iosApp/iosAppTests/Utils/FetchApiTests.swift | 7 ++- .../ErrorBannerViewModelTests.swift | 26 +++++++++++ .../iosAppTests/Views/ErrorBannerTests.swift | 8 ++++ .../kotlin/com/mbta/tid/mbta_app/Helpers.kt | 2 +- .../com/mbta/tid/mbta_app/PlatformModule.kt | 5 ++- .../network/NetworkConnectivityMonitor.kt | 30 +++++++++++++ .../dependencyInjection/RepositoryDI.kt | 1 + .../tid/mbta_app/model/ErrorBannerState.kt | 4 +- .../network/NetworkConnectivityMonitor.kt | 6 +++ .../ErrorBannerStateRepository.kt | 43 +++++++++++++++++-- .../ErrorBannerStateRepositoryTest.kt | 25 +++++++++++ .../kotlin/com/mbta/tid/mbta_app/Helpers.kt | 2 +- .../com/mbta/tid/mbta_app/PlatformModule.kt | 5 ++- .../network/NetworkConnectivityMonitor.kt | 23 ++++++++++ 20 files changed, 211 insertions(+), 17 deletions(-) create mode 100644 iosApp/iosAppTests/ViewModels/ErrorBannerViewModelTests.swift create mode 100644 shared/src/androidMain/kotlin/com/mbta/tid/mbta_app/network/NetworkConnectivityMonitor.kt create mode 100644 shared/src/commonMain/kotlin/com/mbta/tid/mbta_app/network/NetworkConnectivityMonitor.kt create mode 100644 shared/src/iosMain/kotlin/com/mbta/tid/mbta_app/network/NetworkConnectivityMonitor.kt diff --git a/androidApp/src/main/AndroidManifest.xml b/androidApp/src/main/AndroidManifest.xml index 9ff6a44b5..0e4f58869 100644 --- a/androidApp/src/main/AndroidManifest.xml +++ b/androidApp/src/main/AndroidManifest.xml @@ -3,6 +3,8 @@ + + : View { + @ViewBuilder let details: Content var button: (() -> AnyView)? - init(_ details: () -> Text) { + init(_ details: () -> Content) { self.details = details() button = nil } - init(details: Text, button: (() -> AnyView)? = nil) { + init(details: Content, button: (() -> AnyView)? = nil) { self.details = details self.button = button } diff --git a/iosApp/iosApp/Localizable.xcstrings b/iosApp/iosApp/Localizable.xcstrings index 65b59f597..0e3197784 100644 --- a/iosApp/iosApp/Localizable.xcstrings +++ b/iosApp/iosApp/Localizable.xcstrings @@ -178,6 +178,9 @@ }, "Crossing Malfunction" : { "comment" : "Possible alert cause" + }, + "Custom message" : { + }, "Demonstration" : { "comment" : "Possible alert cause" @@ -425,6 +428,9 @@ }, "trains" : { "comment" : "trains" + }, + "Unable to connect" : { + }, "Unruly Passenger" : { "comment" : "Possible alert cause" diff --git a/iosApp/iosApp/ViewModels/ErrorBannerViewModel.swift b/iosApp/iosApp/ViewModels/ErrorBannerViewModel.swift index 2ba130fac..f25e491c6 100644 --- a/iosApp/iosApp/ViewModels/ErrorBannerViewModel.swift +++ b/iosApp/iosApp/ViewModels/ErrorBannerViewModel.swift @@ -18,19 +18,28 @@ class ErrorBannerViewModel: ObservableObject { @Published var loadingWhenPredictionsStale: Bool + // option for testing + var skipListeningForStateChanges = false + init( errorRepository: IErrorBannerStateRepository = RepositoryDI().errorBanner, - initialLoadingWhenPredictionsStale: Bool = false + initialLoadingWhenPredictionsStale: Bool = false, + skipListeningForStateChanges: Bool = false ) { self.errorRepository = errorRepository loadingWhenPredictionsStale = initialLoadingWhenPredictionsStale errorState = self.errorRepository.state.value + self.skipListeningForStateChanges = skipListeningForStateChanges } @MainActor func activate() async { - for await errorState in errorRepository.state { - self.errorState = errorState + errorRepository.subscribeToNetworkStatusChanges() + + if !skipListeningForStateChanges { + for await errorState in errorRepository.state { + self.errorState = errorState + } } } diff --git a/iosApp/iosAppTests/Utils/FetchApiTests.swift b/iosApp/iosAppTests/Utils/FetchApiTests.swift index cd4977b08..4776ff885 100644 --- a/iosApp/iosAppTests/Utils/FetchApiTests.swift +++ b/iosApp/iosAppTests/Utils/FetchApiTests.swift @@ -54,7 +54,12 @@ final class FetchApiTests: XCTestCase { onRefreshAfterError: { expRefresh.fulfill() } ) XCTAssertNotNil(errorBannerRepo.state.value) - errorBannerRepo.state.value?.action() + + if let action = errorBannerRepo.state.value?.action { + action() + } else { + XCTFail("data error missing action") + } await fulfillment(of: [expRefresh], timeout: 1) } diff --git a/iosApp/iosAppTests/ViewModels/ErrorBannerViewModelTests.swift b/iosApp/iosAppTests/ViewModels/ErrorBannerViewModelTests.swift new file mode 100644 index 000000000..60eb30919 --- /dev/null +++ b/iosApp/iosAppTests/ViewModels/ErrorBannerViewModelTests.swift @@ -0,0 +1,26 @@ +// +// ErrorBannerViewModelTests.swift +// iosAppTests +// +// Created by Kayla Brady on 10/18/24. +// Copyright © 2024 MBTA. All rights reserved. +// + +import Foundation +@testable import iosApp +import shared +import XCTest + +final class ErrorBannerViewModelTests: XCTestCase { + func testActivateSubscribesToNetworkChanges() async { + let onSubscribeExp = XCTestExpectation(description: "onSubscribe called") + let repo = MockErrorBannerStateRepository(state: nil, onSubscribeToNetworkChanges: { onSubscribeExp.fulfill() }) + let errorVM = ErrorBannerViewModel( + errorRepository: repo, + initialLoadingWhenPredictionsStale: false, + skipListeningForStateChanges: true + ) + await errorVM.activate() + wait(for: [onSubscribeExp], timeout: 1) + } +} diff --git a/iosApp/iosAppTests/Views/ErrorBannerTests.swift b/iosApp/iosAppTests/Views/ErrorBannerTests.swift index fd4c1ebb2..2b8176623 100644 --- a/iosApp/iosAppTests/Views/ErrorBannerTests.swift +++ b/iosApp/iosAppTests/Views/ErrorBannerTests.swift @@ -49,6 +49,14 @@ final class ErrorBannerTests: XCTestCase { wait(for: [callsAction], timeout: 1) } + @MainActor func testWhenNetworkError() throws { + let sut = ErrorBanner(.init( + errorRepository: MockErrorBannerStateRepository(state: .NetworkError()), + initialLoadingWhenPredictionsStale: true + )) + XCTAssertNotNil(try sut.inspect().find(text: "Unable to connect")) + } + @MainActor func testLoadingWhenPredictionsStale() throws { let sut = ErrorBanner(.init( errorRepository: MockErrorBannerStateRepository(state: .StalePredictions( diff --git a/shared/src/androidMain/kotlin/com/mbta/tid/mbta_app/Helpers.kt b/shared/src/androidMain/kotlin/com/mbta/tid/mbta_app/Helpers.kt index ad94fd6fc..7816bc272 100644 --- a/shared/src/androidMain/kotlin/com/mbta/tid/mbta_app/Helpers.kt +++ b/shared/src/androidMain/kotlin/com/mbta/tid/mbta_app/Helpers.kt @@ -14,6 +14,6 @@ internal fun createDataStore(context: Context): DataStore = fun initKoin(appVariant: AppVariant, nativeModule: Module, context: Context) { startKoin { androidContext(context) - modules(appModule(appVariant) + platformModule() + nativeModule) + modules(platformModule() + appModule(appVariant) + nativeModule) } } diff --git a/shared/src/androidMain/kotlin/com/mbta/tid/mbta_app/PlatformModule.kt b/shared/src/androidMain/kotlin/com/mbta/tid/mbta_app/PlatformModule.kt index 603327f5d..9a498d9e5 100644 --- a/shared/src/androidMain/kotlin/com/mbta/tid/mbta_app/PlatformModule.kt +++ b/shared/src/androidMain/kotlin/com/mbta/tid/mbta_app/PlatformModule.kt @@ -1,5 +1,7 @@ package com.mbta.tid.mbta_app +import com.mbta.tid.mbta_app.network.INetworkConnectivityMonitor +import com.mbta.tid.mbta_app.network.NetworkConnectivityMonitor import com.mbta.tid.mbta_app.utils.AndroidSystemPaths import com.mbta.tid.mbta_app.utils.SystemPaths import org.koin.dsl.module @@ -7,6 +9,7 @@ import org.koin.dsl.module fun platformModule() = module { includes( module { single { createDataStore(get()) } }, - module { single { AndroidSystemPaths(get()) } } + module { single { AndroidSystemPaths(get()) } }, + module { single { NetworkConnectivityMonitor(get()) } } ) } diff --git a/shared/src/androidMain/kotlin/com/mbta/tid/mbta_app/network/NetworkConnectivityMonitor.kt b/shared/src/androidMain/kotlin/com/mbta/tid/mbta_app/network/NetworkConnectivityMonitor.kt new file mode 100644 index 000000000..09989a837 --- /dev/null +++ b/shared/src/androidMain/kotlin/com/mbta/tid/mbta_app/network/NetworkConnectivityMonitor.kt @@ -0,0 +1,30 @@ +package com.mbta.tid.mbta_app.network + +import android.content.Context +import android.content.Context.CONNECTIVITY_SERVICE +import android.net.ConnectivityManager +import android.net.Network + +class NetworkConnectivityMonitor(context: Context) : INetworkConnectivityMonitor { + private var networkCallback: ConnectivityManager.NetworkCallback? = null + private val connectivityManager = + context.getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager + + override fun registerListener(onNetworkAvailable: () -> Unit, onNetworkLost: () -> Unit) { + networkCallback = + object : ConnectivityManager.NetworkCallback() { + override fun onAvailable(network: Network) { + onNetworkAvailable() + } + + override fun onUnavailable() { + onNetworkLost() + } + + override fun onLost(network: Network) { + onNetworkLost() + } + } + networkCallback?.let { connectivityManager.registerDefaultNetworkCallback(it) } + } +} diff --git a/shared/src/commonMain/kotlin/com/mbta/tid/mbta_app/dependencyInjection/RepositoryDI.kt b/shared/src/commonMain/kotlin/com/mbta/tid/mbta_app/dependencyInjection/RepositoryDI.kt index ebfc85777..5f96a0cf5 100644 --- a/shared/src/commonMain/kotlin/com/mbta/tid/mbta_app/dependencyInjection/RepositoryDI.kt +++ b/shared/src/commonMain/kotlin/com/mbta/tid/mbta_app/dependencyInjection/RepositoryDI.kt @@ -93,6 +93,7 @@ class RepositoryDI : IRepositories, KoinComponent { class RealRepositories : IRepositories { // initialize repositories with platform-specific dependencies as null. // instantiate the real repositories in makeNativeModule + override val alerts = null override val appCheck = null override val config = ConfigRepository() diff --git a/shared/src/commonMain/kotlin/com/mbta/tid/mbta_app/model/ErrorBannerState.kt b/shared/src/commonMain/kotlin/com/mbta/tid/mbta_app/model/ErrorBannerState.kt index 3f2176c75..f39c9c6c5 100644 --- a/shared/src/commonMain/kotlin/com/mbta/tid/mbta_app/model/ErrorBannerState.kt +++ b/shared/src/commonMain/kotlin/com/mbta/tid/mbta_app/model/ErrorBannerState.kt @@ -5,7 +5,7 @@ import kotlinx.datetime.Instant sealed class ErrorBannerState { /** What to do when the button in the error banner is pressed */ - abstract val action: () -> Unit + abstract val action: (() -> Unit)? data class StalePredictions(val lastUpdated: Instant, override val action: () -> Unit) : ErrorBannerState() { @@ -13,4 +13,6 @@ sealed class ErrorBannerState { } data class DataError(override val action: () -> Unit) : ErrorBannerState() + + data class NetworkError(override val action: (() -> Unit)?) : ErrorBannerState() } diff --git a/shared/src/commonMain/kotlin/com/mbta/tid/mbta_app/network/NetworkConnectivityMonitor.kt b/shared/src/commonMain/kotlin/com/mbta/tid/mbta_app/network/NetworkConnectivityMonitor.kt new file mode 100644 index 000000000..152a61196 --- /dev/null +++ b/shared/src/commonMain/kotlin/com/mbta/tid/mbta_app/network/NetworkConnectivityMonitor.kt @@ -0,0 +1,6 @@ +package com.mbta.tid.mbta_app.network + +/** Observe changes in the device's network connectivity. */ +interface INetworkConnectivityMonitor { + fun registerListener(onNetworkAvailable: () -> Unit, onNetworkLost: () -> Unit) +} diff --git a/shared/src/commonMain/kotlin/com/mbta/tid/mbta_app/repositories/ErrorBannerStateRepository.kt b/shared/src/commonMain/kotlin/com/mbta/tid/mbta_app/repositories/ErrorBannerStateRepository.kt index a251505da..9e2a277bf 100644 --- a/shared/src/commonMain/kotlin/com/mbta/tid/mbta_app/repositories/ErrorBannerStateRepository.kt +++ b/shared/src/commonMain/kotlin/com/mbta/tid/mbta_app/repositories/ErrorBannerStateRepository.kt @@ -1,24 +1,47 @@ package com.mbta.tid.mbta_app.repositories import com.mbta.tid.mbta_app.model.ErrorBannerState +import com.mbta.tid.mbta_app.network.INetworkConnectivityMonitor import kotlin.time.Duration.Companion.minutes import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.datetime.Clock import kotlinx.datetime.Instant import org.koin.core.component.KoinComponent +import org.koin.core.component.inject + +sealed class NetworkStatus { + data object Connected : NetworkStatus() + + data object Disconnected : NetworkStatus() +} + +abstract class IErrorBannerStateRepository(initialState: ErrorBannerState? = null) : KoinComponent { + + private val networkConnectivityMonitor: INetworkConnectivityMonitor by inject() + + /* + Registers platform-specific observer of network status changes. + */ + open fun subscribeToNetworkStatusChanges() { + this.networkConnectivityMonitor.registerListener( + onNetworkAvailable = { setNetworkStatus(NetworkStatus.Connected) }, + onNetworkLost = { setNetworkStatus(NetworkStatus.Disconnected) } + ) + } -abstract class IErrorBannerStateRepository -protected constructor(initialState: ErrorBannerState? = null) { protected val flow = MutableStateFlow(initialState) val state = flow.asStateFlow() + private var networkStatus: NetworkStatus? = null + private var predictionsStale: ErrorBannerState.StalePredictions? = null private val dataErrors = mutableMapOf() protected open fun updateState() { flow.value = when { + networkStatus == NetworkStatus.Disconnected -> ErrorBannerState.NetworkError(null) dataErrors.isNotEmpty() -> // encapsulate all the different error actions within one error ErrorBannerState.DataError { dataErrors.values.forEach { it.action() } } @@ -41,6 +64,11 @@ protected constructor(initialState: ErrorBannerState? = null) { updateState() } + private fun setNetworkStatus(newStatus: NetworkStatus) { + networkStatus = newStatus + updateState() + } + fun setDataError(key: String, action: () -> Unit) { dataErrors[key] = ErrorBannerState.DataError(action) updateState() @@ -60,8 +88,15 @@ protected constructor(initialState: ErrorBannerState? = null) { class ErrorBannerStateRepository : IErrorBannerStateRepository(), KoinComponent -class MockErrorBannerStateRepository(state: ErrorBannerState? = null) : - IErrorBannerStateRepository(state) { +class MockErrorBannerStateRepository( + state: ErrorBannerState? = null, + onSubscribeToNetworkChanges: (() -> Unit)? = null +) : IErrorBannerStateRepository(state) { + private val onSubscribeToNetworkChanges = onSubscribeToNetworkChanges val mutableFlow get() = flow + + override fun subscribeToNetworkStatusChanges() { + onSubscribeToNetworkChanges?.invoke() + } } diff --git a/shared/src/commonTest/kotlin/com/mbta/tid/mbta_app/repositories/ErrorBannerStateRepositoryTest.kt b/shared/src/commonTest/kotlin/com/mbta/tid/mbta_app/repositories/ErrorBannerStateRepositoryTest.kt index 84c286daf..d87bc538a 100644 --- a/shared/src/commonTest/kotlin/com/mbta/tid/mbta_app/repositories/ErrorBannerStateRepositoryTest.kt +++ b/shared/src/commonTest/kotlin/com/mbta/tid/mbta_app/repositories/ErrorBannerStateRepositoryTest.kt @@ -1,6 +1,12 @@ package com.mbta.tid.mbta_app.repositories import com.mbta.tid.mbta_app.model.ErrorBannerState +import com.mbta.tid.mbta_app.network.INetworkConnectivityMonitor +import dev.mokkery.MockMode +import dev.mokkery.matcher.any +import dev.mokkery.mock +import dev.mokkery.verify +import kotlin.test.AfterTest import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertIs @@ -13,8 +19,13 @@ import kotlinx.coroutines.flow.take import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.datetime.Clock +import org.koin.core.context.startKoin +import org.koin.core.context.stopKoin +import org.koin.dsl.module class ErrorBannerStateRepositoryTest { + @AfterTest fun `stop koin`() = run { stopKoin() } + @Test fun `initial state is null`() = runBlocking { val repo = ErrorBannerStateRepository() @@ -112,4 +123,18 @@ class ErrorBannerStateRepositoryTest { assertNull(channel.receive()) } + + @Test + fun `subscribe to connectivity changes`() { + + val mockNetworkMonitor = mock(MockMode.autofill) + + startKoin { modules(module { single { mockNetworkMonitor } }) } + + val repo = ErrorBannerStateRepository() + + repo.subscribeToNetworkStatusChanges() + + verify { mockNetworkMonitor.registerListener(any(), any()) } + } } diff --git a/shared/src/iosMain/kotlin/com/mbta/tid/mbta_app/Helpers.kt b/shared/src/iosMain/kotlin/com/mbta/tid/mbta_app/Helpers.kt index df9d9d25a..55c0f679b 100644 --- a/shared/src/iosMain/kotlin/com/mbta/tid/mbta_app/Helpers.kt +++ b/shared/src/iosMain/kotlin/com/mbta/tid/mbta_app/Helpers.kt @@ -33,7 +33,7 @@ internal fun createDataStore(): DataStore = ) fun initKoin(appVariant: AppVariant, nativeModule: Module) { - startKoin { modules(appModule(appVariant) + platformModule() + nativeModule) } + startKoin { modules(platformModule() + appModule(appVariant) + nativeModule) } } /* diff --git a/shared/src/iosMain/kotlin/com/mbta/tid/mbta_app/PlatformModule.kt b/shared/src/iosMain/kotlin/com/mbta/tid/mbta_app/PlatformModule.kt index 659d2f3cd..6bd381b75 100644 --- a/shared/src/iosMain/kotlin/com/mbta/tid/mbta_app/PlatformModule.kt +++ b/shared/src/iosMain/kotlin/com/mbta/tid/mbta_app/PlatformModule.kt @@ -1,5 +1,7 @@ package com.mbta.tid.mbta_app +import com.mbta.tid.mbta_app.network.INetworkConnectivityMonitor +import com.mbta.tid.mbta_app.network.NetworkConnectivityMonitor import com.mbta.tid.mbta_app.utils.IOSSystemPaths import com.mbta.tid.mbta_app.utils.SystemPaths import org.koin.dsl.module @@ -7,6 +9,7 @@ import org.koin.dsl.module fun platformModule() = module { includes( module { single { createDataStore() } }, - module { single { IOSSystemPaths() } } + module { single { IOSSystemPaths() } }, + module { single { NetworkConnectivityMonitor() } } ) } diff --git a/shared/src/iosMain/kotlin/com/mbta/tid/mbta_app/network/NetworkConnectivityMonitor.kt b/shared/src/iosMain/kotlin/com/mbta/tid/mbta_app/network/NetworkConnectivityMonitor.kt new file mode 100644 index 000000000..8ac906287 --- /dev/null +++ b/shared/src/iosMain/kotlin/com/mbta/tid/mbta_app/network/NetworkConnectivityMonitor.kt @@ -0,0 +1,23 @@ +package com.mbta.tid.mbta_app.network + +import platform.Network.* +import platform.darwin.dispatch_get_main_queue + +class NetworkConnectivityMonitor : INetworkConnectivityMonitor { + private val monitor = nw_path_monitor_create() + + override fun registerListener(onNetworkAvailable: () -> Unit, onNetworkLost: () -> Unit) { + nw_path_monitor_set_update_handler(monitor) { path -> + val pathStatus = nw_path_get_status(path) + + if (pathStatus == nw_path_status_satisfied) { + onNetworkAvailable() + } else { + onNetworkLost() + } + } + + nw_path_monitor_set_queue(monitor, dispatch_get_main_queue()) + nw_path_monitor_start(monitor) + } +} From e8d189c04e2f29e51a3d3849a261df3627b5c9db Mon Sep 17 00:00:00 2001 From: KaylaBrady <31781298+KaylaBrady@users.noreply.github.com> Date: Mon, 21 Oct 2024 10:24:15 -0400 Subject: [PATCH 2/2] address PR feedback --- androidApp/src/main/AndroidManifest.xml | 2 +- iosApp/iosApp/ComponentViews/ErrorBanner.swift | 4 ++-- .../src/androidMain/kotlin/com/mbta/tid/mbta_app/Helpers.kt | 2 +- .../mbta/tid/mbta_app/network/NetworkConnectivityMonitor.kt | 3 +++ shared/src/iosMain/kotlin/com/mbta/tid/mbta_app/Helpers.kt | 2 +- 5 files changed, 8 insertions(+), 5 deletions(-) diff --git a/androidApp/src/main/AndroidManifest.xml b/androidApp/src/main/AndroidManifest.xml index 0e4f58869..2d6374a46 100644 --- a/androidApp/src/main/AndroidManifest.xml +++ b/androidApp/src/main/AndroidManifest.xml @@ -4,7 +4,7 @@ - + = fun initKoin(appVariant: AppVariant, nativeModule: Module, context: Context) { startKoin { androidContext(context) - modules(platformModule() + appModule(appVariant) + nativeModule) + modules(appModule(appVariant) + platformModule() + nativeModule) } } diff --git a/shared/src/androidMain/kotlin/com/mbta/tid/mbta_app/network/NetworkConnectivityMonitor.kt b/shared/src/androidMain/kotlin/com/mbta/tid/mbta_app/network/NetworkConnectivityMonitor.kt index 09989a837..65d4d8ebe 100644 --- a/shared/src/androidMain/kotlin/com/mbta/tid/mbta_app/network/NetworkConnectivityMonitor.kt +++ b/shared/src/androidMain/kotlin/com/mbta/tid/mbta_app/network/NetworkConnectivityMonitor.kt @@ -1,5 +1,6 @@ package com.mbta.tid.mbta_app.network +import android.annotation.SuppressLint import android.content.Context import android.content.Context.CONNECTIVITY_SERVICE import android.net.ConnectivityManager @@ -10,6 +11,8 @@ class NetworkConnectivityMonitor(context: Context) : INetworkConnectivityMonitor private val connectivityManager = context.getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager + @SuppressLint("MissingPermission") + // Permission is included in AndroidManifest.xml override fun registerListener(onNetworkAvailable: () -> Unit, onNetworkLost: () -> Unit) { networkCallback = object : ConnectivityManager.NetworkCallback() { diff --git a/shared/src/iosMain/kotlin/com/mbta/tid/mbta_app/Helpers.kt b/shared/src/iosMain/kotlin/com/mbta/tid/mbta_app/Helpers.kt index 55c0f679b..df9d9d25a 100644 --- a/shared/src/iosMain/kotlin/com/mbta/tid/mbta_app/Helpers.kt +++ b/shared/src/iosMain/kotlin/com/mbta/tid/mbta_app/Helpers.kt @@ -33,7 +33,7 @@ internal fun createDataStore(): DataStore = ) fun initKoin(appVariant: AppVariant, nativeModule: Module) { - startKoin { modules(platformModule() + appModule(appVariant) + nativeModule) } + startKoin { modules(appModule(appVariant) + platformModule() + nativeModule) } } /*