Firebase Analytics в KMP: Android, iOS, Desktop (MacOS, Windows)

от автора

Я — Денис, Middle Android-разработчик в «Black Bricks». В этой статье я расскажу о том как я подключил Firebase Analytics в KMP проект для Android, iOS, Desktop (MacOS, Windows).

Раньше мне приходилось подключать аналитику в проекты, но это всегда были нативные мобильные приложения. И я сильно удивился, когда не увидел в Firebase консоли:

  1. поддержки Desktop;

  2. поддержки KMP.

Я начал ресёрчинг библиотек и инструментов, похожих на Firebase. На самом деле не очень хотелось использовать сервис, отличный от Firebase, потому что за много лет уже многие привыкли к его интерфейсу и возможностям. Я искал альтернативы, посмотрел Mixpanel, Flurry, Amplitude — но все они, в любом случае, были в основном также только для мобильных телефонов и показались чуть сложнее, чем привычный Firebase.

Тут я наткнулся на KMP-библиотеку, обёртку над популярными сервисами Firebase. Но аналитики тут я не нашёл. Я посмотрел, как организован интероп Crashlytics в этой библиотеке и даже попытался написать нечто похожее для моей задачи. Увы, не вышло.

Библиотека-обёртка над сервисами Firebase

Библиотека-обёртка над сервисами Firebase

На второй день ресёрча я просто создал 4 проекта в Firebase под Desktop MacOS, Desktop Windows, Android и iOS. Уже отчаявшись, нашёл способ взаимодействия с Firebase через их REST API — этот способ как раз отлично подошёл для моей задачи. Далее подробнее о нём расскажу.

Как уже писал ранее, нужно было создать 4 приложения в рамках одной консоли под все ОС, которые мы поддерживаем на проекте. Для этого решения нам даже не нужно добавлять google-services.json файлы в проект, если вы, конечно, не используете что-то ещё кроме аналитики. Далее дело за малым — написать REST код для аналитики.

Базовая семантика двух функций, которые отсылают события в аналитику с параметрами или без.

expect suspend fun logFirebaseEvent(event: FirebaseAnalyticsEvent, params: Map<FirebaseAnalyticsParameter, String>) expect suspend fun logFirebaseEvent(event: FirebaseAnalyticsEvent)

Под каждую платформу нужно написать реализацию этих функций, чуть проще это сделать для мобилок, просто вызываем нужные UseCase.

actual suspend fun logFirebaseEvent(event: FirebaseAnalyticsEvent, params: Map<FirebaseAnalyticsParameter, String>) {      SendAndroidAnalyticsEventUseCase()          .execute(event = event, params = params)  }   actual suspend fun logFirebaseEvent(event: FirebaseAnalyticsEvent) {      SendAndroidAnalyticsEventUseCase()          .execute(event = event)  }

Для разделения аналитики на Desktop под MacOS и Windows — я написал метод для определения под какой ОС он запущен.

actual fun getPlatform(): Platform = object : Platform {      override val name: String          get() {              val osName = System.getProperty("os.name").lowercase(Locale.ROOT)              return when {                  osName.contains("mac") -> "macOS"                  osName.contains("win") -> "Windows"                  else -> "Unknown"              }          }  }

Сам UseCase вызывает REST API метод и варьирует полями, о которых чуть позже.

class SendIphoneAnalyticsEventUseCase : KoinComponent {      private val firebaseAnalyticsRepo: FirebaseAnalyticsRepo = get()       @NativeCoroutines      suspend fun execute(          event: FirebaseAnalyticsEvent,          params: Map<FirebaseAnalyticsParameter, String>? = null,      ) {          firebaseAnalyticsRepo.sendIphoneEvent(              event = Event(                  name = event.eventName,                  params = params?.mapKeys { it.key.paramName },              ),          )      }  }

Для REST запросов я использую Ktorfit, это обёртка над Ktor — вполне удобно использовать в KMP.

private const val BASE_FIREBASE_URL = "https://www.google-analytics.com/mp/collect"  interface AnalyticsApi {      @POST(BASE_FIREBASE_URL)      @Headers(          value = [              "${ParseHeaders.CONTENT_TYPE}: application/json",          ],      )      suspend fun logEvent(          @Query("firebase_app_id") firebaseAppId: String,          @Query("api_secret") apiSecret: String,          @Body events: FirebaseEvent,      )  }

1. clientId — всегда одинаковый, можно посмотреть в карточке проекта в поле Project number;

2. apiSecret — уникальное значение для каждого проекта, создаётся вручную в аккаунте Google аналитики;

3. firebaseAppId — уникальное значение для каждого проекта, можно взять из поля mobilesdk_app_id в google-services.json.

Структура, которую отправляю по REST:

@Serializable data class FirebaseEvent(      @SerialName("client_id")      val clientId: String,      val events: List<Event>,  )   @Serializable  data class Event(      val name: String,      val params: Map<String, String>? = null,  )

Для каждого UseCase я подставляю разные значения этих полей из BuildConfig. А сами методы аналитики вызываю в UI или бизнес-логике:

LaunchedEffect(Unit) {     logFirebaseEvent(         event = FirebaseAnalyticsEvent.Event,         params = mapOf(FirebaseAnalyticsParameter.Param to param),     ) }

Спасибо за чтение!

Денис Попков

Middle Android разработчик в «Лайв Тайпинге»

Если вы нашли неточности/ошибки в статье или просто хотите дополнить её своим мнением — то прошу в комментарии! Или можете написать мне в Telegram — t.me/MolodoyDenis.


ссылка на оригинал статьи https://habr.com/ru/articles/818891/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *