Flutter

Пакет unolink — это тонкий слой поверх нативных SDK для iOS и Android, которые поставляются внутри него. Ему не нужны разрешения кроме доступа в интернет и записи в Info.plist. Опубликован на pub.dev издателем unolink.dev. Тариф Pro и выше.

Требования

  • Flutter 3.22+ (Dart 3.4+).
  • Android 6.0+ (minSdk 23) и iOS 13+.
  • Ключ SDK: в панели откройте приложение → Настройки → Ключи SDK → Новый ключ. Он показывается один раз, поэтому скопируйте его сразу.

Установка

flutter pub add unolink

Это добавит unolink: ^0.3.3 в pubspec.yaml и загрузит пакет. После этого пересоберите приложение; после добавления нативного кода hot reload недостаточно.

Если CocoaPods падает при сборке iOS с ошибкой кодировки (Ruby 4 или локаль не в UTF-8), запустите его сами в UTF-8, а затем соберите снова:

cd ios && LANG=en_US.UTF-8 pod install

Настройка

Вызовите configure один раз в main(), после WidgetsFlutterBinding.ensureInitialized() и до runApp:

import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:unolink/unolink.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Unolink.configure('ulk_…', debug: kDebugMode);
  runApp(const MyApp());
}

Параметры

ПараметрПо умолчаниюЧто делает
debugfalseПомечает эту установку как тестовое устройство. Передавайте kDebugMode. Панели скрывают тестовые устройства, если не включено Учитывать тестовые устройства.
logLevelUnolinkLogLevel.warnСколько выводится в консоль Xcode или Logcat (тег Unolink): none, error, warn, info, debug.
baseUrlhttps://api.unolink.devSDK API. Оставьте как есть.

Ключ публичный: он поставляется внутри вашего приложения и может отправлять данные только для этого одного приложения. См. Ключи SDK.

События воронки

Добавьте по одной строке в каждый важный момент. Они станут шагами в Воронке и «Путях».

Unolink.onboardingCompleted();                     // the last onboarding screen is done
Unolink.paywallViewed(placement: 'onboarding');    // a paywall appeared; placement says which one
Unolink.paywallSkipped(placement: 'onboarding');   // closed without buying
Unolink.signUp(method: 'apple');                   // an account was created
Unolink.signUpSkipped();                           // went on as a guest (0.3.2+)

Без RevenueCat сообщайте о пробных периодах и покупках сами. Сумма передаётся строкой, поэтому она никогда не округляется:

Unolink.trialStarted(productId: 'pro_yearly');
Unolink.purchase('49.99', 'USD', productId: 'pro_yearly');
Unolink.purchaseCancelled(productId: 'pro_yearly');              // closed the purchase sheet
Unolink.purchaseFailed(productId: 'pro_yearly', reason: 'payment_failed');

Ваши собственные события принимают имя и до 20 свойств:

Unolink.track('level_up', {'level': 3});

Полный список с названиями, которые попадают в панель, — в разделе События.

ID пользователя и RevenueCat

Если вы используете RevenueCat, подключите его в приложении → Настройки → RevenueCat. Тогда не вызывайте purchase и trialStarted: RevenueCat сам отправляет пробные периоды, покупки, продления, отмены и возвраты, а Unolink игнорирует вызовы покупок из приложения, чтобы ничего не учитывалось дважды.

Чтобы привязать эти покупки к установке, сообщайте собственный app user ID RevenueCat. В SDK 0.3.2 и новее одна строка поддерживает его в актуальном состоянии. Вызовите её один раз при запуске, до или после configure:

Unolink.connectRevenueCat(
  () => Purchases.appUserID,
  addListener: Purchases.addCustomerInfoUpdateListener,
  removeListener: Purchases.removeCustomerInfoUpdateListener,
);

Unolink считывает ID после настройки, при каждом обновлении данных клиента (покупки, logIn, logOut) и каждый раз, когда приложение возобновляется, и отправляет его, только если он изменился. Если RevenueCat ещё не настроен, чтение тихо завершается неудачей и повторяется позже. Вызов возвращает функцию для отключения. Поэтому на Flutter Unolink.revenueCatUserChanged() обычно не нужен.

