Мы используем текущую версию Firebase iOS Framework (5.9.0) и видим странную проблему при попытке запустить A / B тестовые эксперименты, в которых есть событие активации.
Смы хотим запустить эксперименты при первом запуске , у нас при запуске приложения есть собственный заставочный экран, который мы отображаем во время выборки удаленной конфигурации.После завершения выборки мы немедленно активируем выбранную конфигурацию и затем проверяем, получили ли мы информацию об участии в эксперименте, чтобы соответствующим образом перенастроить следующий пользовательский интерфейс.Существуют дополнительные проверки, прежде чем мы определим, что текущий экземпляр фактически должен быть частью теста, то есть события активации.По сути, код выглядит следующим образом:
<code that shows splash>
…
[[FIRRemoteConfig remoteConfig] fetchWithExpirationDuration:7 completionHandler:^(FIRRemoteConfigFetchStatus status, NSError * _Nullable error) {
[[FIRRemoteConfig remoteConfig] activateFetched];
if (<checks that see if we received info about being selected to participate in the experiment and if local conditions are met for experiment participation>) {
[FIRAnalytics logEventWithName:@"RegistrationEntryExperimentActivation" parameters:nil];
<dismiss splash screen and show next UI screen based on experiment variation received in remote config>
} else {
<dismiss splash screen and show next UI screen>
}
}
При описанном выше подходе (который является полностью прямым IMO) не работает правильно.Проведя время с включенным ведением журнала отладчика и Firebase, я вижу в журнале, что возникает проблема состояния гонки.По сути, вызов Firebase activateFetched()
не устанавливает синхронно «условный идентификатор экспериментального свойства пользователя» внутри вызова activFetched, а вместо этого устанавливает его некоторое время спустя.Из-за этого срабатывание события активации сразу после activFetched не вызывает этого условного пользовательского свойства, и последующие события последовательности / цели эксперимента не помечаются должным образом как часть эксперимента (эксперимент даже не активируется в первую очередь).
Если мы изменим код, чтобы задержать отправку события активации на некоторую произвольную задержку:
<code that shows splash>
…
[[FIRRemoteConfig remoteConfig] fetchWithExpirationDuration:7 completionHandler:^(FIRRemoteConfigFetchStatus status, NSError * _Nullable error) {
[[FIRRemoteConfig remoteConfig] activateFetched];
if (<checks that see if we received info about being selected to participate in the experiment and if local conditions are met for experiment participation>) {
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[FIRAnalytics logEventWithName:@"RegistrationEntryExperimentActivation" parameters:nil];
<dismiss splash screen and show next UI screen based on experiment variation received in remote config>
}
} else {
<dismiss splash screen and show next UI screen>
}
}
, условное пользовательское свойство для эксперимента будет правильно настроено заранее и будет вызвано событием (вызываяактивация эксперимента и последующие события правильно помечены как часть эксперимента).
Теперь этот код, очевидно, довольно уродлив и склонен к возможным гоночным условиям.Задержка 0,5 секунды консервативно настроена на то, что, надеюсь, будет достаточно на всех устройствах iOS, но ¯_ (ツ) _ / ¯.Я прочитал доступную документацию несколько раз и попытался просмотреть все доступные методы API, но безуспешно выяснил, какой должна быть правильная точка начала отправки событий.Если в методе activateFetched
используется асинхронный процесс перенастройки внутренних объектов, можно ожидать, что метод обратного вызова указывает вызывающей стороне момент времени, когда все выполнено, перенастраивается и готов к дальнейшему использованию приложением.Похоже, что разработчики фреймворков не ожидали сценария использования, когда кто-то должен отправить событие активации сразу после активации профиля удаленной конфигурации ...
Кто-нибудь еще сталкивался с этой проблемой?Мы что-то упускаем в API?Есть ли более разумный способ дать activateFetched
закончить свое дело?
Надеюсь, что некоторые инженеры Firebase также могут присоединиться к своей мудрости:)
Спасибо