iOS/tvOS Application Integration¶
Collect metrics data from various iOS applications and analyze the performance of each iOS application side through visualization.
Prerequisites¶
Note
If the RUM Headless service has been activated, the prerequisites are automatically configured, and you can directly integrate the application.
- Install DataKit;
- Configure the RUM Collector;
- Configure DataKit to be accessible over the public network and install the IP geolocation database.
Application Integration¶
- Go to RUM > Create Application > iOS;
- Enter the application name;
- Enter the application ID;
-
Choose the application integration method:
- Public DataWay: Directly receives RUM data without installing the DataKit collector.
- Local Environment Deployment: Receives RUM data after meeting the prerequisites.
Installation¶
Source Code Repository: https://github.com/GuanceCloud/datakit-ios
Demo: https://github.com/GuanceDemo/guance-app-demo
-
Configure the
Podfilefile.-
Using Dynamic Library
-
Using Static Library
-
Download the code repository to local and use it
Podfilefile:use_modular_headers! # Main project target 'yourProjectName' do pod 'FTMobileSDK', :path => '[folder_path]' end # Widget Extension target 'yourWidgetExtensionName' do pod 'FTMobileSDK', :subspecs => ['Extension'] , :path => '[folder_path]' endfolder_path: The path to the folder containing theFTMobileSDK.podspecfile.FTMobileSDK.podspecfile:Modify
s.versionands.sourcein theFTMobileSDK.podspecfile.Pod::Spec.new do |s| s.name = "FTMobileSDK" s.version = "[latest_version]" s.source = { :git => "https://github.com/GuanceCloud/datakit-ios.git", :tag => s.version } ends.version: Modify to the specified version, it is recommended to be consistent withSDK_VERSIONinFTMobileSDK/FTMobileAgent/Core/FTMobileAgentVersion.h.s.source:tag => s.version
-
-
Execute
pod installin thePodfiledirectory to install the SDK.
-
Configure the
Cartfilefile. -
Update dependencies.
Depending on your target platform (iOS or tvOS), execute the corresponding
carthage updatecommand and add the--use-xcframeworksparameter to generate XCFrameworks:-
For iOS platform:
-
For tvOS platform:
The generated xcframework is used in the same way as a regular Framework. Add the compiled library to the project.
FTMobileAgent: Add to the main project Target, supports iOS and tvOS platforms.FTMobileExtension: Add to the Widget Extension Target. -
-
Add
-ObjCtoTARGETS->Build Setting->Other Linker Flags. -
When integrating with Carthage, SDK version support:
FTMobileAgent: >=1.3.4-beta.2FTMobileExtension: >=1.4.0-beta.1
Using Xcode UI
-
Select
PROJECT->Package Dependency, click the + under thePackagessection. -
In the pop-up page's search box, enter
https://github.com/GuanceCloud/datakit-ios.git. -
After Xcode successfully fetches the package, the SDK configuration page will be displayed.
Dependency Rule: It is recommended to selectUp to Next Major Version.Add To Project: Select the supported project.After filling in the configuration, click the
Add Packagebutton and wait for the loading to complete. -
In the pop-up window
Choose Package Products for datakit-ios, select the Target to which the SDK needs to be added, click theAdd Packagebutton, and the SDK is now successfully added.FTMobileSDK: Add to the main project Target.FTMobileExtension: Add to the Widget Extension Target.
Using Package.swift
If your project is managed by SPM, add the SDK as a dependency by adding dependencies to Package.swift.
// Main project
dependencies: [
.package(url: "https://github.com/GuanceCloud/datakit-ios.git",
.upToNextMajor(from: "[latest_version]"))
]
Add dependencies for your Targets.
targets: [
.target(
name: "YourTarget",
dependencies: [
.product(name: "FTMobileSDK", package: "FTMobileSDK"),
]),
.target(
name: "YourWidgetExtensionTarget",
dependencies: [
.product(name: "FTMobileExtension", package: "FTMobileSDK"),
]),
]
Note: Swift Package Manager is supported from version 1.4.0-beta.1 and above.
Add Header File¶
SDK Initialization¶
Basic Configuration¶
-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
// SDK FTMobileConfig settings
// Local environment deployment, Datakit deployment
//FTMobileConfig *config = [[FTMobileConfig alloc]initWithDatakitUrl:datakitUrl];
// Use public DataWay deployment
FTMobileConfig *config = [[FTMobileConfig alloc]initWithDatawayUrl:datawayUrl clientToken:clientToken];
//config.enableSDKDebugLog = YES; //debug mode
config.compressIntakeRequests = YES;
//Start SDK
[FTMobileAgent startWithConfigOptions:config];
//...
return YES;
}
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// SDK FTMobileConfig settings
// Local environment deployment, Datakit deployment
//let config = FTMobileConfig(datakitUrl: url)
// Use public DataWay deployment
let config = FTMobileConfig(datawayUrl: datawayUrl, clientToken: clientToken)
//config.enableSDKDebugLog = true //debug mode
config.compressIntakeRequests = true //Compress intake data
FTMobileAgent.start(withConfigOptions: config)
//...
return true
}
| Attribute | Type | Required | Meaning |
|---|---|---|---|
| datakitUrl | NSString | Yes | Local environment deployment (Datakit) reporting URL address, example: http://10.0.0.1:9529, default port 9529, the device installing the SDK must be able to access this address. Note: Choose one between datakitUrl and datawayUrl configuration |
| datawayUrl | NSString | Yes | Public DataWay reporting URL address, obtained from the [RUM] application, example: https://open.dataway.url, the device installing the SDK must be able to access this address. Note: Choose one between datakitUrl and datawayUrl configuration |
| clientToken | NSString | Yes | Authentication token, needs to be used together with datawayUrl. |
| enableSDKDebugLog | BOOL | No | Set whether to allow printing logs. Default NO. |
| env | NSString | No | Set the collection environment. Default prod, supports customization, can also be set using the provided FTEnv enumeration via the -setEnvWithType: method. |
| service | NSString | No | Set the name of the business or service. Affects the service field data in Log and RUM. Default: df_rum_ios. |
| globalContext | NSDictionary | No | Add custom tags. Addition rules please refer to here. |
| groupIdentifiers | NSArray | No | Array of AppGroups Identifiers corresponding to Widget Extensions that need to be collected. If Widget Extensions data collection is enabled, App Groups must be set, and the Identifier must be configured in this attribute. |
| autoSync | BOOL | No | Whether to automatically sync data to the server after collection. Default YES. When NO, use [[FTMobileAgent sharedInstance] flushSyncData] to manage data synchronization manually. |
| syncPageSize | int | No | Set the number of entries per sync request. Range [5,). Note: A larger number of request entries means data synchronization occupies more computing resources. Default is 10. |
| syncSleepTime | int | No | Set the sync interval time. Range [0,5000], default is not set. |
| enableDataIntegerCompatible | BOOL | No | It is recommended to enable when coexisting with web data. This configuration is used to handle web data type storage compatibility issues. |
| compressIntakeRequests | BOOL | No | Deflate compression for uploaded sync data. This parameter is supported from SDK version 1.5.6 and above, default is off. |
| enableLimitWithDbSize | BOOL | No | Enable the function to limit total cache size using DB. Note: After enabling, FTLoggerConfig.logCacheLimitCount and FTRUMConfig.rumCacheLimitCount will become invalid. This parameter is supported from SDK version 1.5.8 and above. |
| dbCacheLimit | long | No | DB cache limit size. Range [30MB,), default 100MB, unit byte. Supported from SDK version 1.5.8 and above. |
| dbDiscardType | FTDBCacheDiscard | No | Set the data discard rule in the database. Default FTDBDiscard FTDBDiscard: When the data count exceeds the maximum, discard appended data. FTDBDiscardOldest: When the data count exceeds the maximum, discard the oldest data. Supported from SDK version 1.5.8 and above. |
| dataModifier | FTDataModifier | No | Modify individual fields. Supported from SDK version 1.5.16 and above. Usage example can be found here. |
| lineDataModifier | FTLineDataModifier | No | Modify single data entries. Supported from SDK version 1.5.16 and above. Usage example can be found here. |
| remoteConfiguration | BOOL | No | Whether to enable the remote configuration function for data collection. Default is not enabled. After enabling, SDK initialization or application hot start will trigger data updates. Supported from SDK version 1.5.17 and above. Datakit version requirement >=1.60 or using public dataway. |
| remoteConfigMiniUpdateInterval | int | No | Set the minimum update interval for remote dynamic configuration, unit seconds, default 12 hours. Supported from SDK version 1.5.17 and above. |
| remoteConfigFetchCompletionBlock | FTRemoteConfigFetchCompletionBlock | No | Remote configuration result callback, used to receive fetch results and support custom adjustment of the configuration model. Supported from SDK version 1.5.19 and above. Usage example can be found here. |
RUM Configuration¶
//Enable rum
FTRumConfig *rumConfig = [[FTRumConfig alloc]initWithAppid:appid];
rumConfig.enableTraceUserView = YES;
rumConfig.deviceMetricsMonitorType = FTDeviceMetricsMonitorAll;
rumConfig.monitorFrequency = FTMonitorFrequencyRare;
rumConfig.enableTraceUserAction = YES;
rumConfig.enableTraceUserResource = YES;
rumConfig.enableTrackAppFreeze = YES;
rumConfig.enableTrackAppCrash = YES;
rumConfig.enableTrackAppANR = YES;
rumConfig.errorMonitorType = FTErrorMonitorAll;
[[FTMobileAgent sharedInstance] startRumWithConfigOptions:rumConfig];
let rumConfig = FTRumConfig(appid: appid)
rumConfig.enableTraceUserView = true
rumConfig.deviceMetricsMonitorType = .all
rumConfig.monitorFrequency = .rare
rumConfig.enableTraceUserAction = true
rumConfig.enableTraceUserResource = true
rumConfig.enableTrackAppFreeze = true
rumConfig.enableTrackAppCrash = true
rumConfig.enableTrackAppANR = true
rumConfig.errorMonitorType = .all
FTMobileAgent.sharedInstance().startRum(withConfigOptions: rumConfig)
| Attribute | Type | Required | Meaning |
|---|---|---|---|
| appid | NSString | Yes | RUM application ID unique identifier. Corresponds to setting the RUM appid, which enables the RUM collection function. Method to obtain appid. |
| samplerate | int | No | Sampling rate. Value range [0,100], 0 means no collection, 100 means full collection, default value is 100. Scope is all View, Action, LongTask, Error data under the same session_id. |
| sessionOnErrorSampleRate | int | No | Set error collection rate. When a session is not sampled by samplerate, if an error occurs during the session, data from 1 minute before the error can be collected. Value range [0,100], 0 means no collection, 100 means full collection, default value is 0. Scope is all View, Action, LongTask, Error data under the same session_id. Supported from SDK version 1.5.16 and above. |
| enableTrackAppCrash | BOOL | No | Set whether to collect crash logs. Default NO. |
| enableTrackAppANR | BOOL | No | Collect ANR (Application Not Responding) freeze events. Default NO. |
| enableTrackAppFreeze | BOOL | No | Collect UI freeze events. Default NOCan enable freeze collection and set the freeze threshold via the -setEnableTrackAppFreeze:freezeDurationMs: method. |
| freezeDurationMs | long | No | Set the UI freeze threshold. Value range [100,), unit milliseconds, default 250ms. Supported from SDK version 1.5.7 and above. |
| enableTraceUserView | BOOL | No | Set whether to track user View operations. Default NO. |
| enableTraceUserAction | BOOL | No | Set whether to track user Action operations. Default NO Can customize action_name via view.accessibilityIdentifier. |
| enableTraceUserResource | BOOL | No | Set whether to track user network requests. Default NO, only affects native http. Note: Network requests initiated via [NSURLSession sharedSession] cannot collect performance data;SDK 1.5.9 and above support collecting network requests initiated via Swift's URLSession async/await APIs. |
| resourceUrlHandler | FTResourceUrlHandler | No | Custom collection resource rule. Default is no filtering. Return: NO means to collect, YES means not to collect. |
| errorMonitorType | FTErrorMonitorType | No | Error event monitoring supplement type. Adds monitored information to the collected crash data. FTErrorMonitorBattery for battery level, FTErrorMonitorMemory for memory usage, FTErrorMonitorCpu for CPU usage, default is not set. |
| deviceMetricsMonitorType | FTDeviceMetricsMonitorType | No | View performance monitoring type, default is not set. Adds corresponding monitoring item information to the collected View data. FTDeviceMetricsMonitorMemory monitors current application memory usage, FTDeviceMetricsMonitorCpu monitors CPU ticks, FTDeviceMetricsMonitorFps monitors screen frame rate, default is not set. |
| monitorFrequency | FTMonitorFrequency | No | View performance monitoring sampling period. Configure monitorFrequency to set the sampling period for View monitoring item information. FTMonitorFrequencyDefault 500ms (default), FTMonitorFrequencyFrequent 100ms, FTMonitorFrequencyRare 1000ms. |
| enableResourceHostIP | BOOL | No | Whether to collect the IP address of the request target domain. Supported under >= iOS 13.0 >= tvOS 13.0. |
| globalContext | NSDictionary | No | Add custom tags for user monitoring data source differentiation. If tracking function is needed, the parameter key should be track_id, value can be any value. For addition rule precautions, please refer to here. |
| rumCacheLimitCount | int | No | RUM maximum cache count. Default 100_000. Supported from SDK version 1.5.8 and above. |
| rumDiscardType | FTRUMCacheDiscard | No | Set RUM discard rule. Default FTRUMCacheDiscard FTRUMCacheDiscard: When RUM data count exceeds the maximum, discard appended data. FTRUMDiscardOldest: When RUM data count exceeds the maximum, discard the oldest data. Supported from SDK version 1.5.8 and above. |
| resourcePropertyProvider | FTResourcePropertyProvider | No | Add RUM Resource custom attributes via block callback. Supported from SDK version 1.5.10 and above. Priority is lower than URLSession custom collection. |
| enableTraceWebView | BOOL | No | Set to enable webView data collection, default YES. Supported from SDK version 1.5.17 and above. |
| allowWebViewHost | NSArray | No | Set the allowed WebView host addresses for data tracking. nil means collect all, default is nil. Supported from SDK version 1.5.17 and above. |
| sessionTaskErrorFilter | FTSessionTaskErrorFilter | No | Set whether to intercept URLSessionTask Error. Return YES to confirm interception, NO to not intercept. After interception, RUM-Error will not collect this error. Supported from SDK version 1.5.17 and above. |
| viewTrackingHandler | FTViewTrackingHandler | No | Custom View tracking logic, used to determine which ViewControllers need to be monitored as RUM Views and customize View Name. Effective condition: enableTraceUserView = YES. Supported from SDK version 1.5.18 and above. Usage example can be found here. |
| actionTrackingHandler | FTActionTrackingHandler | No | Custom Action tracking logic, used to filter RUM Action events that need to be recorded and customize Action Name. Effective condition: enableTraceUserAction = YES. Supported from SDK version 1.5.18 and above. Usage example can be found here. |
| crashMonitoring | FTCrashMonitorType | No | Configure the type range of SDK crash monitoring. Default is FTCrashMonitorTypeHighCompatibility (high compatibility mode preset macro). Effective condition: enableTrackAppCrash = YES Note: It is necessary to specify FTCrashMonitorTypeSystem |FTCrashMonitorTypeApplicationState, as these provide important information for reports.Supported from SDK version 1.5.19 and above. |
Log Configuration¶
//Enable logger
FTLoggerConfig *loggerConfig = [[FTLoggerConfig alloc]init];
loggerConfig.enableCustomLog = YES;
loggerConfig.enableLinkRumData = YES;
loggerConfig.logLevelFilter = @[@(FTStatusError),@(FTStatusCritical)];
loggerConfig.discardType = FTDiscardOldest;
[[FTMobileAgent sharedInstance] startLoggerWithConfigOptions:loggerConfig];
let loggerConfig = FTLoggerConfig()
loggerConfig.enableCustomLog = true
loggerConfig.enableLinkRumData = true
loggerConfig.logLevelFilter = [NSNumber(value: FTLogStatus.statusError.rawValue),NSNumber(value: FTLogStatus.statusCritical.rawValue)] // loggerConfig.logLevelFilter = [2,3]
loggerConfig.discardType = .discardOldest
FTMobileAgent.sharedInstance().startLogger(withConfigOptions: loggerConfig)
| Attribute | Type | Required | Meaning |
|---|---|---|---|
| samplerate | int | No | Sampling rate. Value range [0,100], 0 means no collection, 100 means full collection, default value is 100. |
| enableCustomLog | BOOL | No | Whether to upload custom logs. Default NO. |
| printCustomLogToConsole | BOOL | No | Set whether to output custom logs to the console. Default NO. Custom log output format. |
| logLevelFilter | NSArray | No | Set log level filtering, default is not set. Example: 1. To collect logs with levels Info and Error, set to @[@(FTStatusInfo),@(FTStatusError)] or @[@0,@1]2. To collect logs including custom levels, e.g., collect "customLevel" and Error, set to @[@"customLevel",@(FTStatusError)]Supported from SDK version 1.5.16 and above for filtering custom levels. |
| enableLinkRumData | BOOL | No | Whether to associate with RUM data. Default NO. |
| globalContext | NSDictionary | No | Add log custom tags. Addition rules please refer to here. |
| logCacheLimitCount | int | No | Local cache maximum log entry count limit [1000,). The larger the log, the greater the disk cache pressure. Default is 5000. |
| discardType | FTLogCacheDiscard | No | Set the log discard rule after reaching the limit. Default FTDiscard FTDiscard: When log data count exceeds the maximum (5000), discard appended data. FTDiscardOldest: When log data count exceeds the maximum, discard the oldest data. |
Trace Configuration¶
| Attribute | Type | Required | Meaning |
|---|---|---|---|
| samplerate | int | No | Sampling rate. Value range [0,100], 0 means no collection, 100 means full collection, default value is 100. |
| networkTraceType | FTNetworkTraceType | No | Set the type of link tracing. Default is DDTrace. Currently supports Zipkin, Jaeger, DDTrace, Skywalking (8.0+), TraceParent (W3C). If integrating with OpenTelemetry, please check the supported types and agent related configurations when choosing the corresponding link type. |
| enableLinkRumData | BOOL | No | Whether to associate with RUM data. Default NO. |
| enableAutoTrace | BOOL | No | Set whether to enable automatic http trace. Default NO, currently only supports NSURLSession. |
| traceInterceptor | FTTraceInterceptor | No | Support custom link tracing judgment via URLRequest. Return TraceContext after confirming interception, return nil if not intercepted. Supported from SDK version 1.5.10 and above. Priority is lower than URLSession custom collection. |
RUM User Data Tracking¶
Use FTRUMConfig to configure enableTraceUserAction, enableTraceUserView, enableTraceUserResource, enableTrackAppFreeze, enableTrackAppCrash, and enableTrackAppANR to achieve automatic collection and tracking of Action, View, Resource, LongTask, and Error data. If you want custom collection, you can use FTExternalDataManager to report data, as shown in the following examples:
View¶
Usage¶
/// Create a page
///
/// Call this method before `-startViewWithName`. This method is used to record the page loading time. If the loading time cannot be obtained, this method can be omitted.
/// - Parameters:
/// - viewName: Page name
/// - loadTime: Page loading time (nanoseconds)
-(void)onCreateView:(NSString *)viewName loadTime:(NSNumber *)loadTime;
/// Enter page
/// - Parameters:
/// - viewName: Page name
/// - property: Event custom attributes (optional)
-(void)startViewWithName:(NSString *)viewName property:(nullable NSDictionary *)property;
/// Update the loading time of the current RUM View.
/// Must be called between `-startView` and `-stopView` methods to take effect.
/// - Parameter duration: Loading duration (nanoseconds).
-(void)updateViewLoadingTime:(NSNumber *)duration;
/// Leave page
/// - Parameter property: Event custom attributes (optional)
-(void)stopViewWithProperty:(nullable NSDictionary *)property;
/// Create a page
///
/// Call this method before `-startViewWithName`. This method is used to record the page loading time. If the loading time cannot be obtained, this method can be omitted.
/// - Parameters:
/// - viewName: Page name
/// - loadTime: Page loading time (ns)
open func onCreateView(_ viewName: String, loadTime: NSNumber)
/// Enter page
/// - Parameters:
/// - viewName: Page name
/// - property: Event custom attributes (optional)
open func startView(withName viewName: String, property: [AnyHashable : Any]?)
/// Update the loading time of the current RUM View.
/// Must be called between `-startView` and `-stopView` methods to take effect.
/// - Parameter duration: Loading duration (nanoseconds).
open func updateViewLoadingTime(_ duration: NSNumber)
/// Leave page
/// - Parameter property: Event custom attributes (optional)
open func stopView(withProperty property: [AnyHashable : Any]?)
Code Example¶
- (void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
// Scenario 1:
[[FTExternalDataManager sharedManager] startViewWithName:@"TestVC"];
// Scenario 2: Dynamic parameters
[[FTExternalDataManager sharedManager] startViewWithName:@"TestVC" property:@{@"custom_key":@"custom_value"}];
}
-(void)viewDidDisappear:(BOOL)animated{
[super viewDidDisappear:animated];
// Scenario 1:
[[FTExternalDataManager sharedManager] stopView];
// Scenario 2: Dynamic parameters
[[FTExternalDataManager sharedManager] stopViewWithProperty:@{@"custom_key":@"custom_value"}];
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// Scenario 1:
FTExternalDataManager.shared().startView(withName: "TestVC")
// Scenario 2: Dynamic parameters
FTExternalDataManager.shared().startView(withName: "TestVC",property: ["custom_key":"custom_value"])
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
// Scenario 1:
FTExternalDataManager.shared().stopView()
// Scenario 2: Dynamic parameters
FTExternalDataManager.shared().stopView(withProperty: ["custom_key":"custom_value"])
}
Action¶
Usage¶
/// Start RUM Action.
///
/// RUM will bind Resource, Error, LongTask events that this Action may trigger. Avoid adding multiple times within 0.1 s. Only one Action can be associated with the same View at the same time. If the previous Action has not ended, the new Action will be discarded.
/// Does not interfere with Actions added via the `addAction:actionType:property` method.
///
/// - Parameters:
/// - actionName: Event name
/// - actionType: Event type
/// - property: Event custom attributes (optional)
- (void)startAction:(NSString *)actionName actionType:(NSString *)actionType property:(nullable NSDictionary *)property;
/// Add Action event. No duration, no discard logic.
///
/// Does not interfere with RUM Actions started via `startAction:actionType:property:`.
/// - Parameters:
/// - actionName: Event name
/// - actionType: Event type
/// - property: Event custom attributes (optional)
- (void)addAction:(NSString *)actionName actionType:(NSString *)actionType property:(nullable NSDictionary *)property;
/// Start RUM Action.
///
/// RUM will bind Resource, Error, LongTask events that this Action may trigger. Avoid adding multiple times within 0.1 s. Only one Action can be associated with the same View at the same time. If the previous Action has not ended, the new Action will be discarded.
/// Does not interfere with Actions added via the `addAction:actionType:property` method.
///
/// - Parameters:
/// - actionName: Event name
/// - actionType: Event type
/// - property: Event custom attributes (optional)
open func startAction(_ actionName: String, actionType: String, property: [AnyHashable : Any]?)
/// Add Action event. No duration, no discard logic.
///
/// Does not interfere with RUM Actions started via `startAction:actionType:property:`.
/// - Parameters:
/// - actionName: Event name
/// - actionType: Event type
/// - property: Event custom attributes (optional)
open func addAction(_ actionName: String, actionType: String, property: [AnyHashable : Any]?)
Code Example¶
Error¶
Usage¶
/// Add Error event
/// - Parameters:
/// - type: error type
/// - message: Error message
/// - stack: Stack information
/// - property: Event custom attributes (optional)
- (void)addErrorWithType:(NSString *)type message:(NSString *)message stack:(NSString *)stack property:(nullable NSDictionary *)property;
/// Add Error event
/// - Parameters:
/// - type: error type
/// - state: Program running state
/// - message: Error message
/// - stack: Stack information
/// - property: Event custom attributes (optional)
- (void)addErrorWithType:(NSString *)type state:(FTAppState)state message:(NSString *)message stack:(NSString *)stack property:(nullable NSDictionary *)property;
/// Add Error event
/// - Parameters:
/// - type: error type
/// - message: Error message
/// - stack: Stack information
/// - property: Event custom attributes (optional)
open func addError(withType: String, message: String, stack: String, property: [AnyHashable : Any]?)
/// Add Error event
/// - Parameters:
/// - type: error type
/// - state: Program running state
/// - message: Error message
/// - stack: Stack information
/// - property: Event custom attributes (optional)
open func addError(withType type: String, state: FTAppState, message: String, stack: String, property: [AnyHashable : Any]?)
Code Example¶
// Scenario 1
[[FTExternalDataManager sharedManager] addErrorWithType:@"type" message:@"message" stack:@"stack"];
// Scenario 2: Dynamic parameters
[[FTExternalDataManager sharedManager] addErrorWithType:@"ios_crash" message:@"crash_message" stack:@"crash_stack" property:@{@"custom_key":@"custom_value"}];
// Scenario 3: Dynamic parameters
[[FTExternalDataManager sharedManager] addErrorWithType:@"ios_crash" state:FTAppStateUnknown message:@"crash_message" stack:@"crash_stack" property:@{@"custom_key":@"custom_value"}];
// Scenario 1
FTExternalDataManager.shared().addError(withType: "custom_type", message: "custom_message", stack: "custom_stack")
// Scenario 2: Dynamic parameters
FTExternalDataManager.shared().addError(withType: "custom_type", message: "custom_message", stack: "custom_stack",property: ["custom_key":"custom_value"])
// Scenario 3: Dynamic parameters
FTExternalDataManager.shared().addError(withType: "custom_type", state: .unknown, message: "custom_message", stack: "custom_stack", property: ["custom_key":"custom_value"])
LongTask¶
Usage¶
Code Example¶
Resource¶
Usage¶
/// HTTP request start
/// - Parameters:
/// - key: Request identifier
/// - property: Event custom attributes (optional)
- (void)startResourceWithKey:(NSString *)key property:(nullable NSDictionary *)property;
/// HTTP add request data
///
/// - Parameters:
/// - key: Request identifier
/// - metrics: Request-related performance attributes
/// - content: Request-related data
- (void)addResourceWithKey:(NSString *)key metrics:(nullable FTResourceMetricsModel *)metrics content:(FTResourceContentModel *)content;
/// HTTP request end
/// - Parameters:
/// - key: Request identifier
/// - property: Event custom attributes (optional)
- (void)stopResourceWithKey:(NSString *)key property:(nullable NSDictionary *)property;
/// HTTP request start
/// - Parameters:
/// - key: Request identifier
/// - property: Event custom attributes (optional)
open func startResource(withKey key: String, property: [AnyHashable : Any]?)
/// HTTP request end
/// - Parameters:
/// - key: Request identifier
/// - property: Event custom attributes (optional)
open func stopResource(withKey key: String, property: [AnyHashable : Any]?)
/// HTTP add request data
///
/// - Parameters:
/// - key: Request identifier
/// - metrics: Request-related performance attributes
/// - content: Request-related data
open func addResource(withKey key: String, metrics: FTResourceMetricsModel?, content: FTResourceContentModel)
Code Example¶
//Step 1: Before request starts
[[FTExternalDataManager sharedManager] startResourceWithKey:key];
//Step 2: Request completes
[[FTExternalDataManager sharedManager] stopResourceWithKey:key];
//Step 3: Assemble Resource data
//FTResourceContentModel data
FTResourceContentModel *content = [[FTResourceContentModel alloc]init];
content.httpMethod = request.HTTPMethod;
content.requestHeader = request.allHTTPHeaderFields;
content.responseHeader = httpResponse.allHeaderFields;
content.httpStatusCode = httpResponse.statusCode;
content.responseBody = responseBody;
//ios native
content.error = error;
//If time data for each phase can be obtained
//FTResourceMetricsModel
//ios native obtained NSURLSessionTaskMetrics data, directly use FTResourceMetricsModel's initialization method
FTResourceMetricsModel *metricsModel = [[FTResourceMetricsModel alloc]initWithTaskMetrics:metrics];
//Other platforms, all time data in nanoseconds
FTResourceMetricsModel *metricsModel = [[FTResourceMetricsModel alloc]init];
//Step 4: add resource. If no time data, pass nil for metrics
[[FTExternalDataManager sharedManager] addResourceWithKey:key metrics:metricsModel content:content];
//Step 1: Before request starts
FTExternalDataManager.shared().startResource(withKey: key)
//Step 2: Request completes
FTExternalDataManager.shared().stopResource(withKey: resource.key)
//Step 3: ① Assemble Resource data
let contentModel = FTResourceContentModel(request: task.currentRequest!, response: task.response as? HTTPURLResponse, data: resource.data, error: error)
//② If time data for each phase can be obtained
//FTResourceMetricsModel
//ios native obtained NSURLSessionTaskMetrics data, directly use FTResourceMetricsModel's initialization method
var metricsModel:FTResourceMetricsModel?
if let metrics = resource.metrics {
metricsModel = FTResourceMetricsModel(taskMetrics:metrics)
}
//Other platforms, all time data in nanoseconds
metricsModel = FTResourceMetricsModel()
...
//Step 4: add resource. If no time data, pass nil for metrics
FTExternalDataManager.shared().addResource(withKey: resource.key, metrics: metricsModel, content: contentModel)
Logger Log Printing¶
In the SDK initialization Log configuration, configure enableCustomLog to allow custom log addition.
Currently, the log content is limited to 30 KB. Characters beyond this limit will be truncated.
Usage¶
//
// FTLogger.h
// FTMobileSDK
/// Add info level custom log
/// - Parameters:
/// - content: Log content
/// - property: Custom attributes (optional)
-(void)info:(NSString *)content property:(nullable NSDictionary *)property;
/// Add warning level custom log
/// - Parameters:
/// - content: Log content
/// - property: Custom attributes (optional)
-(void)warning:(NSString *)content property:(nullable NSDictionary *)property;
/// Add error level custom log
/// - Parameters:
/// - content: Log content
/// - property: Custom attributes (optional)
-(void)error:(NSString *)content property:(nullable NSDictionary *)property;
/// Add critical level custom log
/// - Parameters:
/// - content: Log content
/// - property: Custom attributes (optional)
-(void)critical:(NSString *)content property:(nullable NSDictionary *)property;
/// Add ok level custom log
/// - Parameters:
/// - content: Log content
/// - property: Custom attributes (optional)
-(void)ok:(NSString *)content property:(nullable NSDictionary *)property;
/// Add custom log
/// - Parameters:
/// - content: Log content
/// - status: Log status level
/// - property: Custom attributes (optional)
- (void)log:(NSString *)content status:(NSString *)status property:(nullable NSDictionary *)property;
open class FTLogger : NSObject, FTLoggerProtocol {}
public protocol FTLoggerProtocol : NSObjectProtocol {
/// Add info level custom log
/// - Parameters:
/// - content: Log content
/// - property: Custom attributes (optional)
optional func info(_ content: String, property: [AnyHashable : Any]?)
/// Add warning level custom log
/// - Parameters:
/// - content: Log content
/// - property: Custom attributes (optional)
optional func warning(_ content: String, property: [AnyHashable : Any]?)
/// Add error level custom log
/// - Parameters:
/// - content: Log content
/// - property: Custom attributes (optional)
optional func error(_ content: String, property: [AnyHashable : Any]?)
/// Add critical level custom log
/// - Parameters:
/// - content: Log content
/// - property: Custom attributes (optional)
optional func critical(_ content: String, property: [AnyHashable : Any]?)
/// Add ok level custom log
/// - Parameters:
/// - content: Log content
/// - property: Custom attributes (optional)
optional func ok(_ content: String, property: [AnyHashable : Any]?)
/// Add custom log
/// - Parameters:
/// - content: Log content
/// - status: Log status level
/// - property: Custom attributes (optional)
optional func log(_ content: String, status: String, property: [AnyHashable : Any]?)
}
Log Levels¶
Code Example¶
Custom Log Output to Console¶
Set printCustomLogToConsole = YES to enable outputting custom logs to the console. You will see logs in the following format in the xcode debug console:
2023-06-29 13:47:56.960021+0800 App[64731:44595791]: Standard prefix for os_log output;
[IOS APP]: Prefix to distinguish SDK output custom logs;
[INFO]: Custom log level;
content: Custom log content;
{K=V,...,Kn=Vn}: Custom attributes.
Trace Network Link Tracing¶
You can configure FTTraceConfig to enable automatic mode, and also support users to custom add Trace related data. The API for custom addition is as follows:
NSString *key = [[NSUUID UUID]UUIDString];
NSURL *url = [NSURL URLWithString:@"http://www.baidu.com"];
//Manual operation required: Obtain traceHeader before the request and add it to the request header.
NSDictionary *traceHeader = [[FTTraceManager sharedInstance] getTraceHeaderWithKey:key url:url];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
if (traceHeader && traceHeader.allKeys.count>0) {
[traceHeader enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
[request setValue:value forHTTPHeaderField:field];
}];
}
NSURLSession *session=[NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:[NSOperationQueue mainQueue]];
NSURLSessionTask *task = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
//Your code
}];
[task resume];
let url:URL = NSURL.init(string: "https://www.baidu.com")! as URL
if let traceHeader = FTExternalDataManager.shared().getTraceHeader(withKey: NSUUID().uuidString, url: url) {
let request = NSMutableURLRequest(url: url)
//Manual operation required: Obtain traceHeader before the request and add it to the request header.
for (a,b) in traceHeader {
request.setValue(b as? String, forHTTPHeaderField: a as! String)
}
let task = URLSession.shared.dataTask(with: request as URLRequest) { data, response, error in
//Your code
}
task.resume()
}
Custom Collection of Network via Forwarding URLSession Delegate¶
The SDK provides a class FTURLSessionDelegate, which can be used to perform custom RUM Resource collection and link tracing for network requests initiated by a specific URLSession.
FTURLSessionDelegatesupports interceptingURLResquestby setting thetraceInterceptorblock for custom link tracing (supported from SDK version 1.5.9 and above). Priority >FTTraceConfig.traceInterceptor.FTURLSessionDelegatesupports custom RUM Resource attributes that need additional collection via theproviderblock. Priority >FTRumConfig.resourcePropertyProvider.FTURLSessionDelegatesupports custom interception of SessionTask Error by setting theerrorFilterblock. (Supported from SDK version 1.5.17 and above)- return YES: Intercept, RUM-Error will not add this
network_error. - return NO: Do not intercept, RUM-Error will add this
network_error. - When used together with
FTRumConfig.enableTraceUserResourceandFTTraceConfig.enableAutoTrace, the priority is: Custom > Automatic Collection.
Three methods are provided below to meet different user scenarios.
Method One¶
Directly set the delegate object of the URLSession to an instance of FTURLSessionDelegate.
id<NSURLSessionDelegate> delegate = [[FTURLSessionDelegate alloc]init];
// Add custom RUM resource attributes. It is recommended to add the project abbreviation prefix to tag names, e.g., `df_tag_name`.
delegate.provider = ^NSDictionary * _Nullable(NSURLRequest *request, NSURLResponse *response, NSData *data, NSError *error) {
NSString *body = [[NSString alloc] initWithData:request.HTTPBody encoding:NSUTF8StringEncoding];
return @{@"df_requestbody":body};
};
// Support custom trace. Return TraceContext after confirming interception, return nil if not intercepted.
delegate.traceInterceptor = ^FTTraceContext * _Nullable(NSURLRequest *request) {
FTTraceContext *context = [FTTraceContext new];
context.traceHeader = @{@"trace_key":@"trace_value"};
context.traceId = @"trace_id";
context.spanId = @"span_id";
return context;
};
// Custom whether to intercept SessionTask Error. return YES: intercept, RUM-Error will not add this `network_error`.
delegate.errorFilter = ^BOOL(NSError * _Nonnull error) {
return error.code == NSURLErrorCancelled;
};
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:delegate delegateQueue:nil];
let delegate = FTURLSessionDelegate.init()
// Add custom RUM resource attributes. It is recommended to add the project abbreviation prefix to tag names, e.g., `df_tag_name`.
delegate.provider = { request,response,data,error in
var extraData:Dictionary<String, Any> = Dictionary()
if let data = data,let requestBody = String(data: data, encoding: .utf8) {
extraData["df_requestBody"] = requestBody
}
if let error = error {
extraData["df_error"] = error.localizedDescription
}
return extraData
}
// Support custom trace. Return TraceContext after confirming interception, return nil if not intercepted.
delegate.traceInterceptor = { request in
let traceContext = FTTraceContext()
traceContext.traceHeader = ["trace_key":"trace_value"]
traceContext.spanId = "spanId"
traceContext.traceId = "traceId"
return traceContext
}
delegate.errorFilter = { error in
return (error as? URLError)?.code == .cancelled
}
let session = URLSession.init(configuration: URLSessionConfiguration.default, delegate:delegate
, delegateQueue: nil)
Method Two¶
Make the delegate object of the URLSession inherit from the FTURLSessionDelegate class.
If the delegate object implements the following methods, be sure to call the corresponding method of the parent class within the method.
-URLSession:dataTask:didReceiveData:-URLSession:task:didCompleteWithError:-URLSession:task:didFinishCollectingMetrics:
@interface InstrumentationInheritClass:FTURLSessionDelegate
@property (nonatomic, strong) NSURLSession *session;
@end
@implementation InstrumentationInheritClass
-(instancetype)init{
self = [super init];
if(self){
_session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:self delegateQueue:nil];
// Add custom RUM resource attributes. It is recommended to add the project abbreviation prefix to tag names, e.g., `df_tag_name`.
self.provider = ^NSDictionary * _Nullable(NSURLRequest *request, NSURLResponse *response, NSData *data, NSError *error) {
NSString *body = [[NSString alloc] initWithData:request.HTTPBody encoding:NSUTF8StringEncoding];
return @{@"df_requestbody":body};
};
// Support custom trace. Return TraceContext after confirming interception, return nil if not intercepted.
self.traceInterceptor = ^FTTraceContext * _Nullable(NSURLRequest *request) {
FTTraceContext *context = [FTTraceContext new];
context.traceHeader = @{@"trace_key":@"trace_value"};
context.traceId = @"trace_id";
context.spanId = @"span_id";
return context;
};
// Custom whether to intercept SessionTask Error. return YES: intercept, RUM-Error will not add this `network_error`.
self.errorFilter = ^BOOL(NSError * _Nonnull error) {
return error.code == NSURLErrorCancelled;
};
}
return self;
}
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics{
// Must call the parent class method
[super URLSession:session task:task didFinishCollectingMetrics:metrics];
// Your own logic
// ......
}
@end
class InheritHttpEngine:FTURLSessionDelegate {
var session:URLSession?
override init(){
session = nil
super.init()
let configuration = URLSessionConfiguration.default
configuration.timeoutIntervalForRequest = 30
session = URLSession.init(configuration: configuration, delegate:self, delegateQueue: nil)
override init() {
super.init()
// Add custom RUM resource attributes. It is recommended to add the project abbreviation prefix to tag names, e.g., `df_tag_name`.
provider = { request,response,data,error in
var extraData:Dictionary<String, Any> = Dictionary()
if let data = data,let requestBody = String(data: data, encoding: .utf8) {
extraData["df_requestBody"] = requestBody
}
if let error = error {
extraData["df_error"] = error.localizedDescription
}
return extraData
}
// Support custom trace. Return TraceContext after confirming interception, return nil if not intercepted.
traceInterceptor = { request in
let traceContext = FTTraceContext()
traceContext.traceHeader = ["trace_key":"trace_value"]
traceContext.spanId = "spanId"
traceContext.traceId = "traceId"
return traceContext
}
errorFilter = { error in
return (error as? URLError)?.code == .cancelled
}
}
}
override func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) {
// Must call the parent class method
super.urlSession(session, task: task, didFinishCollecting: metrics)
// User's own logic
// ......
}
}
Method Three¶
Make the delegate object of the URLSession conform to the FTURLSessionDelegateProviding protocol.
- Implement the get method for the
ftURLSessionDelegateproperty in the protocol. - Forward the following URLSession delegate methods to
ftURLSessionDelegateto enable SDK data collection.-URLSession:dataTask:didReceiveData:-URLSession:task:didCompleteWithError:-URLSession:task:didFinishCollectingMetrics:
@interface UserURLSessionDelegateClass:NSObject<NSURLSessionDataDelegate,FTURLSessionDelegateProviding>
@end
@implementation UserURLSessionDelegateClass
@synthesize ftURLSessionDelegate = _ftURLSessionDelegate;
- (nonnull FTURLSessionDelegate *)ftURLSessionDelegate {
if(!_ftURLSessionDelegate){
_ftURLSessionDelegate = [[FTURLSessionDelegate alloc]init];
// Add custom RUM resource attributes. It is recommended to add the project abbreviation prefix to tag names, e.g., `df_tag_name`.
_ftURLSessionDelegate.provider = ^NSDictionary * _Nullable(NSURLRequest *request, NSURLResponse *response, NSData *data, NSError *error) {
NSString *body = [[NSString alloc] initWithData:request.HTMPBody encoding:NSUTF8StringEncoding];
return @{@"df_requestbody":body};
};
// Support custom trace. Return TraceContext after confirming interception, return nil if not intercepted.
_ftURLSessionDelegate.requestInterceptor = ^NSURLRequest * _Nonnull(NSURLRequest * _Nonnull request) {
NSDictionary *traceHeader = [[FTExternalDataManager sharedManager] getTraceHeaderWithUrl:request.URL];
NSMutableURLRequest *newRequest = [request mutableCopy];
if(traceHeader){
for (NSString *key in traceHeader.allKeys) {
[newRequest setValue:traceHeader[key] forHTTPHeaderField:key];
}
}
return newRequest;
};
// Custom whether to intercept SessionTask Error. return YES: intercept, RUM-Error will not add this `network_error`.
_ftURLSessionDelegate.errorFilter = ^BOOL(NSError * _Nonnull error) {
return error.code == NSURLErrorCancelled;
};
}
return _ftURLSessionDelegate;
}
- (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data{
[self.ftURLSessionDelegate URLSession:session dataTask:dataTask didReceiveData:data];
}
- (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error{
[self.ftURLSessionDelegate URLSession:session task:task didCompleteWithError:error];
}
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics{
[self.ftURLSessionDelegate URLSession:session task:task didFinishCollectingMetrics:metrics];
}
@end
class HttpEngine:NSObject,URLSessionDataDelegate,FTURLSessionDelegateProviding {
var ftURLSessionDelegate: FTURLSessionDelegate = FTURLSessionDelegate()
var session:URLSession?
override init(){
session = nil
super.init()
let configuration = URLSessionConfiguration.default
configuration.timeoutIntervalForRequest = 30
session = URLSession.init(configuration: configuration, delegate:self, delegateQueue: nil)
// Add custom RUM resource attributes. It is recommended to add the project abbreviation prefix to tag names, e.g., `df_tag_name`.
ftURLSessionDelegate.provider = { request,response,data,error in
var extraData:Dictionary<String, Any> = Dictionary()
if let data = data,let requestBody = String(data: data, encoding: .utf8) {
extraData["df_requestBody"] = requestBody
}
if let error = error {
extraData["df_error"] = error.localizedDescription
}
return extraData
}
// Support custom trace. Return TraceContext after confirming interception, return nil if not intercepted.
ftURLSessionDelegate.traceInterceptor = { request in
let traceContext = FTTraceContext()
traceContext.traceHeader = ["trace_key":"trace_value"]
traceContext.spanId = "spanId"
traceContext.traceId = "traceId"
return traceContext
}
ftURLSessionDelegate.errorFilter = { error in
return (error as? URLError)?.code == .cancelled
}
}
// The following methods must be implemented.
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
ftURLSessionDelegate.urlSession(session, dataTask: dataTask, didReceive: data)
}
func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) {
ftURLSessionDelegate.urlSession(session, task: task, didFinishCollecting: metrics)
}
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
ftURLSessionDelegate.urlSession(session, task: task, didCompleteWithError: error)
}
}
User Binding and Unbinding¶
Use FTMobileAgent to bind user information and unbind the current user.
/// Bind user information. This method can be called after the user logs in successfully to bind user information.
///
/// - Parameters:
/// - Id: User Id
/// - userName: User name (optional)
/// - userEmail: User email (optional)
/// - extra: User's extra information (optional)
- (void)bindUserWithUserID:(NSString *)Id userName:(nullable NSString *)userName userEmail:(nullable NSString *)userEmail extra:(nullable NSDictionary *)extra;
/// Unbind the current user. This method can be called after the user logs out to unbind user information.
- (void)unbindUser;
/// Bind user information. This method can be called after the user logs in successfully to bind user information.
///
/// - Parameters:
/// - Id: User Id
/// - userName: User name (optional)
/// - userEmail: User email (optional)
/// - extra: User's extra information (optional)
open func bindUser(withUserID Id: String, userName: String?, userEmail: String?, extra: [AnyHashable : Any]?)
/// Unbind the current user. This method can be called after the user logs out to unbind user information.
open func unbindUser()
For addition rule precautions for extra, please refer to here.
Shut Down SDK¶
Use FTMobileAgent to shut down the SDK. Be sure to call it in the main thread, otherwise thread safety issues may occur. If you dynamically change the SDK configuration, you need to shut it down first to avoid generating erroneous data.
Clear SDK Cache Data¶
Use FTMobileAgent to clear unreported cache data.
Active Data Sync¶
Use FTMobileAgent to actively sync data.
FTMobileConfig.autoSync = NO, then you need to perform data synchronization manually.
Active Dynamic Configuration Sync¶
Usage¶
Use FTMobileAgent to actively sync dynamic configuration. When automatic updates do not meet the requirements, adjust the update timing by actively calling the method.
FTMobileConfig.remoteConfiguration= YES, then calling the active dynamic configuration sync method takes effect.
/// Actively update remote configuration. The call frequency is affected by FTMobileConfig.remoteConfigMiniUpdateInterval.
+ (void)updateRemoteConfig;
/// Actively update remote configuration. This method ignores the FTMobileConfig.remoteConfigMiniUpdateInterval configuration.
/// - Parameters:
/// - miniUpdateInterval: Remote configuration time interval, unit seconds [0,)
/// - completion: Callback after the request is completed, supports custom adjustment of the configuration model in the callback.
+ (void)updateRemoteConfigWithMiniUpdateInterval:(NSInteger)miniUpdateInterval
completion:(nullable FTRemoteConfigFetchCompletionBlock)completion;
/// Actively update remote configuration. The call frequency is affected by FTMobileConfig.remoteConfigMiniUpdateInterval.
open class func updateRemoteConfig()
/// Actively update remote configuration. This method ignores the FTMobileConfig.remoteConfigMiniUpdateInterval configuration.
/// - Parameters:
/// - miniUpdateInterval: Remote configuration time interval, unit seconds [0,)
/// - completion: Callback after the request is completed, supports custom adjustment of the configuration model in the callback.
open class func updateRemoteConfig(withMiniUpdateInterval miniUpdateInterval: Int, completion: FTRemoteConfigFetchCompletionBlock? = nil)
Remote Configuration Fetch Callback and Custom Configuration¶
SDK >= 1.5.19, supports custom modification of the final configuration via FTRemoteConfigFetchCompletionBlock return value.
/**
* @brief Remote configuration fetch completion callback Block type.
* @details This Block is used to receive the fetch/parse result of the remote configuration, and returns the configuration model finally used by the SDK. The SDK adjusts corresponding functions based on the returned result.
*
* @param success Boolean value, whether the fetch/parse was successful.
* - YES: Operation successful, does not guarantee configuration data is non-empty.
* - NO: Operation failed (function not enabled/not reached minimum time interval/network exception/data parsing failed, etc.).
* @param error Error information object, only returns valid error details when success=NO, always nil when success=YES.
* @param model Structured configuration model, only returns a valid instance when success=YES and configuration data is non-empty, otherwise returns nil.
* @param content Raw configuration dictionary, raw data without structured parsing, only returns a valid dictionary when success=YES and configuration data is non-empty, otherwise returns nil.
*
* @return FTRemoteConfigModel optional instance, the configuration model finally used by the SDK.
* - Successful scenario (success=YES):
* 1. Return non-nil instance: SDK uses this modified model to adjust functions.
* 2. Return nil: SDK uses the originally parsed model (if it exists).
* - Failed scenario (success=NO):
* Must return nil, SDK ignores the callback result.
*/
typedef FTRemoteConfigModel*_Nullable(^FTRemoteConfigFetchCompletionBlock)(BOOL success,
NSError * _Nullable error,
FTRemoteConfigModel * _Nullable model,
NSDictionary<NSString *, id> * _Nullable content);
Callback Priority: +updateRemoteConfigWithMiniUpdateInterval:completion: (exclusive callback for active method) > FTMobileConfig.remoteConfigFetchCompletionBlock (global unified callback).
Additional Notes:
- The exclusive
completionof the active method only takes effect for the current active configuration fetch, with higher priority, suitable for "one-time personalized configuration processing" scenarios. - The global
remoteConfigFetchCompletionBlocktakes effect for all remote configuration fetch behaviors (including automatic updates, active calls without exclusive completion, and when the exclusive completion returns nil), suitable for "global unified configuration processing" scenarios, set once and effective throughout.
Usage Example
The current example only shows the callback for the active sync method. The usage logic of the global remoteConfigFetchCompletionBlock is consistent with this example.
```objective-c
[FTMobileAgent updateRemoteConfigWithMiniUpdateInterval:0 completion:^FTRemoteConfigModel * _Nullable(BOOL success, NSError * _Nullablererror, FTRemoteConfigModel * _Nullable model, NSDictionary
} // Process configuration data when the operation is successful. if (success) { // Get custom environment variable values from remote configuration. // Example: Need to adjust for specified users, specified user is uid = @"user_1". NSString *userId = content[@"custom_userid"]; if ([userId isEqualToString:@"user_1"]) { model.rumSampleRate = @(1); model.logSampleRate = @(1); model.traceSampleRate = @(1); } } // Return the modified model (returning model is equivalent to returning nil when not modified, both use the original model). return model; }]; ```
FTMobileAgent.updateRemoteConfig(withMiniUpdateInterval: 0) { (success: Bool, error: Error?, model: FTRemoteConfigModel?, content: [String: Any]?) -> FTRemoteConfigModel? in
if let error = errofr {
print("remoteConfigFetch error:\(error.localizedDescription)")
}
// Process configuration data when the operation is successful.
if success {
// Get custom environment variable values from remote configuration.
// Example: Need to adjust for specified users, specified user is uid = @"user_1".
let userId = content?["custom_userid"] as? String
if userId == "user_1" {
model?.rumSampleRate = 1
model?.logSampleRate = 1
model?.traceSampleRate = 1
}
}
// Return the modified model (returning model is equivalent to returning nil when not modified, both use the original model).
return model
}
Add Custom Tags¶
Use FTMobileAgent to dynamically add tags while the SDK is running. For addition rule precautions, please refer to here.
/// Add SDK global tag, effective for RUM, Log data.
/// - Parameter context: Custom data.
+ (void)appendGlobalContext:(NSDictionary <NSString*,id>*)context;
/// Add RUM custom tag, effective for RUM data.
/// - Parameter context: Custom data.
+ (void)appendRUMGlobalContext:(NSDictionary <NSString*,id>*)context;
/// Add Log global tag, effective for Log data.
/// - Parameter context: Custom data.
+ (void)appendLogGlobalContext:(NSDictionary <NSString*,id>*)context;
/// Add SDK global tag, effective for RUM, Log data.
/// - Parameter context: Custom data.
open class func appendGlobalContext(_ context: [String : Any])
/// Add RUM custom tag, effective for RUM data.
/// - Parameter context: Custom data.
open class func appendRUMGlobalContext(_ context: [String : Any])
/// Add Log global tag, effective for Log data.
/// - Parameter context: Custom data.
open class func appendLogGlobalContext(_ context: [String : Any])
Symbol File Upload¶
Xcode Add Run Script Script (Only Supports Datakit [Local Deployment])¶
-
XCode add custom Run Script Phase:
Build Phases -> + -> New Run Script Phase. -
Copy the script into the Xcode project's build phase run script. Parameters such as <app_id>, <datakit_address>, <env>, <dataway_token> need to be set in the script.
-
Script: FTdSYMUploader.sh
#Parameters that need to be configured in the script.
#<app_id>
FT_APP_ID="YOUR_APP_ID"
#<datakit_address>
FT_DATAKIT_ADDRESS="YOUR_DATAKIT_ADDRESS"
#<env> Environment field. Attribute values: prod/gray/pre/common/local. Must be consistent with SDK settings.
FT_ENV="common"
#<dataway_token> Token for dataway in the datakit.conf configuration file.
FT_TOKEN="YOUR_DATAWAY_TOKEN"
# Whether to only package the dSYM file into a zip (optional, default 0 upload), 1=do not upload, only package dSYM zip, 0=upload. You can search for FT_DSYM_ZIP_FILE in the script output log to view the DSYM_SYMBOL.zip file path.
FT_DSYM_ZIP_ONLY=0
If you need to use multiple environments to upload symbol files for different environments, you can refer to the following method.
Multi-Environment Configuration Parameters¶
Example: Use .xcconfig configuration file to configure multiple environments.
1. Create an xcconfig configuration file and configure variables in the .xcconfig file.
For methods to create an xcconfig configuration file, please refer to: Adding a Build Configuration File to Your Project.
//If using cocoapods, you need to add the path of pods' .xcconfig to your .xcconfig file.
//Import the .xcconfig corresponding to the pod.
#include "Pods/Target Support Files/Pods-Demo/Pods-Demo.pre.xcconfig"
SDK_APP_ID = app_id_common
SDK_ENV = common
// URL // Need to add $()
SDK_DATAKIT_ADDRESS = http:/$()/xxxxxxxx:9529
SDK_DATAWAY_TOKEN = token
At this point, user-defined parameters have been automatically added. You can view them through Target —> Build Settings -> + -> Add User-Defined Setting.
2. Configure parameters in the script.
#Parameters that need to be configured in the script.
#<app_id>
FT_APP_ID=${SDK_APP_ID}
#<datakit_address>
FT_DATAKIT_ADDRESS=${SDK_DATAKIT_ADDRESS}
#<dev> Environment field. Attribute values: prod/gray/pre/common/local. Must be consistent with SDK settings.
FT_ENV=${SDK_ENV}
#<dataway_token> Token for dataway in the datakit.conf configuration file.
FT_TOKEN=${SDK_DATAWAY_TOKEN}
3. Configure SDK.
Do parameter mapping in the Info.plist file.
Get the parameters in Info.plist to configure the SDK.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let info = Bundle.main.infoDictionary!
let appid:String = info["SDK_APP_ID"] as! String
let env:String = info["SDK_ENV"] as! String
let config = FTMobileConfig.init(datakitUrl: UserDefaults.datakitURL)
config.enableSDKDebugLog = true
config.autoSync = false
config.env = env
.....
}
For detailed details, please refer to the multi-environment usage in SDK Demo.
Terminal Run Script¶
Symbol File Upload (Only Supports Datakit [Local Deployment])¶
sh FTdSYMUploader.sh <datakit_address> <app_id> <version> <env> <dataway_token> <dSYMBOL_src_dir>
Example:
sh FTdSYMUploader.sh http://10.0.0.1:9529 appid_mock 1.0.6 prod tkn_mock /Users/mock/Desktop/dSYMs
Only Compress Symbol Files¶
sh FTdSYMUploader.sh -dSYMFolderPath <dSYMBOL_src_dir> -z
Example:
sh FTdSYMUploader.sh -dSYMFolderPath /Users/mock/Desktop/dSYMs -z You can search for
FT_DSYM_ZIP_FILEin the script output log to view the Zip file path.
Parameter Description:
<datakit_address>: DataKit service address, such ashttp://localhost:9529.<app_id>: Corresponds to RUM'sapplicationId.<env>: Corresponds to RUM'senv.<version>: Applicationversion, the value ofCFBundleShortVersionString.<dataway_token>: Token for dataway in thedatakit.confconfiguration file.<dSYMBOL_src_dir>: Directory path containing all.dSYMfiles.
Manual Upload¶
Widget Extension Data Collection¶
Widget Extension Data Collection Support¶
- Logger custom logs.
- Trace link tracing.
- RUM data collection.
- Manual collection (RUM User Data Tracking).
- Automatic collection of crash logs, HTTP Resource data.
Since HTTP Resource data is bound to Views, users need to manually collect View data.
Widget Extension Collection Configuration¶
Use FTExtensionConfig to configure the automatic switch for Widget Extension data collection and the file sharing Group Identifier. Other configurations use the already set configurations from the main project SDK.
| Field | Type | Required | Description |
|---|---|---|---|
| groupIdentifier | NSString | Yes | File sharing Group Identifier. |
| enableSDKDebugLog | BOOL | No (Default NO) | Set whether to allow SDK to print Debug logs. |
| enableTrackAppCrash | BOOL | No (Default NO) | Set whether to collect crash logs. |
| enableRUMAutoTraceResource | BOOL | No (Default NO) | Set whether to track user network requests (only affects native http). |
| enableTracerAutoTrace | BOOL | No (Default NO) | Set whether to enable automatic http link tracing. |
| memoryMaxCount | NSInteger | No (Default 1000 entries) | Maximum number of data stored in Widget Extension. |
Widget Extension SDK usage example:
let extensionConfig = FTExtensionConfig.init(groupIdentifier: "group.identifier")
extensionConfig.enableTrackAppCrash = true
extensionConfig.enableRUMAutoTraceResource = true
extensionConfig.enableTracerAutoTrace = true
extensionConfig.enableSDKDebugLog = true
FTExtensionManager.start(with: extensionConfig)
FTExternalDataManager.shared().startView(withName: "WidgetDemoEntryView")
At the same time, when setting FTMobileConfig in the main project, groupIdentifiers must be set.
Widget Extension Collected Data Upload¶
The Widget Extension SDK only implements data collection. The data upload logic is handed over to the main project's SDK to implement. The timing for synchronizing collected data to the main project is customized by the user.
Usage¶
// Call in the main project.
/// Track data cached in the App Extension groupIdentifier.
/// - Parameters:
/// - groupIdentifier: groupIdentifier.
/// - completion: Callback after track is completed.
- (void)trackEventFromExtensionWithGroupIdentifier:(NSString *)groupIdentifier completion:(nullable void (^)(NSString *groupIdentifier, NSArray *events)) completion;
Code Example¶
WebView Data Monitoring¶
WebView data monitoring requires integrating the Web Monitoring SDK on the WebView access page.
Custom Tag Usage Example¶
Compilation Configuration Method¶
Multiple Configurations can be created, and precompilation instructions can be used to set values.
- Create multiple Configurations.
- Set preset properties to distinguish different Configurations.
- Use precompilation instructions.
//Target -> Build Settings -> GCC_PREPROCESSOR_DEFINITIONS to configure preset definitions.
#if PRE
#define Track_id @"0000000001"
#define STATIC_TAG @"preprod"
#elif DEVELOP
#define Track_id @"0000000002"
#define STATIC_TAG @"common"
#else
#define Track_id @"0000000003"
#define STATIC_TAG @"prod"
#endif
FTRumConfig *rumConfig = [[FTRumConfig alloc]init];
rumConfig.globalContext = @{@"track_id":Track_id,@"static_tag":STATIC_TAG};
... //Other setting operations.
[[FTMobileAgent sharedInstance] startRumWithConfigOptions:rumConfig];
You can also refer to the Multi-Environment Configuration Parameters method for configuration.
Runtime Read/Write File Method¶
Since the globalContext set after RUM is started will not take effect, users can save it locally and set it to take effect the next time the application starts.
- Save locally via file storage, such as
NSUserDefaults. Configure using theSDKand add code to obtain tag data at the configuration location.
NSString *dynamicTag = [[NSUserDefaults standardUserDefaults] valueForKey:@"DYNAMIC_TAG"]?:@"NO_VALUE";
FTRumConfig *rumConfig = [[FTRumConfig alloc]init];
rumConfig.globalContext = @{@"dynamic_tag":dynamicTag};
... //Other setting operations.
[[FTMobileAgent sharedInstance] startRumWithConfigOptions:rumConfig];
- Add a method to change the file data anywhere.
- Finally, restart the application to take effect.
Add During SDK Runtime¶
After the SDK initialization is completed, use [FTMobileAgent appendGlobalContext:globalContext], [FTMobileAgent appendRUMGlobalContext:globalContext], [FTMobileAgent appendLogGlobalContext:globalContext] to dynamically add tags. After setting, it will take effect immediately. Subsequently, RUM or Log data reported later will automatically add tag data. This usage is suitable for scenarios where data acquisition is delayed, such as tag data that needs to be obtained through network requests.
//SDK initialization pseudo code, get.
[FTMobileAgent startWithConfigOptions:config];
-(void)getInfoFromNet:(Info *)info{
NSDictionary *globalContext = @{@"delay_key", info.value}
[FTMobileAgent appendGlobalContext:globalContext];
}
tvOS Data Collection¶
api >= tvOS 12.0
The initialization and use of the SDK are consistent with the iOS side.
Note that tvOS does not support:
-
WebViewdata detection. -
Device battery monitoring in
FTRumConfig.errorMonitorType.
Common Questions¶
About Crash Log Analysis¶
In Debug and Release modes during development, the thread backtrace captured during a Crash is symbolized. However, the release package does not carry the symbol table. The key backtrace of the exception thread will display the name of the image and will not be converted into valid code symbols. The related information obtained in the crash log are all 16-bit memory addresses and cannot locate the crashing code. Therefore, it is necessary to parse the 16-bit memory addresses into the corresponding classes and methods.
How to Find the dSYM File After Compilation or Packaging¶
- In Xcode, the dSYM file is usually generated together with the compiled .app file and located in the same directory.
- If the project has been archived, you can select
Organizerin theWindowmenu of Xcode, then select the corresponding archive file. Right-click the archive file and selectShow in Finder”. In Finder, find the corresponding.xcarchivefile. Right-click the.xcarchivefile, selectShow Package Contents, then enter thedSYMs` folder to find the corresponding dSYM file.
XCode Does Not Generate dSYM File After Compilation?¶
XCode Release compilation generates dSYM files by default, while Debug compilation does not generate them by default. The corresponding Xcode configuration is as follows:
Build Settings -> Code Generation -> Generate Debug Symbols -> Yes
Build Settings -> Build Option -> Debug Information Format -> DWARF with dSYM File
How to Upload the Symbol Table When BitCode is Enabled?¶
When you upload your bitcode App to the App Store, check the declaration of symbol file (dSYM file) generation in the submission dialog:
- Before configuring the symbol table file, you need to download the dSYM file corresponding to this version from the App Store to the local, and then use the script to process and upload the symbol table file according to the input parameters.
- There is no need to integrate the script into the Target of the Xcode project, nor use the locally generated dSYM file to generate the symbol table file, because the symbol table information in the locally compiled dSYM file is hidden. If you upload using the locally compiled dSYM file, the restored result will be a symbol similar to "__hiden#XXX".
How to Retrieve the dSYM File Corresponding to the App Already Published to the App Store?¶
| Distribution options for the app uploaded to App Store Connect | dSym file |
|---|---|
| Don’t include bitcode Upload symbols |
Retrieve via Xcode. |
| Include bitcode Upload symbols |
Retrieve via iTunes Connect. Retrieve via Xcode, requires using .bcsymbolmap for de-obfuscation processing. |
| Include bitcode Don’t upload symbols |
Retrieve via Xcode, requires using .bcsymbolmap for de-obfuscation processing. |
| Don’t include bitcode Don’t upload symbols |
Retrieve via Xcode. |
Retrieve via Xcode¶
-
Xcode -> Window -> Organizer. -
Select the
Archivestab. -
Find the published archive package, right-click the corresponding archive package, and select
Show in Finder. -
Right-click the located archive file and select
Show Package Contents. -
Select the
dSYMsdirectory. The dSYM file is located inside this directory.
Retrieve via iTunes Connect¶
- Log in to App Store Connect;
- Go to "My Apps (My Apps)".
- In "App Store" or "TestFlight", select a certain version", click "Build Version Metadata (Build Metadata)". On this page, click the button "Download dSYM (Download dSYM)" to download the dSYM file.
.bcsymbolmap De-obfuscation Processing¶
When finding the dSYM file via Xcode, you can see the BCSymbolMaps directory.
Open the terminal and use the following command for de-obfuscation processing.
xcrun dsymutil -symbol-map <BCSymbolMaps_path> <.dSYM_path>
Add Global Variables to Avoid Conflicting Fields¶
To avoid conflicts between custom fields and SDK data, it is recommended to add the project abbreviation prefix to tag names, such as df_tag_name. The key values used in the project can be queried from the source code. When the same variable appears in the SDK global variables and RUM, Log, RUM and Log will override the global variables in the SDK.