Без этого (или до версии 0.3.2) вызывайте setUserId с ID из RevenueCat после Purchases.configure, после каждого Purchases.logIn и Purchases.logOut и при каждом запуске. Используйте что-то одно, а не оба способа сразу:

// after Purchases.configure, logIn and logOut, and on every launch
Unolink.setUserId(await Purchases.appUserID);

Для гостя это анонимный ID RevenueCat, поэтому покупка, сделанная до регистрации, сразу привязывается к установке. После logIn это ID аккаунта. Учитываются все ID, которые сообщала установка, поэтому покупка гостя остаётся привязанной. См. Гости и аккаунты.

Unolink никогда не хранит сам ID, только односторонний хеш, который у каждого вашего приложения свой.

На Android, когда человек устанавливает приложение по ссылке Unolink с диплинком, слушатель срабатывает один раз, при первом открытии. На iPhone он не вызывается: у Apple нет реферера установки, а Unolink никогда не использует фингерпринтинг.

Unolink.setDeferredDeepLinkListener((link) {
  // link.deepLink: '/promo/3for2', link.params: {'offer': '3for2'}
  // link.linkSlug: 'tiktok' or null, link.clickId: 'clk_01JA…'
  if (link.deepLink != null) router.go(link.deepLink!);
  Unolink.consumeDeferredDeepLink();
});

Слушатель вызывается один раз на установку. Ссылка, пришедшая до того, как вы его задали, доставляется, когда вы его зададите.

Откройте её после онбординга

Первое открытие обычно начинается с онбординга. Если экран ссылки находится за онбордингом или входом в аккаунт, не открывайте его из слушателя. Когда онбординг (или вход) завершён, прочитайте ожидающую ссылку, откройте её, а затем отметьте как использованную:

// when onboarding (or sign-in) is done, and on every launch after that until it's consumed
final link = await Unolink.getPendingDeferredDeepLink();
if (link?.deepLink != null) {
  router.go(link!.deepLink!);
  await Unolink.consumeDeferredDeepLink();
}

Ссылка ждёт, пока вы её не используете, даже если приложение за это время закрывали (до 30 дней после установки; 0.3.3+). Новый пользователь, который бросил онбординг на середине, всё равно получит её при следующем запуске, так что хранить её самим не нужно. Вызовите consumeDeferredDeepLink(), когда откроете её: до этого getPendingDeferredDeepLink() возвращает её при каждом запуске. Он возвращает null до configure и пока Google Play не ответил, а на iPhone всегда null.

Задайте Диплинк и Параметры ссылки в панели (Ссылки → ⋯ → Изменить). Как это работает, описано в разделе Отложенные диплинки на Android.

Другие вызовы

ВызовЧто делает
await Unolink.getInstallId()Случайный ID этой установки или null до configure. Он не идентифицирует ни человека, ни устройство.
Unolink.flush()Отправляет события из очереди прямо сейчас. Обычно не нужен.
Unolink.setEnabled(false)Отключает отслеживание и удаляет события из очереди, для вашей настройки отказа. true включает его снова.

Тестовые устройства

С debug: kDebugMode debug-сборки считаются тестовыми устройствами. Их данные настоящие, но скрыты, пока вы не включите Включать тестовые устройства в Воронке и «Путях». Подробнее в разделе Тестирование.

Конфиденциальность

  • Без IDFA, без Google Advertising ID, без запроса App Tracking Transparency, без фингерпринтинга.
  • Случайный ID установки, версия приложения, версия ОС, версия SDK, телефон или планшет, язык. Страна определяется на границе сети. IP-адреса никогда не хранятся.
  • На iOS манифест конфиденциальности нативного SDK входит в пакет: аналитика, не связанная с личностью.
  • Никогда не передавайте имена, email или другие персональные данные в свойствах событий.

Решение проблем

  • MissingPluginException? Остановите приложение и запустите его снова. Hot reload и hot restart не загружают новый нативный код.
  • В панели ничего нет? Включите Учитывать тестовые устройства: сборки с kDebugMode по умолчанию скрыты.
  • Всё равно ничего? Настройте с logLevel: UnolinkLogLevel.debug и ищите Unolink в консоли Xcode или Logcat. Неверный или отозванный ключ записывается в лог как ошибка.

Подробнее в разделе Решение проблем.