From 937b74418ec21d6b50a5aa8e9e42b59ce583837d Mon Sep 17 00:00:00 2001 From: Olivier Pineau Date: Fri, 3 Nov 2023 17:30:46 -0400 Subject: [PATCH 1/6] Analytics stuff --- androidApp/build.gradle.kts | 5 +- .../AndroidSharedAnalyticsService.kt | 30 ++++++ .../app/bootstrap/AndroidBootstrap.kt | 8 ++ gradle/libs.versions.toml | 13 ++- ios/Podfile | 6 +- ios/Podfile.lock | 98 ++++++++++++++++++- ios/iosApp.xcodeproj/project.pbxproj | 4 + ios/iosApp/Domain/AnalyticsServiceImpl.swift | 20 ++++ ios/iosApp/Domain/BootstrapImpl.swift | 9 ++ shared/build.gradle.kts | 2 + .../analytics/EmptySharedAnalyticsService.kt | 12 +++ .../analytics/SharedAnalyticsConfiguration.kt | 5 + .../analytics/SharedAnalyticsService.kt | 9 ++ .../usecase/ProjectsUseCaseTestImpl.kt | 4 +- 14 files changed, 216 insertions(+), 9 deletions(-) create mode 100644 androidApp/src/main/java/com/mirego/kmp/boilerplate/app/analytics/AndroidSharedAnalyticsService.kt create mode 100644 ios/iosApp/Domain/AnalyticsServiceImpl.swift create mode 100644 shared/src/commonMain/kotlin/com/mirego/kmp/boilerplate/analytics/EmptySharedAnalyticsService.kt create mode 100644 shared/src/commonMain/kotlin/com/mirego/kmp/boilerplate/analytics/SharedAnalyticsConfiguration.kt create mode 100644 shared/src/commonMain/kotlin/com/mirego/kmp/boilerplate/analytics/SharedAnalyticsService.kt diff --git a/androidApp/build.gradle.kts b/androidApp/build.gradle.kts index a45089d..219dfb2 100644 --- a/androidApp/build.gradle.kts +++ b/androidApp/build.gradle.kts @@ -1,6 +1,7 @@ plugins { alias(libs.plugins.android.application) alias(libs.plugins.kotlin.android) + alias(libs.plugins.crashlyticsPlugin) } kotlin { @@ -77,9 +78,11 @@ dependencies { implementation(project(":shared")) implementation(libs.android.splash) + implementation(libs.android.firebase.analytics) + implementation(libs.android.firebase.crashlytics) + implementation(platform(libs.android.firebase.bom)) implementation(libs.androidx.appcompat) implementation(libs.androidx.activity.compose) - implementation(platform(libs.androidx.compose.bom)) implementation(libs.accompanist.systemuicontroller) implementation(libs.androidx.compose.ui) diff --git a/androidApp/src/main/java/com/mirego/kmp/boilerplate/app/analytics/AndroidSharedAnalyticsService.kt b/androidApp/src/main/java/com/mirego/kmp/boilerplate/app/analytics/AndroidSharedAnalyticsService.kt new file mode 100644 index 0000000..3406dc4 --- /dev/null +++ b/androidApp/src/main/java/com/mirego/kmp/boilerplate/app/analytics/AndroidSharedAnalyticsService.kt @@ -0,0 +1,30 @@ +package com.mirego.kmp.boilerplate.app.analytics + +import android.content.Context +import android.os.Bundle +import com.google.firebase.analytics.FirebaseAnalytics +import com.mirego.kmp.boilerplate.analytics.SharedAnalyticsService +import com.mirego.trikot.analytics.AnalyticsEvent +import com.mirego.trikot.analytics.AnalyticsPropertiesType + +class AndroidSharedAnalyticsService( + context: Context, + private var analyticsEnabled: Boolean = true +) : SharedAnalyticsService { + private var firebaseAnalytics = FirebaseAnalytics.getInstance(context) + + override var isEnabled: Boolean + get() = analyticsEnabled + set(value) { + analyticsEnabled = value + firebaseAnalytics.setAnalyticsCollectionEnabled(value) + } + + override fun trackEvent(event: AnalyticsEvent, properties: AnalyticsPropertiesType) { + val bundle = Bundle() + properties.forEach { + bundle.putString(it.key, it.value.toString()) + } + firebaseAnalytics.logEvent(event.name, bundle) + } +} diff --git a/androidApp/src/main/java/com/mirego/kmp/boilerplate/app/bootstrap/AndroidBootstrap.kt b/androidApp/src/main/java/com/mirego/kmp/boilerplate/app/bootstrap/AndroidBootstrap.kt index 59ce0dc..6ad1e91 100644 --- a/androidApp/src/main/java/com/mirego/kmp/boilerplate/app/bootstrap/AndroidBootstrap.kt +++ b/androidApp/src/main/java/com/mirego/kmp/boilerplate/app/bootstrap/AndroidBootstrap.kt @@ -2,6 +2,8 @@ package com.mirego.kmp.boilerplate.app.bootstrap import android.content.Context import com.mirego.kmp.boilerplate.BuildConfig +import com.mirego.kmp.boilerplate.analytics.SharedAnalyticsConfiguration +import com.mirego.kmp.boilerplate.app.analytics.AndroidSharedAnalyticsService import com.mirego.kmp.boilerplate.app.resources.AndroidImageProvider import com.mirego.kmp.boilerplate.bootstrap.AppEnvironment import com.mirego.kmp.boilerplate.bootstrap.Bootstrap @@ -31,5 +33,11 @@ class AndroidBootstrap(context: Context) : Bootstrap { imageProvider = AndroidImageProvider(), textStyleProvider = DefaultTextStyleProvider() ) + + val analyticsEnabled = !BuildConfig.DEBUG + SharedAnalyticsConfiguration.analyticsManager = AndroidSharedAnalyticsService( + context = context, + analyticsEnabled = analyticsEnabled + ) } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a95b901..058fe9e 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -4,10 +4,13 @@ accompanist = "0.32.0" androidComposeCompiler = "1.5.3" androidGradlePlugin = "8.1.2" android-splash = "1.0.1" +android-playServices = "20.7.0" +play-services-measurement-api = "21.5.0" androidxActivityCompose = "1.8.0" androidxAppcompat = "1.6.1" androidxComposeBom = "2023.10.01" apollo = "3.7.4" +crashlytics-plugin = "2.9.9" koin = "3.5.0" koin-android = "3.5.0" koin-androidx-compose = "3.5.0" @@ -18,13 +21,14 @@ kotlinxCoroutines = "1.7.3" kotlinxSerialization = "1.6.0" kword-plugin = "4.0.0" ktlint = "11.6.1" +firebase = "32.5.0" mockk = "1.12.5" okio = "3.6.0" skie = "0.4.20" trikot = "5.2.0" [libraries] -accompanist-systemuicontroller = { module = "com.google.accompanist:accompanist-systemuicontroller", version.ref = "accompanist"} +accompanist-systemuicontroller = { module = "com.google.accompanist:accompanist-systemuicontroller", version.ref = "accompanist" } android-splash = { module = "androidx.core:core-splashscreen", version.ref = "android-splash" } androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidxActivityCompose" } androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "androidxAppcompat" } @@ -32,6 +36,10 @@ 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" } +#android-play-services-measurement-api = { group = "com.google.android.gms", name = "play-services-measurement-api", version.ref = "play-services-measurement-api" } +android-firebase-bom = { module = "com.google.firebase:firebase-bom", version.ref = "firebase" } +android-firebase-crashlytics = { module = "com.google.firebase:firebase-crashlytics-ktx" } +android-firebase-analytics = { module = "com.google.firebase:firebase-analytics-ktx" } apollo-runtime = { group = "com.apollographql.apollo3", name = "apollo-runtime" } koin-core = { module = "io.insert-koin:koin-core", version.ref = "koin" } koin-annotations = { module = "io.insert-koin:koin-annotations", version.ref = "koin_ksp" } @@ -51,6 +59,8 @@ skie = { module = "co.touchlab.skie:configuration-annotations", version.ref = "s mockk-common = { module = "io.mockk:mockk-common", version.ref = "mockk" } mockk = { module = "io.mockk:mockk", version.ref = "mockk" } +trikot-analytics = { module = "com.mirego.trikot:analytics", version.ref = "trikot" } +trikot-analytics-firebase = { module = "com.mirego.trikot.analytics:firebase-ktx", version.ref = "trikot" } trikot-vmd = { module = "com.mirego.trikot:viewmodels-declarative-flow", version.ref = "trikot" } trikot-kword = { module = "com.mirego.trikot:kword", version.ref = "trikot" } trikot-datasources = { module = "com.mirego.trikot:datasources-flow", version.ref = "trikot" } @@ -68,6 +78,7 @@ kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref kotlin-native-cocoapods = { id = "org.jetbrains.kotlin.native.cocoapods", version.ref = "kotlin" } kspPlugin = { id = "com.google.devtools.ksp", version.ref = "ksp" } ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlint" } +crashlyticsPlugin = { id = "com.google.firebase.crashlytics", version.ref = "crashlytics-plugin" } mirego-kwordPlugin = { id = "mirego.kword", version.ref = "kword-plugin" } serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } skie-plugin = { id = "co.touchlab.skie", version.ref = "skie" } diff --git a/ios/Podfile b/ios/Podfile index 1bfb53e..75daacd 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -6,7 +6,11 @@ inhibit_all_warnings! target 'iosApp' do use_frameworks! - platform :ios, $deploymentTarget + platform :ios, $deploymentTarget + # Thirdo-party + pod 'FirebaseCore' + pod 'FirebaseAnalytics' + # Multiplatform pod 'Shared', :path => '../shared' diff --git a/ios/Podfile.lock b/ios/Podfile.lock index c28419c..4e8e454 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1,7 +1,80 @@ PODS: + - FirebaseAnalytics (10.16.0): + - FirebaseAnalytics/AdIdSupport (= 10.16.0) + - FirebaseCore (~> 10.0) + - FirebaseInstallations (~> 10.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.11) + - GoogleUtilities/MethodSwizzler (~> 7.11) + - GoogleUtilities/Network (~> 7.11) + - "GoogleUtilities/NSData+zlib (~> 7.11)" + - nanopb (< 2.30910.0, >= 2.30908.0) + - FirebaseAnalytics/AdIdSupport (10.16.0): + - FirebaseCore (~> 10.0) + - FirebaseInstallations (~> 10.0) + - GoogleAppMeasurement (= 10.16.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.11) + - GoogleUtilities/MethodSwizzler (~> 7.11) + - GoogleUtilities/Network (~> 7.11) + - "GoogleUtilities/NSData+zlib (~> 7.11)" + - nanopb (< 2.30910.0, >= 2.30908.0) + - FirebaseCore (10.16.0): + - FirebaseCoreInternal (~> 10.0) + - GoogleUtilities/Environment (~> 7.8) + - GoogleUtilities/Logger (~> 7.8) + - FirebaseCoreInternal (10.16.0): + - "GoogleUtilities/NSData+zlib (~> 7.8)" + - FirebaseInstallations (10.16.0): + - FirebaseCore (~> 10.0) + - GoogleUtilities/Environment (~> 7.8) + - GoogleUtilities/UserDefaults (~> 7.8) + - PromisesObjC (~> 2.1) + - GoogleAppMeasurement (10.16.0): + - GoogleAppMeasurement/AdIdSupport (= 10.16.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.11) + - GoogleUtilities/MethodSwizzler (~> 7.11) + - GoogleUtilities/Network (~> 7.11) + - "GoogleUtilities/NSData+zlib (~> 7.11)" + - nanopb (< 2.30910.0, >= 2.30908.0) + - GoogleAppMeasurement/AdIdSupport (10.16.0): + - GoogleAppMeasurement/WithoutAdIdSupport (= 10.16.0) + - GoogleUtilities/AppDelegateSwizzler (~> 7.11) + - GoogleUtilities/MethodSwizzler (~> 7.11) + - GoogleUtilities/Network (~> 7.11) + - "GoogleUtilities/NSData+zlib (~> 7.11)" + - nanopb (< 2.30910.0, >= 2.30908.0) + - GoogleAppMeasurement/WithoutAdIdSupport (10.16.0): + - GoogleUtilities/AppDelegateSwizzler (~> 7.11) + - GoogleUtilities/MethodSwizzler (~> 7.11) + - GoogleUtilities/Network (~> 7.11) + - "GoogleUtilities/NSData+zlib (~> 7.11)" + - nanopb (< 2.30910.0, >= 2.30908.0) + - GoogleUtilities/AppDelegateSwizzler (7.11.5): + - GoogleUtilities/Environment + - GoogleUtilities/Logger + - GoogleUtilities/Network + - GoogleUtilities/Environment (7.11.5): + - PromisesObjC (< 3.0, >= 1.2) + - GoogleUtilities/Logger (7.11.5): + - GoogleUtilities/Environment + - GoogleUtilities/MethodSwizzler (7.11.5): + - GoogleUtilities/Logger + - GoogleUtilities/Network (7.11.5): + - GoogleUtilities/Logger + - "GoogleUtilities/NSData+zlib" + - GoogleUtilities/Reachability + - "GoogleUtilities/NSData+zlib (7.11.5)" + - GoogleUtilities/Reachability (7.11.5): + - GoogleUtilities/Logger + - GoogleUtilities/UserDefaults (7.11.5): + - GoogleUtilities/Logger - Kingfisher (7.8.1) + - nanopb (2.30909.0): + - nanopb/decode (= 2.30909.0) + - nanopb/encode (= 2.30909.0) + - nanopb/decode (2.30909.0) + - nanopb/encode (2.30909.0) + - PromisesObjC (2.3.1) - Shared (0.1) - - SwiftGen (6.6.2) - SwiftLint (0.53.0) - SwiftUIIntrospect (1.1.0) - Trikot/kword (5.2.0): @@ -15,16 +88,24 @@ PODS: - Trikot/viewmodels.declarative.flow DEPENDENCIES: + - FirebaseAnalytics + - FirebaseCore - Shared (from `../shared`) - - SwiftGen - SwiftLint - "Trikot/kword (from `git@github.com:mirego/trikot.git`, tag `5.2.0`)" - "Trikot/viewmodels.declarative.SwiftUI.flow (from `git@github.com:mirego/trikot.git`, tag `5.2.0`)" SPEC REPOS: trunk: + - FirebaseAnalytics + - FirebaseCore + - FirebaseCoreInternal + - FirebaseInstallations + - GoogleAppMeasurement + - GoogleUtilities - Kingfisher - - SwiftGen + - nanopb + - PromisesObjC - SwiftLint - SwiftUIIntrospect @@ -41,13 +122,20 @@ CHECKOUT OPTIONS: :tag: 5.2.0 SPEC CHECKSUMS: + FirebaseAnalytics: 7b41efc4eba5ff841cc94d5994b5f339361258f4 + FirebaseCore: 65a801af84cca84361ef9eac3fd868656968a53b + FirebaseCoreInternal: 26233f705cc4531236818a07ac84d20c333e505a + FirebaseInstallations: b822f91a61f7d1ba763e5ccc9d4f2e6f2ed3b3ee + GoogleAppMeasurement: 079d7632810e9d9704a99932547ba1554acc4342 + GoogleUtilities: 13e2c67ede716b8741c7989e26893d151b2b2084 Kingfisher: 63f677311d36a3473f6b978584f8a3845d023dc5 + nanopb: b552cce312b6c8484180ef47159bc0f65a1f0431 + PromisesObjC: c50d2056b5253dadbd6c2bea79b0674bd5a52fa4 Shared: 185e58fd174d1d4b955d7581bc1bacb9810272fa - SwiftGen: 1366a7f71aeef49954ca5a63ba4bef6b0f24138c SwiftLint: 5ce4d6a8ff83f1b5fd5ad5dbf30965d35af65e44 SwiftUIIntrospect: 53b6a16734c822804ff82c35d9073d8d8edfb085 Trikot: ca201c8ae67ff21f35a60a0bda6c600fbe51b282 -PODFILE CHECKSUM: a6683730b9e619cd4c94d6f20ba4e3f5943142c0 +PODFILE CHECKSUM: 8c6a75418b236f7d0081b7844d58c4e6b129cedb COCOAPODS: 1.13.0 diff --git a/ios/iosApp.xcodeproj/project.pbxproj b/ios/iosApp.xcodeproj/project.pbxproj index 1a07749..3504fbf 100644 --- a/ios/iosApp.xcodeproj/project.pbxproj +++ b/ios/iosApp.xcodeproj/project.pbxproj @@ -34,6 +34,7 @@ 895BED172AF416F5005B1212 /* VMDColor+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 895BED162AF416F5005B1212 /* VMDColor+Extensions.swift */; }; 895BED192AF42C2B005B1212 /* ProjectDetailsContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 895BED182AF42C2B005B1212 /* ProjectDetailsContentView.swift */; }; 895BED1B2AF4469C005B1212 /* NavigationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 895BED1A2AF4469C005B1212 /* NavigationView.swift */; }; + 895BED1D2AF58517005B1212 /* AnalyticsServiceImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 895BED1C2AF58517005B1212 /* AnalyticsServiceImpl.swift */; }; 9B8ACFDB4E332DFCA8B97CBB /* Pods_iosApp.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4E4E1328B104D05A50A097EE /* Pods_iosApp.framework */; }; /* End PBXBuildFile section */ @@ -69,6 +70,7 @@ 895BED162AF416F5005B1212 /* VMDColor+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "VMDColor+Extensions.swift"; sourceTree = ""; }; 895BED182AF42C2B005B1212 /* ProjectDetailsContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectDetailsContentView.swift; sourceTree = ""; }; 895BED1A2AF4469C005B1212 /* NavigationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationView.swift; sourceTree = ""; }; + 895BED1C2AF58517005B1212 /* AnalyticsServiceImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsServiceImpl.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 */ @@ -134,6 +136,7 @@ 89011D4C2AE9A4FC0073544B /* AppEnvironment+iOS.swift */, 89011D4A2AE9A4CD0073544B /* BootstrapImpl.swift */, 89011D502AE9A7750073544B /* ImageProvider.swift */, + 895BED1C2AF58517005B1212 /* AnalyticsServiceImpl.swift */, ); path = Domain; sourceTree = ""; @@ -413,6 +416,7 @@ 895BED192AF42C2B005B1212 /* ProjectDetailsContentView.swift in Sources */, 895BED112AF3FD32005B1212 /* NavigationModifier.swift in Sources */, 89011D512AE9A7750073544B /* ImageProvider.swift in Sources */, + 895BED1D2AF58517005B1212 /* AnalyticsServiceImpl.swift in Sources */, 89011D4B2AE9A4CD0073544B /* BootstrapImpl.swift in Sources */, 895BED032AEFF281005B1212 /* TextStyle.swift in Sources */, 895BECFB2AEAB3DC005B1212 /* RootView.swift in Sources */, diff --git a/ios/iosApp/Domain/AnalyticsServiceImpl.swift b/ios/iosApp/Domain/AnalyticsServiceImpl.swift new file mode 100644 index 0000000..d4e8abd --- /dev/null +++ b/ios/iosApp/Domain/AnalyticsServiceImpl.swift @@ -0,0 +1,20 @@ +import FirebaseAnalytics +import Foundation +import Shared + +public class AnalyticsServiceImpl: SharedAnalyticsService { + + public init(enableAnalytics: Bool = true) { + isEnabled = enableAnalytics + } + + public var isEnabled: Bool { + didSet { + Analytics.setAnalyticsCollectionEnabled(isEnabled) + } + } + + public func trackEvent(event: AnalyticsEvent, properties: [String: Any]) { + Analytics.logEvent(event.name, parameters: properties) + } +} diff --git a/ios/iosApp/Domain/BootstrapImpl.swift b/ios/iosApp/Domain/BootstrapImpl.swift index df75fa2..c26d47f 100644 --- a/ios/iosApp/Domain/BootstrapImpl.swift +++ b/ios/iosApp/Domain/BootstrapImpl.swift @@ -8,5 +8,14 @@ final class BootstrapImpl: Bootstrap { init() { appInformation = AppInformationImpl(environmentKey: environment.key) + + let firebaseAnalyticsService = AnalyticsServiceImpl() + #if DEBUG + firebaseAnalyticsService.isEnabled = false + #else + firebaseAnalyticsService.isEnabled = true + #endif + + SharedAnalyticsConfiguration().analyticsManager = firebaseAnalyticsService } } diff --git a/shared/build.gradle.kts b/shared/build.gradle.kts index e992969..fcdfbd0 100644 --- a/shared/build.gradle.kts +++ b/shared/build.gradle.kts @@ -19,6 +19,7 @@ val TRIKOT_FRAMEWORK_NAME = "Shared" fun org.jetbrains.kotlin.gradle.plugin.mpp.Framework.configureFramework() { baseName = TRIKOT_FRAMEWORK_NAME isStatic = false + export(libs.trikot.analytics) export(libs.trikot.vmd) export(libs.trikot.kword) export(libs.trikot.datasources) @@ -102,6 +103,7 @@ kotlin { api(libs.koin.core) implementation(libs.okio) implementation(libs.skie) + api(libs.trikot.analytics) api(libs.trikot.vmd.annotations) api(libs.trikot.datasources) api(libs.trikot.kword) diff --git a/shared/src/commonMain/kotlin/com/mirego/kmp/boilerplate/analytics/EmptySharedAnalyticsService.kt b/shared/src/commonMain/kotlin/com/mirego/kmp/boilerplate/analytics/EmptySharedAnalyticsService.kt new file mode 100644 index 0000000..1f89368 --- /dev/null +++ b/shared/src/commonMain/kotlin/com/mirego/kmp/boilerplate/analytics/EmptySharedAnalyticsService.kt @@ -0,0 +1,12 @@ +package com.mirego.kmp.boilerplate.analytics + +import com.mirego.trikot.analytics.AnalyticsEvent +import com.mirego.trikot.analytics.AnalyticsPropertiesType + +class EmptySharedAnalyticsService : SharedAnalyticsService { + override var isEnabled: Boolean = false + + override fun trackEvent(event: AnalyticsEvent, properties: AnalyticsPropertiesType) { + // No-Op + } +} diff --git a/shared/src/commonMain/kotlin/com/mirego/kmp/boilerplate/analytics/SharedAnalyticsConfiguration.kt b/shared/src/commonMain/kotlin/com/mirego/kmp/boilerplate/analytics/SharedAnalyticsConfiguration.kt new file mode 100644 index 0000000..d236cbb --- /dev/null +++ b/shared/src/commonMain/kotlin/com/mirego/kmp/boilerplate/analytics/SharedAnalyticsConfiguration.kt @@ -0,0 +1,5 @@ +package com.mirego.kmp.boilerplate.analytics + +object SharedAnalyticsConfiguration { + var analyticsManager: SharedAnalyticsService = EmptySharedAnalyticsService() +} diff --git a/shared/src/commonMain/kotlin/com/mirego/kmp/boilerplate/analytics/SharedAnalyticsService.kt b/shared/src/commonMain/kotlin/com/mirego/kmp/boilerplate/analytics/SharedAnalyticsService.kt new file mode 100644 index 0000000..cd5d468 --- /dev/null +++ b/shared/src/commonMain/kotlin/com/mirego/kmp/boilerplate/analytics/SharedAnalyticsService.kt @@ -0,0 +1,9 @@ +package com.mirego.kmp.boilerplate.analytics + +import com.mirego.trikot.analytics.AnalyticsEvent +import com.mirego.trikot.analytics.AnalyticsPropertiesType + +interface SharedAnalyticsService { + var isEnabled: Boolean + fun trackEvent(event: AnalyticsEvent, properties: AnalyticsPropertiesType) +} diff --git a/shared/src/commonTest/kotlin/com/mirego/kmp/boilerplate/usecase/ProjectsUseCaseTestImpl.kt b/shared/src/commonTest/kotlin/com/mirego/kmp/boilerplate/usecase/ProjectsUseCaseTestImpl.kt index cee7c4c..68ccec3 100644 --- a/shared/src/commonTest/kotlin/com/mirego/kmp/boilerplate/usecase/ProjectsUseCaseTestImpl.kt +++ b/shared/src/commonTest/kotlin/com/mirego/kmp/boilerplate/usecase/ProjectsUseCaseTestImpl.kt @@ -74,6 +74,8 @@ class ProjectsUseCaseTestImpl : BaseTest() { listImageUrl = "https://miregologo.com", client = ProjectsQuery.Data.PagePage.ProjectsListBlock.Projects.Entry.Client( name = "Mirego" - ) + ), + "000000", + "FFFFFF" ) } From e0d42b0e6ff9eff0a86876d8affe828a8545ceea Mon Sep 17 00:00:00 2001 From: Olivier Pineau Date: Sat, 4 Nov 2023 13:32:06 -0400 Subject: [PATCH 2/6] Analytics tracking --- .../ProjectDetails/ProjectDetailsView.swift | 1 + .../kmp/boilerplate/analytics/Analytics.kt | 39 +++++++++++++++++++ .../ProjectDetailsViewModelImpl.kt | 6 ++- .../projects/ProjectsViewModelImpl.kt | 7 +++- 4 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 shared/src/commonMain/kotlin/com/mirego/kmp/boilerplate/analytics/Analytics.kt diff --git a/ios/iosApp/UI/ProjectDetails/ProjectDetailsView.swift b/ios/iosApp/UI/ProjectDetails/ProjectDetailsView.swift index 62c0d0b..9828092 100644 --- a/ios/iosApp/UI/ProjectDetails/ProjectDetailsView.swift +++ b/ios/iosApp/UI/ProjectDetails/ProjectDetailsView.swift @@ -36,6 +36,7 @@ struct ProjectDetailsView: View { } } } + .handleNavigation(viewModel, route: viewModel.navigationRoute) } @ViewBuilder private var contentView: some View { diff --git a/shared/src/commonMain/kotlin/com/mirego/kmp/boilerplate/analytics/Analytics.kt b/shared/src/commonMain/kotlin/com/mirego/kmp/boilerplate/analytics/Analytics.kt new file mode 100644 index 0000000..c81082b --- /dev/null +++ b/shared/src/commonMain/kotlin/com/mirego/kmp/boilerplate/analytics/Analytics.kt @@ -0,0 +1,39 @@ +@file:Suppress("EnumEntryName") + +package com.mirego.kmp.boilerplate.analytics + +import com.mirego.trikot.analytics.AnalyticsConfiguration +import com.mirego.trikot.analytics.AnalyticsEvent + +object Analytics { + private const val PARAM_SCREEN_TITLE = "screen_title" + private const val PARAM_PROJECT_ID = "project_id" + + fun trackScreenView(screen: ScreenName) { + AnalyticsConfiguration.analyticsManager.trackEvent( + event = AnalyticsEvents.screen_view, + properties = mapOf( + PARAM_SCREEN_TITLE to screen.name + ) + ) + } + + fun trackViewProject(projectId: String) { + AnalyticsConfiguration.analyticsManager.trackEvent( + event = AnalyticsEvents.view_project, + properties = mapOf( + PARAM_PROJECT_ID to projectId + ) + ) + } +} + +enum class AnalyticsEvents : AnalyticsEvent { + screen_view, + view_project +} + +enum class ScreenName { + projects, + project_details +} diff --git a/shared/src/commonMain/kotlin/com/mirego/kmp/boilerplate/viewmodel/projectdetails/ProjectDetailsViewModelImpl.kt b/shared/src/commonMain/kotlin/com/mirego/kmp/boilerplate/viewmodel/projectdetails/ProjectDetailsViewModelImpl.kt index 465c386..58c0800 100644 --- a/shared/src/commonMain/kotlin/com/mirego/kmp/boilerplate/viewmodel/projectdetails/ProjectDetailsViewModelImpl.kt +++ b/shared/src/commonMain/kotlin/com/mirego/kmp/boilerplate/viewmodel/projectdetails/ProjectDetailsViewModelImpl.kt @@ -1,5 +1,7 @@ package com.mirego.kmp.boilerplate.viewmodel.projectdetails +import com.mirego.kmp.boilerplate.analytics.Analytics +import com.mirego.kmp.boilerplate.analytics.ScreenName import com.mirego.kmp.boilerplate.localization.KWordTranslation import com.mirego.kmp.boilerplate.usecase.preview.ProjectDetailsUseCasePreview import com.mirego.kmp.boilerplate.usecase.projectdetails.ProjectDetailsUseCase @@ -29,7 +31,9 @@ class ProjectDetailsViewModelImpl( closeAction: () -> Unit, coroutineScope: CoroutineScope ) : ProjectDetailsViewModel, BaseProjectDetailsViewModelImpl( - onTrackScreenView = {}, + onTrackScreenView = { + Analytics.trackScreenView(ScreenName.project_details) + }, viewModelFactory = viewModelFactory, coroutineScope = coroutineScope ) { diff --git a/shared/src/commonMain/kotlin/com/mirego/kmp/boilerplate/viewmodel/projects/ProjectsViewModelImpl.kt b/shared/src/commonMain/kotlin/com/mirego/kmp/boilerplate/viewmodel/projects/ProjectsViewModelImpl.kt index aad482b..757a444 100644 --- a/shared/src/commonMain/kotlin/com/mirego/kmp/boilerplate/viewmodel/projects/ProjectsViewModelImpl.kt +++ b/shared/src/commonMain/kotlin/com/mirego/kmp/boilerplate/viewmodel/projects/ProjectsViewModelImpl.kt @@ -1,5 +1,7 @@ package com.mirego.kmp.boilerplate.viewmodel.projects +import com.mirego.kmp.boilerplate.analytics.Analytics +import com.mirego.kmp.boilerplate.analytics.ScreenName import com.mirego.kmp.boilerplate.extension.prioritiseData import com.mirego.kmp.boilerplate.localization.KWordTranslation import com.mirego.kmp.boilerplate.usecase.preview.ProjectsUseCasePreview @@ -30,7 +32,9 @@ class ProjectsViewModelImpl( viewModelFactory: ViewModelFactory, coroutineScope: CoroutineScope ) : ProjectsViewModel, BaseProjectsViewModelImpl( - onTrackScreenView = {}, + onTrackScreenView = { + Analytics.trackScreenView(ScreenName.projects) + }, viewModelFactory = viewModelFactory, coroutineScope = coroutineScope ) { @@ -95,6 +99,7 @@ class ProjectsViewModelImpl( placeholderImageResource = SharedImageResource.imagePlaceholder ), tapAction = { + Analytics.trackViewProject(projectId = id) navigateToProjectDetails( ProjectDetailsNavigationData( id = id, From 8314deaca6949e6e16ed10741d69a3e30d6e276f Mon Sep 17 00:00:00 2001 From: Olivier Pineau Date: Mon, 6 Nov 2023 15:00:42 -0500 Subject: [PATCH 3/6] Dummy firebase initialization --- ios/iosApp.xcodeproj/project.pbxproj | 4 +++ ios/iosApp/AppInitializer.swift | 23 ++++++++++++- ios/iosApp/Domain/BootstrapImpl.swift | 9 ------ ios/iosApp/ToReplace-GoogleService-Info.plist | 32 +++++++++++++++++++ 4 files changed, 58 insertions(+), 10 deletions(-) create mode 100644 ios/iosApp/ToReplace-GoogleService-Info.plist diff --git a/ios/iosApp.xcodeproj/project.pbxproj b/ios/iosApp.xcodeproj/project.pbxproj index 3504fbf..61bf733 100644 --- a/ios/iosApp.xcodeproj/project.pbxproj +++ b/ios/iosApp.xcodeproj/project.pbxproj @@ -35,6 +35,7 @@ 895BED192AF42C2B005B1212 /* ProjectDetailsContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 895BED182AF42C2B005B1212 /* ProjectDetailsContentView.swift */; }; 895BED1B2AF4469C005B1212 /* NavigationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 895BED1A2AF4469C005B1212 /* NavigationView.swift */; }; 895BED1D2AF58517005B1212 /* AnalyticsServiceImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 895BED1C2AF58517005B1212 /* AnalyticsServiceImpl.swift */; }; + 895BED1F2AF9780C005B1212 /* ToReplace-GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 895BED1E2AF9780C005B1212 /* ToReplace-GoogleService-Info.plist */; }; 9B8ACFDB4E332DFCA8B97CBB /* Pods_iosApp.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4E4E1328B104D05A50A097EE /* Pods_iosApp.framework */; }; /* End PBXBuildFile section */ @@ -71,6 +72,7 @@ 895BED182AF42C2B005B1212 /* ProjectDetailsContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectDetailsContentView.swift; sourceTree = ""; }; 895BED1A2AF4469C005B1212 /* NavigationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationView.swift; sourceTree = ""; }; 895BED1C2AF58517005B1212 /* AnalyticsServiceImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsServiceImpl.swift; sourceTree = ""; }; + 895BED1E2AF9780C005B1212 /* ToReplace-GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "ToReplace-GoogleService-Info.plist"; 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 */ @@ -125,6 +127,7 @@ 058557BA273AAA24004C7B11 /* Assets.xcassets */, 895BED042AEFFEA2005B1212 /* Colors.xcassets */, 058557D7273AAEEB004C7B11 /* Preview Content */, + 895BED1E2AF9780C005B1212 /* ToReplace-GoogleService-Info.plist */, ); path = iosApp; sourceTree = ""; @@ -308,6 +311,7 @@ buildActionMask = 2147483647; files = ( 895BED052AEFFEA2005B1212 /* Colors.xcassets in Resources */, + 895BED1F2AF9780C005B1212 /* ToReplace-GoogleService-Info.plist in Resources */, 058557D9273AAEEB004C7B11 /* Preview Assets.xcassets in Resources */, 058557BB273AAA24004C7B11 /* Assets.xcassets in Resources */, ); diff --git a/ios/iosApp/AppInitializer.swift b/ios/iosApp/AppInitializer.swift index 79768a2..3a97ca6 100644 --- a/ios/iosApp/AppInitializer.swift +++ b/ios/iosApp/AppInitializer.swift @@ -1,14 +1,35 @@ +import FirebaseCore import Foundation import Kingfisher import Shared import Trikot enum AppInitializer { - static func initializeComponents(environment: AppEnvironment) { + static func initializeComponents(environment _: AppEnvironment) { + initializeFirebase() initializeCommon() initializeKingfisher() } + private static func initializeFirebase() { + guard + let filePath = Bundle.main.path(forResource: "ToReplace-GoogleService-Info", ofType: "plist"), + let firebaseOptions = FirebaseOptions(contentsOfFile: filePath) else { + return + } + + FirebaseApp.configure(options: firebaseOptions) + + let firebaseAnalyticsService = AnalyticsServiceImpl() + #if DEBUG + firebaseAnalyticsService.isEnabled = false + #else + firebaseAnalyticsService.isEnabled = true + #endif + + SharedAnalyticsConfiguration().analyticsManager = firebaseAnalyticsService + } + private static func initializeCommon() { TrikotKword.shared.setCurrentLanguage(Foundation.Locale.isPreferredLanguagesFrench ? "fr" : "en") TrikotViewModelDeclarative.shared.initialize( diff --git a/ios/iosApp/Domain/BootstrapImpl.swift b/ios/iosApp/Domain/BootstrapImpl.swift index c26d47f..df75fa2 100644 --- a/ios/iosApp/Domain/BootstrapImpl.swift +++ b/ios/iosApp/Domain/BootstrapImpl.swift @@ -8,14 +8,5 @@ final class BootstrapImpl: Bootstrap { init() { appInformation = AppInformationImpl(environmentKey: environment.key) - - let firebaseAnalyticsService = AnalyticsServiceImpl() - #if DEBUG - firebaseAnalyticsService.isEnabled = false - #else - firebaseAnalyticsService.isEnabled = true - #endif - - SharedAnalyticsConfiguration().analyticsManager = firebaseAnalyticsService } } diff --git a/ios/iosApp/ToReplace-GoogleService-Info.plist b/ios/iosApp/ToReplace-GoogleService-Info.plist new file mode 100644 index 0000000..58af956 --- /dev/null +++ b/ios/iosApp/ToReplace-GoogleService-Info.plist @@ -0,0 +1,32 @@ + + + + + ANDROID_CLIENT_ID + 123456789012-0cnvm52tlj4cn7rkfujdkuq3iq79v5ig.apps.googleusercontent.com + API_KEY + AIzaSyC3_0lgMdiPr41DjqzSWP2MqGIqIakley0 + GCM_SENDER_ID + 123456789012 + PLIST_VERSION + 1 + BUNDLE_ID + com.mirego.kmp.boilerplate + PROJECT_ID + boilerplate-1234567890123 + STORAGE_BUCKET + boilerplate-1234567890123.appspot.com + IS_ADS_ENABLED + + IS_ANALYTICS_ENABLED + + IS_APPINVITE_ENABLED + + IS_GCM_ENABLED + + IS_SIGNIN_ENABLED + + GOOGLE_APP_ID + 1:123456789012:ios:c930c9194f493843b44d0a + + From 448db2bde5b7373aa15d730ce98ddfcd2a604035 Mon Sep 17 00:00:00 2001 From: Olivier Pineau Date: Tue, 7 Nov 2023 10:57:07 -0500 Subject: [PATCH 4/6] fix --- ios/iosApp.xcodeproj/project.pbxproj | 8 ++++---- ios/iosApp/AppInitializer.swift | 8 +------- ...-GoogleService-Info.plist => GoogleService-Info.plist} | 0 3 files changed, 5 insertions(+), 11 deletions(-) rename ios/iosApp/{ToReplace-GoogleService-Info.plist => GoogleService-Info.plist} (100%) diff --git a/ios/iosApp.xcodeproj/project.pbxproj b/ios/iosApp.xcodeproj/project.pbxproj index 61bf733..6d65489 100644 --- a/ios/iosApp.xcodeproj/project.pbxproj +++ b/ios/iosApp.xcodeproj/project.pbxproj @@ -35,7 +35,7 @@ 895BED192AF42C2B005B1212 /* ProjectDetailsContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 895BED182AF42C2B005B1212 /* ProjectDetailsContentView.swift */; }; 895BED1B2AF4469C005B1212 /* NavigationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 895BED1A2AF4469C005B1212 /* NavigationView.swift */; }; 895BED1D2AF58517005B1212 /* AnalyticsServiceImpl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 895BED1C2AF58517005B1212 /* AnalyticsServiceImpl.swift */; }; - 895BED1F2AF9780C005B1212 /* ToReplace-GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 895BED1E2AF9780C005B1212 /* ToReplace-GoogleService-Info.plist */; }; + 895BED1F2AF9780C005B1212 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 895BED1E2AF9780C005B1212 /* GoogleService-Info.plist */; }; 9B8ACFDB4E332DFCA8B97CBB /* Pods_iosApp.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4E4E1328B104D05A50A097EE /* Pods_iosApp.framework */; }; /* End PBXBuildFile section */ @@ -72,7 +72,7 @@ 895BED182AF42C2B005B1212 /* ProjectDetailsContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProjectDetailsContentView.swift; sourceTree = ""; }; 895BED1A2AF4469C005B1212 /* NavigationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationView.swift; sourceTree = ""; }; 895BED1C2AF58517005B1212 /* AnalyticsServiceImpl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsServiceImpl.swift; sourceTree = ""; }; - 895BED1E2AF9780C005B1212 /* ToReplace-GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "ToReplace-GoogleService-Info.plist"; sourceTree = ""; }; + 895BED1E2AF9780C005B1212 /* GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; 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 */ @@ -127,7 +127,7 @@ 058557BA273AAA24004C7B11 /* Assets.xcassets */, 895BED042AEFFEA2005B1212 /* Colors.xcassets */, 058557D7273AAEEB004C7B11 /* Preview Content */, - 895BED1E2AF9780C005B1212 /* ToReplace-GoogleService-Info.plist */, + 895BED1E2AF9780C005B1212 /* GoogleService-Info.plist */, ); path = iosApp; sourceTree = ""; @@ -311,7 +311,7 @@ buildActionMask = 2147483647; files = ( 895BED052AEFFEA2005B1212 /* Colors.xcassets in Resources */, - 895BED1F2AF9780C005B1212 /* ToReplace-GoogleService-Info.plist in Resources */, + 895BED1F2AF9780C005B1212 /* GoogleService-Info.plist in Resources */, 058557D9273AAEEB004C7B11 /* Preview Assets.xcassets in Resources */, 058557BB273AAA24004C7B11 /* Assets.xcassets in Resources */, ); diff --git a/ios/iosApp/AppInitializer.swift b/ios/iosApp/AppInitializer.swift index 3a97ca6..69fb9b8 100644 --- a/ios/iosApp/AppInitializer.swift +++ b/ios/iosApp/AppInitializer.swift @@ -12,13 +12,7 @@ enum AppInitializer { } private static func initializeFirebase() { - guard - let filePath = Bundle.main.path(forResource: "ToReplace-GoogleService-Info", ofType: "plist"), - let firebaseOptions = FirebaseOptions(contentsOfFile: filePath) else { - return - } - - FirebaseApp.configure(options: firebaseOptions) + FirebaseApp.configure() let firebaseAnalyticsService = AnalyticsServiceImpl() #if DEBUG diff --git a/ios/iosApp/ToReplace-GoogleService-Info.plist b/ios/iosApp/GoogleService-Info.plist similarity index 100% rename from ios/iosApp/ToReplace-GoogleService-Info.plist rename to ios/iosApp/GoogleService-Info.plist From 25fcc1d4cdcdd1c0082b07321f3ac4675c8e71ec Mon Sep 17 00:00:00 2001 From: Olivier Pineau Date: Tue, 7 Nov 2023 10:59:09 -0500 Subject: [PATCH 5/6] oups --- ios/Podfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/Podfile b/ios/Podfile index 75daacd..66e4718 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -7,7 +7,7 @@ inhibit_all_warnings! target 'iosApp' do use_frameworks! platform :ios, $deploymentTarget - # Thirdo-party + # Third-party pod 'FirebaseCore' pod 'FirebaseAnalytics' From 46a8dc943f921bcabcd280b151045fb155f3c23a Mon Sep 17 00:00:00 2001 From: Olivier Pineau Date: Tue, 7 Nov 2023 11:00:54 -0500 Subject: [PATCH 6/6] remove comment --- gradle/libs.versions.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 12c2810..6d23505 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -38,7 +38,6 @@ 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" } -#android-play-services-measurement-api = { group = "com.google.android.gms", name = "play-services-measurement-api", version.ref = "play-services-measurement-api" } android-firebase-bom = { module = "com.google.firebase:firebase-bom", version.ref = "firebase" } android-firebase-crashlytics = { module = "com.google.firebase:firebase-crashlytics-ktx" } android-firebase-analytics = { module = "com.google.firebase:firebase-analytics-ktx" }