📱 Мобильное приложение

Полное руководство по аналитике для мобильных приложений: отслеживание экранов, версий, устройств, пуш-уведомлений и поведения пользователей

🎯 Что вы получите
  • Анализ популярности экранов и навигации
  • Распределение пользователей по версиям приложения
  • Данные об устройствах, ОС и версиях
  • Отслеживание эффективности пуш-уведомлений
  • Анализ 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%)

Краши по версиям

Версия Краши Пользователей % крашей
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 после установки?
  • На каком экране бросают чаще?
  • Как версии влияют на удержание?

Дашборды для мобильных приложений

После настройки интеграции вы получите готовые дашборды:

Рекомендации

  • Всегда передавайте версию приложения — это позволит отслеживать влияние обновлений
  • Отслеживайте каждый экран — понимайте путь пользователя
  • Мониторьте краши — критично для качества приложения
  • A/B тестируйте пуш-кампании — заголовки, время отправки, контент
  • Сегментируйте по устройствам — поведение может сильно отличаться
  • Следите за версиями ОС — при выходе новой iOS могут быть проблемы

Что дальше?

📱 Мобильные SDK

Готовые SDK для iOS и Android

Перейти →

📊 Знакомство с дашбордом

Как работать с полученными данными

Перейти →

Нужна помощь с настройкой мобильной аналитики?

Напишите нам, и мы поможем настроить интеграцию под ваше приложение

support@instantbase.online Telegram