📱 Мобильное приложение
Полное руководство по аналитике для мобильных приложений: отслеживание экранов, версий, устройств, пуш-уведомлений и поведения пользователей
🎯 Что вы получите
- Анализ популярности экранов и навигации
- Распределение пользователей по версиям приложения
- Данные об устройствах, ОС и версиях
- Отслеживание эффективности пуш-уведомлений
- Анализ crashes и ошибок
Ключевые метрики для мобильных приложений
DAU / WAU / MAU
4 520 / 12 300 / 28 500
Ежедневная, недельная и месячная аудитория
Средняя сессия
4 мин 35 сек
Время, проведенное в приложении за сессию
iOS / Android
52% / 48%
Распределение пользователей по платформам
CTR пушей
4.2%
Клик по пуш-уведомлениям
Распределение по версиям приложения
v2.1.0
4 520 пользователей (45%)
v2.0.5
3 150 пользователей (30%)
v2.0.1
1 520 пользователей (15%)
v1.9.0
1 050 пользователей (10%)
Распределение по устройствам
📱 iOS
iPhone 15 Pro
25%
iPhone 14
18%
iPhone 13
15%
Другие
42%
🤖 Android
Samsung S24
22%
Xiaomi 14
18%
Google Pixel
12%
Другие
48%
Типовая навигация в приложении
Сплеш
splash_screen
→
Главная
home_screen
→
Профиль
profile_screen
→
Настройки
settings_screen
Какие события отправлять
| Событие | Описание | Обязательность | Ключевые свойства |
|---|---|---|---|
app_open |
Запуск приложения | Обязательно | version, build, source |
app_close |
Закрытие приложения | Рекомендуется | session_duration |
screen_view |
Просмотр экрана | Обязательно | screen_name, previous_screen |
button_tap |
Нажатие на кнопку | Опционально | button_name, screen |
push_received |
Получение пуш-уведомления | Рекомендуется | campaign, message_id |
push_opened |
Открытие пуш-уведомления | Рекомендуется | campaign, message_id |
login |
Вход в аккаунт | Обязательно | method |
logout |
Выход из аккаунта | Опционально | — |
share |
Шеринг контента | Опционально | content_type, method |
error |
Ошибка в приложении | Рекомендуется | error_code, error_message |
crash |
Критическая ошибка (краш) | Обязательно | stack_trace, reason |
Метаданные для мобильных приложений
// Открытие приложения
{
"event": "app_open",
"user_id": "user_123",
"properties": {
"version": "2.1.0",
"build": "245",
"platform": "ios",
"os_version": "17.4",
"device_model": "iPhone15,2",
"source": "push"
},
"app_version": "2.1.0"
}
// Просмотр экрана
{
"event": "screen_view",
"user_id": "user_123",
"properties": {
"screen_name": "profile_screen",
"screen_title": "Мой профиль",
"previous_screen": "home_screen",
"time_on_screen": 45
},
"_metadata": {
"event_display_name": "Экран профиля",
"event_color": "#2A6DF4"
}
}
// Нажатие на кнопку
{
"event": "button_tap",
"user_id": "user_123",
"properties": {
"button_name": "edit_profile",
"screen": "profile_screen",
"button_text": "Редактировать"
}
}
// Пуш-уведомление получено
{
"event": "push_received",
"user_id": "user_123",
"properties": {
"campaign": "welcome_series",
"message_id": "msg_456",
"title": "Новое сообщение",
"category": "promo"
}
}
// Пуш-уведомление открыто
{
"event": "push_opened",
"user_id": "user_123",
"properties": {
"campaign": "welcome_series",
"message_id": "msg_456",
"action": "open"
}
}
// Вход в аккаунт
{
"event": "login",
"user_id": "user_123",
"properties": {
"method": "email",
"previous_user_id": "anonymous_456"
},
"_metadata": {
"is_activation": true,
"event_display_name": "Вход в аккаунт"
}
}
// Ошибка в приложении
{
"event": "error",
"user_id": "user_123",
"properties": {
"error_code": "NETWORK_ERROR",
"error_message": "Connection timeout",
"screen": "home_screen"
}
}
// Краш приложения
{
"event": "crash",
"user_id": "user_123",
"properties": {
"reason": "NSInvalidArgumentException",
"stack_trace": "0x102345678...",
"version": "2.1.0",
"platform": "ios",
"os_version": "17.4",
"device_model": "iPhone15,2"
},
"_metadata": {
"event_display_name": "Краш",
"event_color": "#dc3545"
}
}
Анализ крашей и ошибок
🔥 Топ ошибок за последние 7 дней:
1. NSInvalidArgumentException - 156 раз (42%)
2. Network timeout - 89 раз (24%)
3. OutOfMemoryError - 45 раз (12%)
4. NullPointerException - 34 раза (9%)
5. JSON parsing error - 28 раз (8%)
1. NSInvalidArgumentException - 156 раз (42%)
2. Network timeout - 89 раз (24%)
3. OutOfMemoryError - 45 раз (12%)
4. NullPointerException - 34 раза (9%)
5. JSON parsing error - 28 раз (8%)
Краши по версиям
| Версия | Краши | Пользователей | % крашей |
|---|---|---|---|
| v2.1.0 | 45 | 4 520 | 1.0% |
| v2.0.5 | 89 | 3 150 | 2.8% |
| v2.0.1 | 34 | 1 520 | 2.2% |
Анализ пуш-уведомлений
| Кампания | Отправлено | Доставлено | Открыто | CTR | Конверсия |
|---|---|---|---|---|---|
| Приветственная серия | 5 000 | 4 850 | 890 | 18.4% | 12% |
| Акция на выходные | 12 000 | 11 800 | 1 250 | 10.6% | 5.2% |
| Напоминание | 8 500 | 8 200 | 620 | 7.6% | 3.1% |
Атрибуты пользователей для мобильных приложений
{
"event": "user_data",
"user_id": "user_123",
"attributes": {
"email": "user@example.com",
"name": "Иван Петров",
"push_enabled": true,
"notifications_enabled": true,
"location_enabled": false,
"app_version": "2.1.0",
"platform": "ios",
"device_model": "iPhone15,2",
"os_version": "17.4",
"last_seen": "2026-03-19T15:30:00Z",
"total_sessions": 156,
"total_time": 23400,
"push_opt_in_date": "2026-01-15",
"_metadata": {
"push_enabled": {
"attribute_display_name": "Пуш-уведомления",
"attribute_category": "Настройки",
"attribute_format": "boolean"
},
"platform": {
"attribute_display_name": "Платформа",
"attribute_category": "Устройство",
"attribute_format": "string"
},
"device_model": {
"attribute_display_name": "Модель",
"attribute_category": "Устройство"
},
"total_sessions": {
"attribute_display_name": "Сессий",
"attribute_category": "Активность",
"attribute_format": "number"
},
"total_time": {
"attribute_display_name": "Время в приложении",
"attribute_category": "Активность",
"attribute_format": "number"
}
}
}
}
Пример интеграции на Swift (iOS)
import Foundation
class InstantBaseTracker {
static let shared = InstantBaseTracker()
private let apiKey = "your_api_key"
private let baseURL = "https://api.instantbase.online/v1/track"
private var userId: String
private var sessionId: String
private init() {
self.userId = UserDefaults.standard.string(forKey: "user_id") ?? UUID().uuidString
self.sessionId = UUID().uuidString
UserDefaults.standard.set(self.userId, forKey: "user_id")
}
func track(event: String, properties: [String: Any] = [:], metadata: [String: Any] = [:]) {
var data: [String: Any] = [
"event": event,
"user_id": userId,
"session_id": sessionId,
"timestamp": ISO8601DateFormatter().string(from: Date()),
"properties": properties
]
if !metadata.isEmpty {
data["_metadata"] = metadata
}
guard let jsonData = try? JSONSerialization.data(withJSONObject: data) else { return }
var request = URLRequest(url: URL(string: baseURL)!)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue(apiKey, forHTTPHeaderField: "X-API-Key")
request.httpBody = jsonData
URLSession.shared.dataTask(with: request) { data, response, error in
if let error = error {
print("Failed to track event: \(error)")
} else {
print("Event tracked: \(event)")
}
}.resume()
}
// App open
func trackAppOpen(source: String = "direct") {
let properties: [String: Any] = [
"version": Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "unknown",
"build": Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "unknown",
"platform": "ios",
"os_version": UIDevice.current.systemVersion,
"device_model": UIDevice.current.model,
"source": source
]
track(event: "app_open", properties: properties)
}
// Screen view
func trackScreenView(screenName: String, screenTitle: String, previousScreen: String? = nil) {
var properties: [String: Any] = [
"screen_name": screenName,
"screen_title": screenTitle
]
if let previous = previousScreen {
properties["previous_screen"] = previous
}
track(event: "screen_view", properties: properties)
}
// Push received
func trackPushReceived(campaign: String, messageId: String, title: String) {
let properties: [String: Any] = [
"campaign": campaign,
"message_id": messageId,
"title": title
]
track(event: "push_received", properties: properties)
}
// Push opened
func trackPushOpened(campaign: String, messageId: String) {
let properties: [String: Any] = [
"campaign": campaign,
"message_id": messageId,
"action": "open"
]
track(event: "push_opened", properties: properties)
}
// Login
func trackLogin(method: String, previousUserId: String? = nil) {
var properties: [String: Any] = ["method": method]
if let previous = previousUserId {
properties["previous_user_id"] = previous
}
let metadata: [String: Any] = [
"is_activation": true,
"event_display_name": "Вход в аккаунт"
]
track(event: "login", properties: properties, metadata: metadata)
}
// Error
func trackError(errorCode: String, errorMessage: String, screen: String) {
let properties: [String: Any] = [
"error_code": errorCode,
"error_message": errorMessage,
"screen": screen
]
track(event: "error", properties: properties)
}
// Crash
func trackCrash(reason: String, stackTrace: String) {
let properties: [String: Any] = [
"reason": reason,
"stack_trace": stackTrace,
"version": Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "unknown",
"platform": "ios",
"os_version": UIDevice.current.systemVersion,
"device_model": UIDevice.current.model
]
let metadata: [String: Any] = [
"event_display_name": "Краш",
"event_color": "#dc3545"
]
track(event: "crash", properties: properties, metadata: metadata)
}
}
Пример интеграции на Kotlin (Android)
import android.content.Context
import android.os.Build
import androidx.preference.PreferenceManager
import org.json.JSONObject
import java.io.OutputStreamWriter
import java.net.HttpURLConnection
import java.net.URL
import java.util.*
class InstantBaseTracker private constructor(context: Context) {
private val apiKey = "your_api_key"
private val baseURL = "https://api.instantbase.online/v1/track"
private val prefs = PreferenceManager.getDefaultSharedPreferences(context)
private val userId: String
private val sessionId: String
init {
userId = prefs.getString("user_id", UUID.randomUUID().toString())!!
prefs.edit().putString("user_id", userId).apply()
sessionId = UUID.randomUUID().toString()
}
companion object {
@Volatile
private var instance: InstantBaseTracker? = null
fun getInstance(context: Context): InstantBaseTracker {
return instance ?: synchronized(this) {
instance ?: InstantBaseTracker(context.applicationContext).also { instance = it }
}
}
}
fun track(event: String, properties: Map = emptyMap(), metadata: Map = emptyMap()) {
Thread {
try {
val data = JSONObject().apply {
put("event", event)
put("user_id", userId)
put("session_id", sessionId)
put("timestamp", java.time.Instant.now().toString())
put("properties", JSONObject(properties))
if (metadata.isNotEmpty()) {
put("_metadata", JSONObject(metadata))
}
}
val url = URL(baseURL)
val connection = url.openConnection() as HttpURLConnection
connection.requestMethod = "POST"
connection.setRequestProperty("Content-Type", "application/json")
connection.setRequestProperty("X-API-Key", apiKey)
connection.doOutput = true
OutputStreamWriter(connection.outputStream).use {
it.write(data.toString())
}
val responseCode = connection.responseCode
if (responseCode == 200) {
println("Event tracked: $event")
} else {
println("Failed to track event: $responseCode")
}
connection.disconnect()
} catch (e: Exception) {
println("Error tracking event: ${e.message}")
}
}.start()
}
// App open
fun trackAppOpen(source: String = "direct") {
val properties = mapOf(
"version" to context.packageManager.getPackageInfo(context.packageName, 0).versionName,
"platform" to "android",
"os_version" to Build.VERSION.RELEASE,
"device_model" to Build.MODEL,
"source" to source
)
track("app_open", properties)
}
// Screen view
fun trackScreenView(screenName: String, screenTitle: String, previousScreen: String? = null) {
val properties = mutableMapOf(
"screen_name" to screenName,
"screen_title" to screenTitle
)
previousScreen?.let { properties["previous_screen"] = it }
track("screen_view", properties)
}
// Push received
fun trackPushReceived(campaign: String, messageId: String, title: String) {
val properties = mapOf(
"campaign" to campaign,
"message_id" to messageId,
"title" to title
)
track("push_received", properties)
}
// Push opened
fun trackPushOpened(campaign: String, messageId: String) {
val properties = mapOf(
"campaign" to campaign,
"message_id" to messageId,
"action" to "open"
)
track("push_opened", properties)
}
// Login
fun trackLogin(method: String, previousUserId: String? = null) {
val properties = mutableMapOf("method" to method)
previousUserId?.let { properties["previous_user_id"] = it }
val metadata = mapOf(
"is_activation" to true,
"event_display_name" to "Вход в аккаунт"
)
track("login", properties, metadata)
}
// Error
fun trackError(errorCode: String, errorMessage: String, screen: String) {
val properties = mapOf(
"error_code" to errorCode,
"error_message" to errorMessage,
"screen" to screen
)
track("error", properties)
}
// Crash
fun trackCrash(reason: String, stackTrace: String) {
val properties = mapOf(
"reason" to reason,
"stack_trace" to stackTrace,
"version" to context.packageManager.getPackageInfo(context.packageName, 0).versionName,
"platform" to "android",
"os_version" to Build.VERSION.RELEASE,
"device_model" to Build.MODEL
)
val metadata = mapOf(
"event_display_name" to "Краш",
"event_color" to "#dc3545"
)
track("crash", properties, metadata)
}
}
Ключевые вопросы для анализа мобильных приложений
📱 Вовлеченность
- Как часто пользователи открывают приложение?
- Сколько времени проводят?
- Какие экраны самые популярные?
🔧 Технические
- Какая версия самая стабильная?
- На каких устройствах чаще краши?
- Как обновления влияют на активность?
🔔 Пуш-уведомления
- Какой CTR у разных кампаний?
- Сколько пользователей отключают пуши?
- Какие пуш-кампании работают лучше?
👥 Удержание
- Какой retention после установки?
- На каком экране бросают чаще?
- Как версии влияют на удержание?
Дашборды для мобильных приложений
После настройки интеграции вы получите готовые дашборды:
- Главная — DAU/MAU, новые установки, retention
- Активность — популярные экраны, время в приложении, версии
- Клиенты — устройства, ОС, настройки пушей
- События — лента событий, топ действий, ошибки
- Удержание — когорты по дате установки
- Стабильность — краши и ошибки по версиям
Рекомендации
- Всегда передавайте версию приложения — это позволит отслеживать влияние обновлений
- Отслеживайте каждый экран — понимайте путь пользователя
- Мониторьте краши — критично для качества приложения
- A/B тестируйте пуш-кампании — заголовки, время отправки, контент
- Сегментируйте по устройствам — поведение может сильно отличаться
- Следите за версиями ОС — при выходе новой iOS могут быть проблемы
Что дальше?
Нужна помощь с настройкой мобильной аналитики?
Напишите нам, и мы поможем настроить интеграцию под ваше приложение