From a5e27741201d969b109b4f5e12a48364fe4ddc74 Mon Sep 17 00:00:00 2001 From: Marc-Antoine Fortier Date: Fri, 20 Oct 2023 11:49:11 -0400 Subject: [PATCH] Add konnectivity --- androidApp/build.gradle.kts | 2 ++ .../com/mirego/kmp/boilerplate/Greeting.kt | 5 ++++- .../boilerplate/previews/PreviewContext.kt | 15 ++++++++++++++ gradle/libs.versions.toml | 4 ++++ ios/Podfile.lock | 8 ++++++-- ios/iosApp.xcodeproj/project.pbxproj | 4 ++++ ios/iosApp/GreetingView.swift | 4 +++- .../Preview Content/PreviewContext.swift | 10 ++++++++++ settings.gradle.kts | 1 + shared/Shared.podspec | 4 ++-- shared/build.gradle.kts | 10 ++++++++++ shared/src/androidMain/AndroidManifest.xml | 16 ++++++++++++++- .../platform/AppContextInitializer.kt | 17 ++++++++++++++++ .../com/mirego/kmp/boilerplate/Greeting.kt | 20 +++++++++++++++++-- 14 files changed, 111 insertions(+), 9 deletions(-) create mode 100644 androidApp/src/main/java/com/mirego/kmp/boilerplate/previews/PreviewContext.kt create mode 100644 ios/iosApp/Preview Content/PreviewContext.swift create mode 100644 shared/src/androidMain/kotlin/com/mirego/kmp/boilerplate/platform/AppContextInitializer.kt diff --git a/androidApp/build.gradle.kts b/androidApp/build.gradle.kts index 4d0997c..b5b7c2e 100644 --- a/androidApp/build.gradle.kts +++ b/androidApp/build.gradle.kts @@ -58,4 +58,6 @@ dependencies { implementation(libs.androidx.compose.ui) implementation(libs.androidx.compose.ui.tooling) implementation(libs.androidx.compose.material) + + implementation(libs.androidx.startup.runtime) } diff --git a/androidApp/src/main/java/com/mirego/kmp/boilerplate/Greeting.kt b/androidApp/src/main/java/com/mirego/kmp/boilerplate/Greeting.kt index 4195f8b..ca9de6e 100644 --- a/androidApp/src/main/java/com/mirego/kmp/boilerplate/Greeting.kt +++ b/androidApp/src/main/java/com/mirego/kmp/boilerplate/Greeting.kt @@ -5,6 +5,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.tooling.preview.Preview +import com.mirego.kmp.boilerplate.previews.PreviewContext import kotlinx.coroutines.flow.Flow @Composable @@ -17,5 +18,7 @@ fun Greeting(textFlow: Flow) { @Preview(showSystemUi = true) @Composable fun PreviewGreeting() { - Greeting(Greeting().greeting()) + PreviewContext { + Greeting(Greeting().greeting()) + } } diff --git a/androidApp/src/main/java/com/mirego/kmp/boilerplate/previews/PreviewContext.kt b/androidApp/src/main/java/com/mirego/kmp/boilerplate/previews/PreviewContext.kt new file mode 100644 index 0000000..6fb5c53 --- /dev/null +++ b/androidApp/src/main/java/com/mirego/kmp/boilerplate/previews/PreviewContext.kt @@ -0,0 +1,15 @@ +package com.mirego.kmp.boilerplate.previews + +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalContext +import androidx.startup.AppInitializer +import com.mirego.kmp.boilerplate.platform.AppContextInitializer + +@Composable +fun PreviewContext(content: @Composable () -> Unit) { + // @Composable previews do not call AppInitializer. We must initialize our components manually. + AppInitializer.getInstance(LocalContext.current) + .initializeComponent(AppContextInitializer::class.java) + + content() +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index b0f3e0b..43296ac 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,9 +1,11 @@ [versions] androidComposeCompiler = "1.5.6" androidGradlePlugin = "8.2.0" +androidxStartup = "1.1.1" androidxActivityCompose = "1.8.1" androidxAppcompat = "1.6.1" androidxComposeBom = "2023.10.01" +konnectivity = "0.3.0" kotlin = "1.9.21" kotlinxCoroutines = "1.7.3" kotlinxSerialization = "1.6.0" @@ -16,6 +18,8 @@ androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", versi androidx-compose-ui = { group = "androidx.compose.ui", name = "ui" } androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" } androidx-compose-material = { group = "androidx.compose.material", name = "material" } +androidx-startup-runtime = { module = "androidx.startup:startup-runtime", version.ref = "androidxStartup" } +konnectivity = { module = "com.mirego:konnectivity", version.ref = "konnectivity" } kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinxCoroutines" } kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinxCoroutines" } kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerialization" } diff --git a/ios/Podfile.lock b/ios/Podfile.lock index b58df42..c4e3ab3 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1,5 +1,7 @@ PODS: - - Shared (0.0.1) + - Reachability (3.2) + - Shared (0.0.1): + - Reachability (~> 3.2) - SwiftLint (0.52.4) DEPENDENCIES: @@ -8,6 +10,7 @@ DEPENDENCIES: SPEC REPOS: trunk: + - Reachability - SwiftLint EXTERNAL SOURCES: @@ -15,7 +18,8 @@ EXTERNAL SOURCES: :path: "../shared" SPEC CHECKSUMS: - Shared: 4d22deb908e7a9ed9a64ea2d3fcc53255a207993 + Reachability: 33e18b67625424e47b6cde6d202dce689ad7af96 + Shared: 394ccbb1a148f40e374cb91aa5b75c96b121a775 SwiftLint: 1cc5cd61ba9bacb2194e340aeb47a2a37fda00b3 PODFILE CHECKSUM: 83ebf5d7b61ce65029f18160027c7392430ee27f diff --git a/ios/iosApp.xcodeproj/project.pbxproj b/ios/iosApp.xcodeproj/project.pbxproj index 83dd924..04df674 100644 --- a/ios/iosApp.xcodeproj/project.pbxproj +++ b/ios/iosApp.xcodeproj/project.pbxproj @@ -12,6 +12,7 @@ 2152FB042600AC8F00CF470E /* iOSApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2152FB032600AC8F00CF470E /* iOSApp.swift */; }; 7555FF83242A565900829871 /* GreetingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7555FF82242A565900829871 /* GreetingView.swift */; }; 9B8ACFDB4E332DFCA8B97CBB /* Pods_iosApp.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4E4E1328B104D05A50A097EE /* Pods_iosApp.framework */; }; + BC5700EB2B1A94D200525C22 /* PreviewContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC5700EA2B1A94D200525C22 /* PreviewContext.swift */; }; BC83B466276E4F080053E064 /* FlowUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC83B465276E4F080053E064 /* FlowUtils.swift */; }; /* End PBXBuildFile section */ @@ -24,6 +25,7 @@ 7555FF7B242A565900829871 /* iosApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = iosApp.app; sourceTree = BUILT_PRODUCTS_DIR; }; 7555FF82242A565900829871 /* GreetingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GreetingView.swift; sourceTree = ""; }; 7555FF8C242A565B00829871 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + BC5700EA2B1A94D200525C22 /* PreviewContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreviewContext.swift; sourceTree = ""; }; BC83B465276E4F080053E064 /* FlowUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlowUtils.swift; sourceTree = ""; }; E232C917135C2C1E3BC8748A /* Pods-iosApp.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-iosApp.release.xcconfig"; path = "Target Support Files/Pods-iosApp/Pods-iosApp.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -44,6 +46,7 @@ isa = PBXGroup; children = ( 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */, + BC5700EA2B1A94D200525C22 /* PreviewContext.swift */, ); path = "Preview Content"; sourceTree = ""; @@ -240,6 +243,7 @@ files = ( 2152FB042600AC8F00CF470E /* iOSApp.swift in Sources */, 7555FF83242A565900829871 /* GreetingView.swift in Sources */, + BC5700EB2B1A94D200525C22 /* PreviewContext.swift in Sources */, BC83B466276E4F080053E064 /* FlowUtils.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/ios/iosApp/GreetingView.swift b/ios/iosApp/GreetingView.swift index 321d1ed..621f275 100644 --- a/ios/iosApp/GreetingView.swift +++ b/ios/iosApp/GreetingView.swift @@ -11,5 +11,7 @@ struct GreetingView: View { } #Preview { - GreetingView() + PreviewContext { + GreetingView() + } } diff --git a/ios/iosApp/Preview Content/PreviewContext.swift b/ios/iosApp/Preview Content/PreviewContext.swift new file mode 100644 index 0000000..663fc3a --- /dev/null +++ b/ios/iosApp/Preview Content/PreviewContext.swift @@ -0,0 +1,10 @@ +import Shared +import SwiftUI + +struct PreviewContext: View where Content: View { + let content: @MainActor () -> Content + + var body: some View { + content() + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 5480329..29deac5 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -12,6 +12,7 @@ dependencyResolutionManagement { repositories { google() mavenCentral() + maven("https://s3.amazonaws.com/mirego-maven/public") } } diff --git a/shared/Shared.podspec b/shared/Shared.podspec index b8df6e9..312517c 100644 --- a/shared/Shared.podspec +++ b/shared/Shared.podspec @@ -8,8 +8,8 @@ Pod::Spec.new do |spec| spec.summary = 'Project summary' spec.vendored_frameworks = 'build/cocoapods/framework/Shared.framework' spec.libraries = 'c++' - - + spec.ios.deployment_target = '15.0' + spec.dependency 'Reachability', '~> 3.2' if !Dir.exist?('build/cocoapods/framework/Shared.framework') || Dir.empty?('build/cocoapods/framework/Shared.framework') raise " diff --git a/shared/build.gradle.kts b/shared/build.gradle.kts index 6dfc212..2966fd6 100644 --- a/shared/build.gradle.kts +++ b/shared/build.gradle.kts @@ -32,6 +32,9 @@ kotlin { framework { baseName = "Shared" } + ios.deploymentTarget = "15.0" + + pod("Reachability", "~> 3.2") } sourceSets { @@ -45,6 +48,7 @@ kotlin { dependencies { implementation(libs.kotlinx.coroutines.core) implementation(libs.kotlinx.serialization.json) + api(libs.konnectivity) } } @@ -54,6 +58,12 @@ kotlin { implementation(libs.kotlinx.coroutines.test) } } + + androidMain { + dependencies { + implementation(libs.androidx.startup.runtime) + } + } } @OptIn(ExperimentalKotlinGradlePluginApi::class) diff --git a/shared/src/androidMain/AndroidManifest.xml b/shared/src/androidMain/AndroidManifest.xml index 8072ee0..0a51b8b 100644 --- a/shared/src/androidMain/AndroidManifest.xml +++ b/shared/src/androidMain/AndroidManifest.xml @@ -1,2 +1,16 @@ - + + + + + + + + diff --git a/shared/src/androidMain/kotlin/com/mirego/kmp/boilerplate/platform/AppContextInitializer.kt b/shared/src/androidMain/kotlin/com/mirego/kmp/boilerplate/platform/AppContextInitializer.kt new file mode 100644 index 0000000..4fcab3b --- /dev/null +++ b/shared/src/androidMain/kotlin/com/mirego/kmp/boilerplate/platform/AppContextInitializer.kt @@ -0,0 +1,17 @@ +package com.mirego.kmp.boilerplate.platform + +import android.content.Context +import androidx.startup.Initializer +import com.mirego.konnectivity.KonnectivityInitializer + +internal lateinit var appContext: Context + +class AppContextInitializer : Initializer { + override fun create(context: Context) { + appContext = context + } + + override fun dependencies() = listOf( + KonnectivityInitializer::class.java + ) +} diff --git a/shared/src/commonMain/kotlin/com/mirego/kmp/boilerplate/Greeting.kt b/shared/src/commonMain/kotlin/com/mirego/kmp/boilerplate/Greeting.kt index 03fb89c..5ce8ea0 100644 --- a/shared/src/commonMain/kotlin/com/mirego/kmp/boilerplate/Greeting.kt +++ b/shared/src/commonMain/kotlin/com/mirego/kmp/boilerplate/Greeting.kt @@ -3,17 +3,33 @@ package com.mirego.kmp.boilerplate import com.mirego.kmp.boilerplate.platform.Platform import com.mirego.kmp.boilerplate.utils.CFlow import com.mirego.kmp.boilerplate.utils.wrap -import kotlinx.coroutines.flow.flowOf +import com.mirego.konnectivity.Konnectivity +import com.mirego.konnectivity.NetworkState +import kotlinx.coroutines.flow.map class Greeting { private val platform = Platform() + private val konnectivity = Konnectivity() private val greetingText = buildString { appendLine("Hello! 👋") appendLine(platform.system) appendLine(platform.locale) appendLine(platform.version) + appendLine() } - fun greeting(): CFlow = flowOf(greetingText).wrap() + fun greeting(): CFlow = konnectivity.networkState + .map { networkState -> + greetingText + networkState.asGreetingInfo() + } + .wrap() + + private fun NetworkState.asGreetingInfo(): String = "By the way, you're " + when (this) { + NetworkState.Unreachable -> "offline. 🔌" + is NetworkState.Reachable -> when (metered) { + true -> "online, but your connection is metered. 📶" + else -> "online! 🛜" + } + } }