ENetrebin (обсуждение | вклад) (Новая страница: «Используйте ЕдиныйАгент SDK для iOS, чтобы сообщать дополнительные сведения о сеансах поль...») |
Нет описания правки |
||
Строка 2: | Строка 2: | ||
Для iOS ЕдиныйАгент SDK доступен автоматически после добавления | Для iOS ЕдиныйАгент SDK доступен автоматически после добавления АппОптима CocoaPod в проект. Вы можете использовать ЕдиныйАгент SDK для iOS в Swift и Objective-C. | ||
= 1. Запуск ЕдиныйАгент = | = 1. Запуск ЕдиныйАгент = | ||
Строка 25: | Строка 25: | ||
|'''Objective-C''' | |'''Objective-C''' | ||
|- | |- | ||
| | |АппОптима.startupWithInfoPlistSettings() | ||
|[ | |[АппОптима startupWithInfoPlistSettings]; | ||
|} | |} | ||
= 3. Запуск ЕдиныйАгент с переданным словарем конфигурации = | = 3. Запуск ЕдиныйАгент с переданным словарем конфигурации = | ||
Если вы хотите запустить ЕдиныйАгент с переданным словарем конфигурации, используйте вызов API '''startupWithConfig'''. Ключи конфигурации для словаря такие же, как и для файла '''Info.plist''', и их также можно найти в заголовочном файле ''' | Если вы хотите запустить ЕдиныйАгент с переданным словарем конфигурации, используйте вызов API '''startupWithConfig'''. Ключи конфигурации для словаря такие же, как и для файла '''Info.plist''', и их также можно найти в заголовочном файле '''АппОптима.h''', поставляемом с ЕдиныйАгент. | ||
{| class="wikitable" | {| class="wikitable" | ||
|'''Swift''' | |'''Swift''' | ||
Строка 45: | Строка 45: | ||
] | ] | ||
АппОптима.startup(withConfig: startupDictionary) | |||
|NSDictionary<NSString*, id> *startupDictionary = @{ | |NSDictionary<NSString*, id> *startupDictionary = @{ | ||
Строка 56: | Строка 56: | ||
}; | }; | ||
[ | [АппОптима startupWithConfig:startupDictionary]; | ||
|} | |} | ||
{| class="wikitable" | {| class="wikitable" | ||
|❗ '''''Важно''''' | |❗ '''''Важно''''' | ||
''Может потребоваться указать дополнительные параметры. Чтобы узнать, какие параметры и значения необходимо указать, проверьте instrumentation wizard в веб-интерфейсе | ''Может потребоваться указать дополнительные параметры. Чтобы узнать, какие параметры и значения необходимо указать, проверьте instrumentation wizard в веб-интерфейсе АппОптима.'' | ||
|} | |} | ||
Строка 98: | Строка 98: | ||
Максимальная продолжительность мобильного настраиваемого действия составляет 9 минут. | Максимальная продолжительность мобильного настраиваемого действия составляет 9 минут. | ||
Если настраиваемое действие занимает более 9 минут и не закрывается, оно отбрасывается и не отправляется в | Если настраиваемое действие занимает более 9 минут и не закрывается, оно отбрасывается и не отправляется в АппОптима. | ||
Строка 450: | Строка 450: | ||
] | ] | ||
АппОптима.sendBizEvent(withType: "com.easytravel.funnel.booking-finished", attributes: attributes) | |||
|NSDictionary<NSString*, id> *attributes = @{ | |NSDictionary<NSString*, id> *attributes = @{ | ||
Строка 477: | Строка 477: | ||
}; | }; | ||
[ | [АппОптима sendBizEventWithType: @"com.easytravel.funnel.booking-finished" attributes: attributes]; | ||
|} | |} | ||
= 6. Измерение веб-запросов = | = 6. Измерение веб-запросов = | ||
Для отслеживания веб-запросов ЕдиныйАгент добавляет каждому запросу HTTP заголовок '''x- | Для отслеживания веб-запросов ЕдиныйАгент добавляет каждому запросу HTTP заголовок '''x-АппОптима''' с уникальным значением. Это необходимо для сопоставления данных мониторинга на стороне сервера с соответствующим мобильным веб-запросом. Кроме того, измеряются временные значения с мобильной стороны. | ||
ЕдиныйАгент для iOS автоматически определяет время веб-запросов, сделанных с использованием '''NSURLRequest''', '''NSURLConnection''', '''NSURLSession''', '''NSURLProtocol''', '''NSString''', '''WKWebView''' или '''NSData'''. Однако в следующих случаях вам необходимо вручную инструментировать запросы: | ЕдиныйАгент для iOS автоматически определяет время веб-запросов, сделанных с использованием '''NSURLRequest''', '''NSURLConnection''', '''NSURLSession''', '''NSURLProtocol''', '''NSString''', '''WKWebView''' или '''NSData'''. Однако в следующих случаях вам необходимо вручную инструментировать запросы: | ||
Строка 538: | Строка 538: | ||
'''Ручная инструментация веб-запросов.''' | '''Ручная инструментация веб-запросов.''' | ||
* Если родительское действие не указано (''' | * Если родительское действие не указано ('''АппОптима.getRequestTagValue(for: url)'''), то поведение для поиска родительского действия такое же, как и для автоматического определения времени веб-запроса. | ||
* Если указано родительское действие ('''childAction?.getTagFor(url)'''), то это действие является родительским. | * Если указано родительское действие ('''childAction?.getTagFor(url)'''), то это действие является родительским. | ||
Строка 546: | Строка 546: | ||
import WebKit | import WebKit | ||
import | import АппОптима | ||
class ViewController: UIViewController { | class ViewController: UIViewController { | ||
Строка 566: | Строка 566: | ||
let parentAction = DTXAction.enter(withName: #function) | let parentAction = DTXAction.enter(withName: #function) | ||
let url = URL(string: "<nowiki>https://www. | let url = URL(string: "<nowiki>https://www.АппОптима.com</nowiki>") | ||
downloadRequest(url: url!, wkWebView: wkWebView, parentAction: parentAction) ''//as this is async parent action should be left when request is done'' | downloadRequest(url: url!, wkWebView: wkWebView, parentAction: parentAction) ''//as this is async parent action should be left when request is done'' | ||
Строка 586: | Строка 586: | ||
var webrequestTiming: DTXWebRequestTiming? | var webrequestTiming: DTXWebRequestTiming? | ||
''//if let | ''//if let АппОптимаHeaderValue = АппОптима.getRequestTagValue(for: url) { //let agent decide which action it uses as parent (last created action)'' | ||
if let | if let АппОптимаHeaderValue = childAction?.getTagFor(url) { ''//provide parent action'' | ||
let | let АппОптимаHeaderKey = АппОптима.getRequestTagHeader() ''//this could be cached as it always is x-АппОптима'' | ||
request.addValue( | request.addValue(АппОптимаHeaderValue, forHTTPHeaderField: АппОптимаHeaderKey) | ||
webrequestTiming = DTXWebRequestTiming.getDTXWebRequestTiming( | webrequestTiming = DTXWebRequestTiming.getDTXWebRequestTiming(АппОптимаHeaderValue, request: url) | ||
} | } | ||
Строка 657: | Строка 657: | ||
|import UIKit | |import UIKit | ||
import | import АппОптима | ||
class ViewController: UIViewController { | class ViewController: UIViewController { | ||
Строка 732: | Строка 732: | ||
|#import "ViewController.h" | |#import "ViewController.h" | ||
<nowiki>#</nowiki>import < | <nowiki>#</nowiki>import <АппОптима/АппОптима.h> | ||
@interface ViewController () | @interface ViewController () | ||
Строка 815: | Строка 815: | ||
Вы можете отметить каждого пользователя своих мобильных приложений уникальным именем пользователя. Это позволяет вам искать и фильтровать определенные сеансы пользователей и анализировать поведение отдельных пользователей с течением времени. | Вы можете отметить каждого пользователя своих мобильных приложений уникальным именем пользователя. Это позволяет вам искать и фильтровать определенные сеансы пользователей и анализировать поведение отдельных пользователей с течением времени. | ||
Следующие шаги объясняют, как вручную отметить отдельного пользователя с помощью | Следующие шаги объясняют, как вручную отметить отдельного пользователя с помощью АппОптима API. Вы можете отметить пользователей при входе в систему или когда уже открытая сессия используется или восстанавливается при повторном запуске приложения, поскольку тег не сохраняется при перезапуске приложения. | ||
{| class="wikitable" | {| class="wikitable" | ||
|'''Swift''' | |'''Swift''' | ||
|'''Objective-C''' | |'''Objective-C''' | ||
|- | |- | ||
| | |АппОптима.identifyUser("userId") | ||
|[ | |[АппОптима identifyUser:@"userId"]; | ||
|} | |} | ||
{| class="wikitable" | {| class="wikitable" | ||
Строка 840: | Строка 840: | ||
= 8. Завершение сессии = | = 8. Завершение сессии = | ||
Вы можете принудительно завершить сеанс с помощью | Вы можете принудительно завершить сеанс с помощью АппОптима API. Это также закрывает все открытые действия и запускает новый сеанс. | ||
{| class="wikitable" | {| class="wikitable" | ||
|'''Swift''' | |'''Swift''' | ||
|'''Objective-C''' | |'''Objective-C''' | ||
|- | |- | ||
| | |АппОптима.endVisit() | ||
|[ | |[АппОптима endVisit]; | ||
|} | |} | ||
Строка 863: | Строка 863: | ||
|'''Objective-C''' | |'''Objective-C''' | ||
|- | |- | ||
|import | |import АппОптима | ||
… | … | ||
let privacyConfig = | let privacyConfig = АппОптима.userPrivacyOptions() | ||
privacyConfig.dataCollectionLevel = .userBehavior | privacyConfig.dataCollectionLevel = .userBehavior | ||
Строка 875: | Строка 875: | ||
privacyConfig.crashReplayOptedIn = true | privacyConfig.crashReplayOptedIn = true | ||
АппОптима.applyUserPrivacyOptions(privacyConfig) { (successful) in | |||
''// callback after privacy changed'' | ''// callback after privacy changed'' | ||
} | } | ||
|#import < | |#import <АппОптима/АппОптима.h> | ||
… | … | ||
id<DTXUserPrivacyOptions> privacyConfig = [ | id<DTXUserPrivacyOptions> privacyConfig = [АппОптима userPrivacyOptions]; | ||
privacyConfig.dataCollectionLevel = DTX_DataCollectionUserBehavior; | privacyConfig.dataCollectionLevel = DTX_DataCollectionUserBehavior; | ||
Строка 892: | Строка 892: | ||
privacyConfig.crashReplayOptedIn = YES; | privacyConfig.crashReplayOptedIn = YES; | ||
[ | [АппОптима applyUserPrivacyOptions:privacyConfig completion:^(BOOL successful) { | ||
''// callback after privacy changed'' | ''// callback after privacy changed'' | ||
Строка 916: | Строка 916: | ||
|'''Objective-C''' | |'''Objective-C''' | ||
|- | |- | ||
|import | |import АппОптима | ||
… | … | ||
let privacyOptions = | let privacyOptions = АппОптима.userPrivacyOptions() | ||
let dataCollectionLevel = privacyConfig.dataCollectionLevel | let dataCollectionLevel = privacyConfig.dataCollectionLevel | ||
Строка 927: | Строка 927: | ||
let crashReplay = privacyConfig.crashReplayOptedIn | let crashReplay = privacyConfig.crashReplayOptedIn | ||
|#import < | |#import <АппОптима/АппОптима.h> | ||
… | … | ||
id<DTXUserPrivacyOptions> privacyConfig = [ | id<DTXUserPrivacyOptions> privacyConfig = [АппОптима userPrivacyOptions]; | ||
DTX_DataCollectionLevel level= privacyConfig.dataCollectionLevel; | DTX_DataCollectionLevel level= privacyConfig.dataCollectionLevel; | ||
Строка 998: | Строка 998: | ||
'''ЕдиныйАгент для iOS версии 8.215+''' | '''ЕдиныйАгент для iOS версии 8.215+''' | ||
С помощью ''' | С помощью '''АппОптима.modifyUserAction''' вы можете изменить текущее действие пользователя. Вы можете изменить имя действия пользователя и сообщить о событиях, значениях и ошибках. Вы также можете отменить действие пользователя. | ||
Доступны следующие операции для возвращаемого объекта пользовательского действия: | Доступны следующие операции для возвращаемого объекта пользовательского действия: | ||
Строка 1030: | Строка 1030: | ||
//fetch current autogenerated action and modify it | //fetch current autogenerated action and modify it | ||
АппОптима.modifyUserAction( { (action) -> () in | |||
let oldName = action?.getName() | let oldName = action?.getName() | ||
Строка 1055: | Строка 1055: | ||
''//fetch current autogenerated action and modify it'' | ''//fetch current autogenerated action and modify it'' | ||
[ | [АппОптима modifyUserAction:^(DTXAction * _Nullable modifiableAction) { | ||
NSString* oldName = [modifiableAction getName]; | NSString* oldName = [modifiableAction getName]; | ||
Строка 1082: | Строка 1082: | ||
'''ЕдиныйАгент для iOS версии 8.241+''' | '''ЕдиныйАгент для iOS версии 8.241+''' | ||
Вы можете изменить автоматически сгенерированные действия пользователя с помощью ''' | Вы можете изменить автоматически сгенерированные действия пользователя с помощью '''АппОптима.modifyUserAction'''. Однако вы можете сделать это только для определенного действия пользователя, и обычно вы должны знать, открыто ли это действие пользователя или нет. | ||
Чтобы преодолеть эти ограничения, мы представили функцию, которая позволяет вам получать обратный вызов (callback) для каждого вновь созданного действия пользователя. При таком подходе вы получаете уведомление о каждом новом автоматически сгенерированном действии пользователя, поэтому у вас есть возможность обновить имя действия пользователя, а также сообщить о событиях, значениях и ошибках. Вы также можете отменить действие пользователя. | Чтобы преодолеть эти ограничения, мы представили функцию, которая позволяет вам получать обратный вызов (callback) для каждого вновь созданного действия пользователя. При таком подходе вы получаете уведомление о каждом новом автоматически сгенерированном действии пользователя, поэтому у вас есть возможность обновить имя действия пользователя, а также сообщить о событиях, значениях и ошибках. Вы также можете отменить действие пользователя. | ||
Строка 1108: | Строка 1108: | ||
|'''Objective-C''' | |'''Objective-C''' | ||
|- | |- | ||
| | |АппОптима.setAutoUserActionCreationCallback { modifiableAction in | ||
guard let modifiableAction = modifiableAction else { | guard let modifiableAction = modifiableAction else { | ||
Строка 1127: | Строка 1127: | ||
} | } | ||
|[ | |[АппОптима setAutoUserActionCreationCallback:^(DTXAction * _Nullable modifiableAction) { | ||
if (!modifiableAction) { | if (!modifiableAction) { |
Текущая версия от 03:42, 21 декабря 2024
Используйте ЕдиныйАгент SDK для iOS, чтобы сообщать дополнительные сведения о сеансах пользователей в своем мобильном приложении. ЕдиныйАгент SDK для iOS позволяет создавать настраиваемые действия (custom actions), измерять веб-запросы, сообщать об ошибках и помечать конкретных пользователей. В разделах ниже объясняется, как включить эти возможности.
Для iOS ЕдиныйАгент SDK доступен автоматически после добавления АппОптима CocoaPod в проект. Вы можете использовать ЕдиныйАгент SDK для iOS в Swift и Objective-C.
1. Запуск ЕдиныйАгент
Чтобы отключить автоматический запуск ЕдиныйАгент, установите ключ конфигурации DTXAutoStart в false в файле Info.plist вашего приложения:
<key>DTXAutoStart</key>
<false/>
После этого вы можете запустить ЕдиныйАгент вручную, используя либо конфигурацию Info.plist, либо переданный словарь конфигурации.
❗ Важно
Мы рекомендуем запускать ЕдиныйАгент как можно раньше, например, в applicationWillFinishLaunching. |
2. Запуск ЕдиныйАгент с конфигурацией Info.plist
Если вы хотите запустить ЕдиныйАгент с настройками из файла Info.plist, используйте вызов API startupWithInfoPlistSettings.
Swift | Objective-C |
АппОптима.startupWithInfoPlistSettings() | [АппОптима startupWithInfoPlistSettings]; |
3. Запуск ЕдиныйАгент с переданным словарем конфигурации
Если вы хотите запустить ЕдиныйАгент с переданным словарем конфигурации, используйте вызов API startupWithConfig. Ключи конфигурации для словаря такие же, как и для файла Info.plist, и их также можно найти в заголовочном файле АппОптима.h, поставляемом с ЕдиныйАгент.
Swift | Objective-C |
let startupDictionary: [String : Any?] = [
kDTXApplicationID: "aaaa-bbbb-cccc-dddd-eeee-ffff", kDTXBeaconURL: "https://my.beacon.url/mbeacon", kDTXLogLevel: "WARNING" ] АппОптима.startup(withConfig: startupDictionary) |
NSDictionary<NSString*, id> *startupDictionary = @{
kDTXApplicationID: @"aaaa-bbbb-cccc-dddd-eeee-ffff", kDTXBeaconURL: @"https://my.beacon.url/mbeacon", kDTXLogLevel: @"WARNING" }; [АппОптима startupWithConfig:startupDictionary]; |
❗ Важно
Может потребоваться указать дополнительные параметры. Чтобы узнать, какие параметры и значения необходимо указать, проверьте instrumentation wizard в веб-интерфейсе АппОптима. |
4. Создание настраиваемых действий (custom actions)
Вы можете определять и направлять настраиваемые действия (custom actions). После того, как вы их запустите, их можно дополнить дополнительной информацией, прежде чем закрыть их.
5. Запуск и закрытие настраиваемых действий (custom actions)
Ниже приведен фрагмент кода, который показывает, как запустить и закрыть настраиваемое действие. Обратите внимание, что ЕдиныйАгент отбрасывает все данные мониторинга, связанные с действием, если настраиваемое действие не закрыто. Время измеряется автоматически.
Swift | Objective-C |
// start the "Tap on Search" action
let action = DTXAction.enter(withName: "Tap on Search") // ...do some work here... // close the "Tap on Search" action action?.leave() |
// start the "Tap on Search" action
DTXAction *action = [DTXAction enterActionWithName:@"Tap on Search"]; // ...do some work here... // close the "Tap on Search" action [action leaveAction]; |
Для мобильного настраиваемого действия, созданного пользователем, или для мобильного автоматически сгенерированного пользовательского действия (user action), максимальная длина имени составляет 250 символов.
Максимальная продолжительность мобильного настраиваемого действия составляет 9 минут.
Если настраиваемое действие занимает более 9 минут и не закрывается, оно отбрасывается и не отправляется в АппОптима.
Вы можете выполнять следующие операции мониторинга с настраиваемым действием:
- Создать дочернее действие
- Сообщить о событии
- Сообщить значение
- Сообщить об ошибке
- Прикрепить веб-запрос
- Отменить действие
5.1. Создать дочернее действие
Дочерние действия аналогичны родительским действиям. Когда родительское действие закрывается, ЕдиныйАгент автоматически закрывает все дочерние действия родительского действия.
Фрагмент кода ниже показывает, как запустить действие в качестве дочернего элемента другого действия.
Swift | Objective-C |
// start a parent custom action
let searchAction = DTXAction.enter(withName: "Tap on Search") // ...do some work here... // start a child action let parseAction = DTXAction.enter(withName: "Tap on Confirm", parentAction: searchAction) // ...do some work here... // close a child action parseAction?.leave() // ...do some work here... // close a parent custom action searchAction?.leave() |
// start a parent custom action
let searchAction = DTXAction.enter(withName: "Tap on Search") // ...do some work here... // start a child action let parseAction = DTXAction.enter(withName: "Tap on Confirm", parentAction: searchAction) // ...do some work here... // close a child action parseAction?.leave() // ...do some work here... // close a parent custom action searchAction?.leave()// start a parent custom action DTXAction *searchAction = [DTXAction enterActionWithName: @"Tap on Search"]; // ...do some work here... // start a child action DTXAction *parseAction = [DTXAction enterActionWithName: @"Tap on Confirm" parentAction: searchAction]; // ...do some work here... // close a child action [parseAction leaveAction]; // ...do some work here... // close a parent custom action [searchAction leaveAction]; |
Для настраиваемого мобильного действия (custom action) или мобильного действия пользователя (user action), сгенерированного автоматически, максимальная длина имени составляет 250 символов.
5.1.1. Пример пользовательского действия
В следующем фрагменте кода показан пример ручного инструментирования.
Swift | Objective-C |
func countParameters(query: String?) -> Int {
var count: Int = 0 //create a parent custom action let checkQueryAction = DTXAction.enter(withName: "Tap on Check query") //report a value on an action checkQueryAction?.reportValue(withName: "query", stringValue: String(describing: query)) let countParametersAction = DTXAction.enter(withName: "Count parameters", parentAction: checkQueryAction) if let query = query { count = query.components(separatedBy:"&").count //report a value countParametersAction?.reportValue(withName: "Parameters found", intValue: Int64(count)) //close a child action countParametersAction?.leave() } else { //report an event countParametersAction?.reportEvent(withName: "No parameters found") //close a child action countParametersAction?.leave() //report an error checkQueryAction?.reportError(withName: "Query was nil", errorValue: -42) } //close a parent custom action - automatically closes all open child actions if not closed by API checkQueryAction?.leave() return count } |
func countParameters(query: String?) -> Int {
var count: Int = 0 //create a parent custom action let checkQueryAction = DTXAction.enter(withName: "Tap on Check query") //report a value on an action checkQueryAction?.reportValue(withName: "query", stringValue: String(describing: query)) let countParametersAction = DTXAction.enter(withName: "Count parameters", parentAction: checkQueryAction) if let query = query { count = query.components(separatedBy:"&").count //report a value countParametersAction?.reportValue(withName: "Parameters found", intValue: Int64(count)) //close a child action countParametersAction?.leave() } else { //report an event countParametersAction?.reportEvent(withName: "No parameters found") //close a child action countParametersAction?.leave() //report an error checkQueryAction?.reportError(withName: "Query was nil", errorValue: -42) } //close a parent custom action - automatically closes all open child actions if not closed by API checkQueryAction?.leave() return count } |
5.2. Отменить действие
ЕдиныйАгент для iOS версии 8.229+
Вы можете отменить настраиваемое действие или автоматически сгенерированное действие пользователя. При отмене действия удаляются все связанные с ним данные: все сообщаемые значения отбрасываются, а все дочерние действия отменяются.
❗ Важно
Нельзя отменить закрытое действие, поэтому вызов cancel/cancelAction после leave/leaveAction для того же действия невозможен. То же самое касается закрытия отмененного действия: вы не можете вызвать leave/leaveAction после использования cancel/cancelAction для того же действия. |
Swift | Objective-C |
// start the "Tap on Search" action
let action = DTXAction.enter(withName: "Tap on Search") // ...do some work here... // cancel the "Tap on Search" action action?.cancel() |
// start the "Tap on Search" action
DTXAction *action = [DTXAction enterActionWithName:@"Tap on Search"]; // ...do some work here... // cancel the "Tap on Search" action [action cancelAction]; |
5.3. Сообщить о событии
С помощью reportEvent вы можете сообщить о конкретном событии. Событие должно принадлежать настраиваемому пользовательскому действию или автоматически сгенерированному действию пользователя. Сообщенные события отображаются в каскадном анализе (waterfall analysis) действий пользователя.
Swift | Objective-C |
let myAction = DTXAction.enter(withName: "My action")
myAction?.reportEvent(withName: "Something important just happened") myAction?.leave() |
// report an event
- (DTX_StatusCode) reportEventWithName:(NSString *)eventName |
Если вы хотите сообщать об отдельных событиях с большим количеством дополнительной информации, см. раздел Отчет о бизнес-событии.
5.4. Сообщить об ошибке
С помощью reportError вы можете сообщить о конкретном событии как об ошибке.
Существует два варианта сообщения об ошибке:
- Сообщить об этом как о части действия (настраиваемого действия или автоматически сгенерированного действия пользователя).
- Сообщите об этом как об отдельной ошибке, которая генерируется как глобальное событие, не привязанное к конкретному действию.
Сообщенные ошибки (как отдельные (standalone), так и «прикрепленные» (attached) к действию пользователя) отображаются на странице сведений о сеансе пользователя и многомерной странице анализа действий пользователя (multidimensional User action analysis). Сообщенные ошибки, являющиеся частью действия пользователя, также отображаются в каскадном анализе действий пользователя (user action waterfall analysis).
Вы можете сообщить об ошибке с помощью кода ошибки, исключения или NSError.
Swift | Objective-C |
Автономная (standalone) ошибка (не привязанная к конкретному действию пользователя)
DTXAction.reportError(withName: "My custom error", error: NSError(domain: "Global issue", code: 007, userInfo: nil)) Ошибка связана с конкретным действием пользователя let myAction = DTXAction.enter(withName: "My action") myAction?.reportError(withName: "My custom error", error: NSError(domain: "Action issue", code: 007, userInfo: nil)) myAction?.leave() |
// report an error
DTXAction *action; NSError* error; [action reportErrorWithName:@"CommunicationError" errorValue:[error code]]; |
5.5. Сообщить значение
Метод reportValue позволяет вам сообщать свои собственные показатели. Эти показатели должны быть частью настраиваемого действия (custom action) или автоматически сгенерированного действия пользователя (user action). Сообщаемые значения отображаются в каскадном анализе действий пользователя (user action waterfall analysis).
ЕдиныйАгент SDK позволяет сообщать значения int, double и string.
Swift | Objective-C |
let myAction = DTXAction.enter(withName: "My action")
myAction?.reportValue(withName: "My int value", intValue: 1234) myAction?.reportValue(withName: "My double value", doubleValue: 12.34) myAction?.reportValue(withName: "My string value", stringValue: "Hello World!") myAction?.leave() |
let myAction = DTXAction.enter(withName: "My action")
myAction?.reportValue(withName: "My int value", intValue: 1234) myAction?.reportValue(withName: "My double value", doubleValue: 12.34) myAction?.reportValue(withName: "My string value", stringValue: "Hello World!") myAction?.leave()- (DTX_StatusCode) reportValueWithName:(NSString *)valueName intValue:(NSInteger)value - (DTX_StatusCode) reportValueWithName:(NSString *)valueName doubleValue:(double)doubleValue - (DTX_StatusCode) reportValueWithName:(NSString *)valueName stringValue:(NSString *)stringValue |
5.6. Отчет о бизнес-событии
ЕдиныйАгент для iOS версии 8.253+
С помощью метода sendBizEvent вы можете сообщать о бизнес-событиях. Эти события являются самостоятельными (standalone), так как ЕдиныйАгент отправляет их отдельно от пользовательских действий (user actions) или пользовательских сессий (user session).
Swift | Objective-C |
let attributes: [String: Any] = [
"event.name": "Confirmed Booking", "screen": "booking-confirmation", "product": "Danube Anna Hotel", "amount": 358.35, "currency": "USD", "reviewScore": 4.8, "arrivalDate": "2022-11-05", "departureDate": "2022-11-15", "journeyDuration": 10, "adultTravelers": 2, "childrenTravelers": 0 ] АппОптима.sendBizEvent(withType: "com.easytravel.funnel.booking-finished", attributes: attributes) |
NSDictionary<NSString*, id> *attributes = @{
@"event.name": @"Confirmed Booking", @"screen": @"booking-confirmation", @"product": @"Danube Anna Hotel", @"amount": @358.35, @"currency": @"USD", @"reviewScore": @4.8, @"arrivalDate": @"2022-11-05", @"departureDate": @"2022-11-15", @"journeyDuration": @10, @"adultTravelers": @2, @"childrenTravelers": @0 }; [АппОптима sendBizEventWithType: @"com.easytravel.funnel.booking-finished" attributes: attributes]; |
6. Измерение веб-запросов
Для отслеживания веб-запросов ЕдиныйАгент добавляет каждому запросу HTTP заголовок x-АппОптима с уникальным значением. Это необходимо для сопоставления данных мониторинга на стороне сервера с соответствующим мобильным веб-запросом. Кроме того, измеряются временные значения с мобильной стороны.
ЕдиныйАгент для iOS автоматически определяет время веб-запросов, сделанных с использованием NSURLRequest, NSURLConnection, NSURLSession, NSURLProtocol, NSString, WKWebView или NSData. Однако в следующих случаях вам необходимо вручную инструментировать запросы:
- Когда автоматическая обработка веб-запросов отключена
- Когда запросы стороннего фреймворка (third-party framework) не инструментированы
- Когда вам нужно сообщать о запросах, отличных от HTTP(S)
6.1. Какой тип инструментирования использовать
Для HTTP(S)-запросов нельзя комбинировать автоматическое и ручное инструментирование веб-запросов. Однако вы можете использовать автоматическое инструментирование для HTTP(S)-запросов и ручное инструментирование для не-HTTP(S)-запросов, таких как запросы WebSocket или gRPC.
В приведенной ниже таблице объясняется, какой тип инструментирования запроса следует использовать в зависимости от того, какие типы запросов есть в вашем приложении. Он также показывает, следует ли отключать автоматическую обработку запросов.
Тип запроса | Тип инструмента | DTXInstrumentWebRequestTiming |
Только HTTP(S) | Option A
Авто |
true |
Option B
Вручную |
false | |
Только не-HTTP(S) | Вручную | false (необязательный) |
HTTP(S) + не-HTTP(S) | Option A
Автоматически для HTTP(S) и вручную для не-HTTP(S) |
True |
Option B
Вручную |
False |
6.2. Родительское действие веб-запросов
Существует предопределенный порядок, которому следует ЕдиныйАгент для iOS для определения родителя события веб-запроса, который влияет на то, как веб-запросы отображаются в каскадном анализе (waterfall analysis).
Автоматическая инструментация веб-запросов.
- Если есть активное настраиваемое действие (custom action), запущенное в том же потоке, что и веб-запрос, это действие является родительским.
- Если активного настраиваемого действия (custom action) нет, но есть активное автоматически созданное действие (autogenerated action), то автоматически созданное действие (например, Touch on...) является родительским действием. Это включает в себя автоматически сгенерированное действие Loading ...
- Если ЕдиныйАгент не может найти активное действие, событие веб-запроса является событием корневого уровня, не имеющим родительского действия.
Ручная инструментация веб-запросов.
- Если родительское действие не указано (АппОптима.getRequestTagValue(for: url)), то поведение для поиска родительского действия такое же, как и для автоматического определения времени веб-запроса.
- Если указано родительское действие (childAction?.getTagFor(url)), то это действие является родительским.
6.3. Пример: упрощенная ручная инструментация веб-запросов
import UIKit
import WebKit
import АппОптима
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let wkWebView = WKWebView(frame: self.view.frame)
self.view.addSubview(wkWebView)
manualTaggingDemo(wkWebView: wkWebView)
}
func manualTaggingDemo(wkWebView: WKWebView) {
let parentAction = DTXAction.enter(withName: #function)
let url = URL(string: "https://www.АппОптима.com")
downloadRequest(url: url!, wkWebView: wkWebView, parentAction: parentAction) //as this is async parent action should be left when request is done
}
func downloadRequest(url: URL, wkWebView: WKWebView, parentAction: DTXAction?) {
let childAction = DTXAction.enter(withName: #function, parentAction: parentAction) //add child action to see method call trace
let session = URLSession.shared
let request = NSMutableURLRequest(url: url)
request.httpMethod = "GET"
request.cachePolicy = .reloadIgnoringCacheData
var webrequestTiming: DTXWebRequestTiming?
//if let АппОптимаHeaderValue = АппОптима.getRequestTagValue(for: url) { //let agent decide which action it uses as parent (last created action)
if let АппОптимаHeaderValue = childAction?.getTagFor(url) { //provide parent action
let АппОптимаHeaderKey = АппОптима.getRequestTagHeader() //this could be cached as it always is x-АппОптима
request.addValue(АппОптимаHeaderValue, forHTTPHeaderField: АппОптимаHeaderKey)
webrequestTiming = DTXWebRequestTiming.getDTXWebRequestTiming(АппОптимаHeaderValue, request: url)
}
let task = session.downloadTask(with: request as URLRequest) {
(location, response, error) in
defer {
parentAction?.leave() //leave parent action when request finished - all child actions are automaticlly left on leaving parent
}
guard let _:URL = location, let _:URLResponse = response, error == nil else {
webrequestTiming?.stop("failed") //stop webrequest timing in error case
return
}
let urlContents = try! String(contentsOf: location!, encoding: .utf8)
guard !urlContents.isEmpty else {
webrequestTiming?.stop("empty content") //stop webrequest timing in error case
return
}
webrequestTiming?.stop((response as! HTTPURLResponse).statusCode.description) //stop webrequest when request finished
DispatchQueue.main.async {
wkWebView.loadHTMLString(urlContents, baseURL: nil)
}
}
webrequestTiming?.start() //start webrequest timing
task.resume()
}
}
6.4. Мониторинг запросов, отличных от HTTP(S)
ЕдиныйАгент для iOS не поддерживает автоматическую обработку запросов, отличных от HTTP(S). Если вам нужно сообщить о таких запросах, как запрос WebSocket (начинается с ws:// или wss://), проверьте следующие примеры кода.
❗ Важно
|
Swift |
import UIKit
import АппОптима class ViewController: UIViewController { override func viewDidLoad() { let parentAction = DTXAction.enter(withName: #function)! defer { parentAction.leave() } webSocketMonitoringExample(parentAction: parentAction) } func webSocketMonitoringExample(parentAction : DTXAction) { let childAction = DTXAction.enter(withName: #function, parentAction: parentAction) defer { childAction?.leave() } let urlSession = URLSession(configuration: .default) let url = URL(string: "wss://....")! // example echo wss server let tag = childAction?.getTagFor(url)! // not sending the tag, just using it for internal reference let webrequestTiming = DTXWebRequestTiming.getDTXWebRequestTiming(tag!, request: url) let webSocketTask = urlSession.webSocketTask(with: url) webSocketTask.resume() webrequestTiming?.start() let message = URLSessionWebSocketTask.Message.string("Hello World") webSocketTask.send(message) { error in if let error = error { print("send error: \(error.localizedDescription)") } } // synchronous example let receiveDispatch = DispatchGroup() receiveDispatch.enter() webSocketTask.receive { result in print("received data: \(result)") receiveDispatch.leave() } receiveDispatch.wait() webSocketTask.cancel() webrequestTiming?.stop(String(URLSessionWebSocketTask.CloseCode.normalClosure.rawValue)) } } |
Objective-C |
#import "ViewController.h"
#import <АппОптима/АппОптима.h> @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; DTXAction* parent = [DTXAction enterActionWithName: [NSString stringWithFormat:@"%s", __FUNCTION__]]; [self webSocketMonitoringExample:parent]; [parent leaveAction]; } - (void)webSocketMonitoringExample:(DTXAction*)parentAction { DTXAction* childAction = [DTXAction enterActionWithName:[NSString stringWithFormat:@"%s", __FUNCTION__] parentAction:parentAction]; NSURLSession* urlSession = [NSURLSession sessionWithConfiguration:NSURLSessionConfiguration.defaultSessionConfiguration]; NSURL* url = [NSURL URLWithString:@"wss://...."]; // example echo wss server NSString* tag = [childAction getTagForURL:url]; // not sending the tag, just using it for internal reference DTXWebRequestTiming* webrequestTiming = [DTXWebRequestTiming getDTXWebRequestTiming:tag requestUrl:url]; NSURLSessionWebSocketTask* webSocketTask = [urlSession webSocketTaskWithURL:url]; [webSocketTask resume]; [webrequestTiming startWebRequestTiming]; NSURLSessionWebSocketMessage* message = [[NSURLSessionWebSocketMessage alloc] initWithString:@"Hello World"]; [webSocketTask sendMessage:message completionHandler:^(NSError * _Nullable error) { if (error) { NSLog(@"send error: %@", error.localizedDescription); } }]; // synchronous example dispatch_group_t receiveDispatch = dispatch_group_create(); dispatch_group_enter(receiveDispatch); [webSocketTask receiveMessageWithCompletionHandler:^(NSURLSessionWebSocketMessage * _Nullable message, NSError * _Nullable error) { NSLog(@"received data: %@", message); dispatch_group_leave(receiveDispatch); }]; dispatch_group_wait(receiveDispatch, DISPATCH_TIME_FOREVER); [webSocketTask cancel]; [webrequestTiming stopWebRequestTiming:[NSString stringWithFormat:@"%ld", (long)NSURLSessionWebSocketCloseCodeNormalClosure]]; [childAction leaveAction]; } @end |
6.5. Отключить автоматическое инструментирвоание запросов
Чтобы отключить автоматическое инструментирование веб-запросов, установите для ключа конфигурации DTXInstrumentWebRequestTiming значение false.
7. Отметить конкретных пользователей тегом.
Вы можете отметить каждого пользователя своих мобильных приложений уникальным именем пользователя. Это позволяет вам искать и фильтровать определенные сеансы пользователей и анализировать поведение отдельных пользователей с течением времени.
Следующие шаги объясняют, как вручную отметить отдельного пользователя с помощью АппОптима API. Вы можете отметить пользователей при входе в систему или когда уже открытая сессия используется или восстанавливается при повторном запуске приложения, поскольку тег не сохраняется при перезапуске приложения.
Swift | Objective-C |
АппОптима.identifyUser("userId") | [АппОптима identifyUser:@"userId"]; |
❗ Важно
ЕдиныйАгент для iOS версии 235+ Сеансы, разделенные из-за бездействия или истечения времени продолжительности, автоматически переопределяются (re-tagged). Когда ЕдиныйАгент завершает помеченный сеанс из-за того, что продолжительность сеанса достигла установленного предела, или из-за бездействия пользователя, последующий сеанс автоматически повторно помечается. Вам не нужно снова предоставлять информацию об идентификации пользователя. Однако следует отметить, что ЕдиныйАгент не переопределяет метку следующего сеанса в следующих случаях:
|
8. Завершение сессии
Вы можете принудительно завершить сеанс с помощью АппОптима API. Это также закрывает все открытые действия и запускает новый сеанс.
Swift | Objective-C |
АппОптима.endVisit() | [АппОптима endVisit]; |
9. Настроить конфиденциальность данных
В режиме согласия пользователя для мобильных приложений (user opt-in) вы можете динамически настраивать параметры конфиденциальности данных и создавать свои приложения в соответствии с законами и нормами о защите данных.
Чтобы активировать режим согласия пользователя (user opt-in), добавьте ключ конфигурации DTXUserOptIn в файл Info.plist вашего приложения:
<key>DTXUserOptIn</key>
<true/>
В следующих примерах кода показано, как работать с API:
Swift | Objective-C |
import АппОптима
… let privacyConfig = АппОптима.userPrivacyOptions() privacyConfig.dataCollectionLevel = .userBehavior privacyConfig.crashReportingOptedIn = true privacyConfig.crashReplayOptedIn = true АппОптима.applyUserPrivacyOptions(privacyConfig) { (successful) in // callback after privacy changed } |
#import <АппОптима/АппОптима.h>
… id<DTXUserPrivacyOptions> privacyConfig = [АппОптима userPrivacyOptions]; privacyConfig.dataCollectionLevel = DTX_DataCollectionUserBehavior; privacyConfig.crashReportingOptedIn = YES; privacyConfig.crashReplayOptedIn = YES; [АппОптима applyUserPrivacyOptions:privacyConfig completion:^(BOOL successful) { // callback after privacy changed }]; |
Доступны следующие уровни сбора данных:
- off/DTX_DataCollectionOff
- performance/DTX_DataCollectionPerformance
- userBehavior/DTX_DataCollectionUserBehavior
❗ Важно
ЕдиныйАгент сохраняет настройки конфиденциальности данных и автоматически применяет их при перезапуске приложения. Каждый раз, когда пользователь меняет уровень сбора данных, ЕдиныйАгент создает новый сеанс с новыми настройками. Убедитесь, что вы не обертываете этот метод API с действием пользователя, в противном случае ЕдиныйАгент не сможет привязать действие пользователя к правильному сеансу. |
Вы также можете использовать те же свойства для получения параметров конфиденциальности данных:
Swift | Objective-C |
import АппОптима
… let privacyOptions = АппОптима.userPrivacyOptions() let dataCollectionLevel = privacyConfig.dataCollectionLevel let crashReporting = privacyConfig.crashReportingOptedIn let crashReplay = privacyConfig.crashReplayOptedIn |
#import <АппОптима/АппОптима.h>
… id<DTXUserPrivacyOptions> privacyConfig = [АппОптима userPrivacyOptions]; DTX_DataCollectionLevel level= privacyConfig.dataCollectionLevel; BOOL crashReporting = privacyConfig.crashReportingOptedIn; BOOL crashReplay = privacyConfig.crashReplayOptedIn; |
10. Именование действий пользователя
Чтобы создать имена действий пользователя (user action name), ЕдиныйАгент захватывает заголовок элемента управления UIButton, оценивая следующий порядок API или полей и останавливаясь, как только получает действительный текст:
- titleLabel.attributedText
- attributedTitleForState:UIControlStateNormal
- accessibilityLabel
Если ни один из них не содержит полезного текста, ЕдиныйАгент устанавливает заголовок элемента управления в значение по умолчанию Button.
Для ячеек таблиц происходит аналогичное поведение: используется эвристика для определения самой заметной метки в ячейке, и ЕдиныйАгент использует этот текст в имени действия пользователя.
11. Использование пользовательских заголовков управления (Use custom control titles)
Вы можете переопределить захваченные заголовки элементов управления UIControl, UITableViewCell и UICollectionViewCell. Это позволяет вам изменять заголовки элементов управления или скрывать их по причинам конфиденциальности.
Если вы предоставите пустую строку, ЕдиныйАгент будет использовать тип элемента управления в имени действия пользователя, например, Touch on Button. Если вы предоставите nil, заголовок элемента управления будет сброшен на значение по умолчанию.
Swift | Objective-C |
var button: UIButton
button.dtxCustomControlName("Custom button title") var tableCell: UITableViewCell tableCell.dtxCustomCellName("Custom table cell title") var collectionCell: UICollectionViewCell collectionCell.dtxCustomCellName("Custom collection cell title") |
UIButton *button;
[button dtxCustomControlName:@"Custom button title"]; UIButton *tableCell; [tableCell dtxCustomCellName:@"Custom table cell title"]; UIButton *collectionCell; [collectionCell dtxCustomCellName:@"Custom collection cell title"]; |
Если вы хотите избавиться от заголовков управления во всех действиях пользователей одновременно, см. раздел Маскирование пользовательских действий.
12. Изменение автоматически созданных действий
ЕдиныйАгент для iOS версии 8.241+
ЕдиныйАгент для iOS создает действия пользователя на основе взаимодействия пользователей вашего приложения. Эти действия отличаются от настраиваемых действий (custom actions) и иногда называются автоматически сгенерированными действиями (autogenerated actions). Мы также называем их действиями пользователя (user actions).
Используя ЕдиныйАгент SDK для iOS, вы можете изменить или даже отменить эти автоматически созданные действия.
Если вы хотите избежать захвата персональной информации для всех действий пользователя одновременно, обратитесь к разделу Маскирование пользовательских действий.
12.1. Изменение конкретного действия пользователя
ЕдиныйАгент для iOS версии 8.215+
С помощью АппОптима.modifyUserAction вы можете изменить текущее действие пользователя. Вы можете изменить имя действия пользователя и сообщить о событиях, значениях и ошибках. Вы также можете отменить действие пользователя.
Доступны следующие операции для возвращаемого объекта пользовательского действия:
- getName
- setName
- reportEvent
- reportValue
- reportError
- cancelAction / cancel (ЕдиныйАгент for iOS version 8.241+)
Вы можете изменять пользовательское действие только до тех пор, пока оно не закрыто. Если пользовательское действие истекает до его изменения, изменение не будет иметь эффекта. Вызов метода leave на этом объекте также не будет иметь эффекта.
Swift | Objective-C |
UIButton *button;
[button dtxCustomControlName:@"Custom button title"]; UIButton *tableCell; [tableCell dtxCustomCellName:@"Custom table cell title"]; UIButton *collectionCell; [collectionCell dtxCustomCellName:@"Custom collection cell title"];@IBAction func buttonTouchUp(_ sender: Any) { //for example, handle button touch, get values that should be put into user action name //fetch current autogenerated action and modify it АппОптима.modifyUserAction( { (action) -> () in let oldName = action?.getName() action?.setName("This is a renamed auto user action (was '\(oldName)')") action?.reportValue(withName: "The Answer to the Ultimate Question of Life, the Universe, and Everything", intValue: 42) action?.reportValue(withName: "G-force", doubleValue: 9.81) action?.reportValue(withName: "Palindrome", stringValue: "Was it a car or a cat I saw?") action?.reportError(withName: "Some error", errorValue: -1) //other reportError also possible, skipping in this example }) } |
- (IBAction)buttontouchUp:(UIButton *)sender {
//for example, handle button touch, get values that should be put into user action name //fetch current autogenerated action and modify it [АппОптима modifyUserAction:^(DTXAction * _Nullable modifiableAction) { NSString* oldName = [modifiableAction getName]; [modifiableAction setName:[NSString stringWithFormat: @"This is a renamed auto user action (was '%@')", oldName]]; [modifiableAction reportValueWithName: @"The Answer to the Ultimate Question of Life, the Universe, and Everything" intValue: 42]; [modifiableAction reportValueWithName: @"G-force" doubleValue: 9.81]; [modifiableAction reportValueWithName: @"Palindrome" stringValue: @"Was it a car or a cat I saw?"]; [modifiableAction reportErrorWithName: @"Some error" errorValue: -1]; //other reportError also possible, skipping in this example }]; } |
Максимальная длина имени для мобильного пользовательского действия (mobile custom action) или мобильного автоматически созданного пользовательского действия (mobile autogenerated user action) составляет 250 символов.
12.2. Изменить любое действие пользователя
ЕдиныйАгент для iOS версии 8.241+
Вы можете изменить автоматически сгенерированные действия пользователя с помощью АппОптима.modifyUserAction. Однако вы можете сделать это только для определенного действия пользователя, и обычно вы должны знать, открыто ли это действие пользователя или нет.
Чтобы преодолеть эти ограничения, мы представили функцию, которая позволяет вам получать обратный вызов (callback) для каждого вновь созданного действия пользователя. При таком подходе вы получаете уведомление о каждом новом автоматически сгенерированном действии пользователя, поэтому у вас есть возможность обновить имя действия пользователя, а также сообщить о событиях, значениях и ошибках. Вы также можете отменить действие пользователя.
Вы можете зарегистрировать обратный вызов, который вызывается для каждого автоматически сгенерированного действия пользователя. Вы можете установить обратный вызов в любом месте приложения в любой момент времени.
❗ Важно
Любой ранее зарегистрированный обратный вызов перезаписывается последующим вызовом метода API. |
Разрешены следующие операции:
- getName
- setName
- reportEvent
- reportValue
- reportError
- cancelAction / cancel
Swift | Objective-C |
АппОптима.setAutoUserActionCreationCallback { modifiableAction in
guard let modifiableAction = modifiableAction else { return } if modifiableAction.getName().starts(with: "Loading") { modifiableAction.cancel() } else { modifiableAction.setName("Modified Action: " + modifiableAction.getName()) } } |
[АппОптима setAutoUserActionCreationCallback:^(DTXAction * _Nullable modifiableAction) {
if (!modifiableAction) { return; } if ([[modifiableAction getName] hasPrefix:@"Loading"]) { [modifiableAction cancelAction]; } else { [modifiableAction setName:[NSString stringWithFormat:@"Modified Action: %@", [modifiableAction getName]]]; } }]; |
Максимальная длина имени для мобильных пользовательских действий или мобильных автогенерированных пользовательских действий составляет 250 символов.
13. Маскирование пользовательских действий
ЕдиныйАгент для iOS версии 8.249+
По умолчанию имена действий пользователя получаются из заголовков элементов пользовательского интерфейса, например, заголовков кнопок или ячеек таблицы. В редких случаях адреса электронной почты, имена пользователей или другая личная информация могут быть непреднамеренно включены в имена действий пользователя. Это происходит, когда эта информация включена в параметры, используемые для заголовков элементов управления, что приводит к именам действий пользователя, таким как Touch on Account 123456.
Если в названиях действий пользователей вашего приложения содержится такая личная информация, как имена или адреса, включите механизм маскирования действий пользователей. ЕдиныйАгент будет заменять все названия действий Touch on <control title> на тип элемента управления, к которому пользователь коснулся, например:
- Touch on Account 123456 > Touch on Button
- Touch on Transfer all amount > Touch on Switch
- Touch on Country > Touch on TableCell
Чтобы включить маскирование действий пользователей, установите ключ конфигурации DTXUIActionNamePrivacy в true в файле Info.plist вашего приложения.
<key>DTXUIActionNamePrivacy</key>
<true/>
Маскирование пользовательских действий (user action masking) не изменяет пользовательские заголовки элементов управления (custom control titles).
Если вы хотите изменить названия только для определенных элементов управления или определенных действий пользователя, используйте одну из следующих настроек:
- Используйте пользовательские заголовки элементов управления (custom control tiles), чтобы переопределить заголовки элементов пользовательского интерфейса, захваченные по умолчанию.
- Измените автоматически сгенерированные действия (autogenerated actions), чтобы изменить имена действий пользователя.
- Установите правила именования (mobile app settings > Naming rules), чтобы настроить правила именования действий пользователя (user action naming rules) или правила извлечения (extraction rules).