Skip to content

iOS Application Access


Overview

Guance Real User Monitoring can analyze the performance of each iOS application in a visual way by collecting the metrics data of each iOS application.

Precondition

iOS Application Access

Login to Guance Console, enter "Real User Monitoring" page, click "New Application" in the upper right corner, enter "Application Name" and customize "Application ID" in the new window, and click "Create" to select the application type to get access.

  • Application Name (required): The name of the application used to identify the current implementation of user access monitoring.
  • Application ID (required): The unique identification of the application in the current workspace, which is used for SDK data collection and upload matching, and corresponds to the field: app_id after data entry. This field only supports English, numeric, underscore input, up to 48 characters.

Installation

Source Code Addresshttps://github.com/GuanceCloud/datakit-ios

Demohttps://github.com/GuanceDemo/guance-app-demo

1.Configure the Podfile file.

  • Use Dynamic Library
platform :ios, '10.0' 
use_frameworks!
def shared_pods
pod 'FTMobileSDK', '[latest_version]'
# If you need to collect widget Extension data
pod 'FTMobileSDK/Extension', '[latest_version]'
end
//Main Project Target
target 'yourProjectName' do
shared_pods
end

//Widget Extension
target 'yourWidgetExtensionName' do
# If you need to collect widget Extension data
shared_pods
end
  • Use Static Library
use_modular_headers!
//Main Project Target
target 'yourProjectName' do
pod 'FTMobileSDK', '[latest_version]'
end
//Widget Extension
target 'yourWidgetExtensionName' do
pod 'FTMobileSDK/Extension', '[latest_version]'
end
  • Download the code repository for local use Podfile file
    use_modular_headers!
    //Main Project Target
    target 'yourProjectName' do
    pod 'FTMobileSDK', :path => '[folder_path]' 
    end
    //Widget Extension
    target 'yourWidgetExtensionName' do
    pod 'FTMobileSDK/Extension', :path => '[folder_path]'
    end
    

folder_path:Path to the folder where 'FTMobileSDK.podspec' is located.

FTMobileSDK.podspec file Modify s.version and s.source in the 'FTMobileSDK.podspec' file. s.version :This parameter is recommended to be consistent with SDK_VERSION in FTMobileSDK/FTMobileAgent/Core/FTMobileAgentVersion.h. s.source:tag => s.version

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 }

2.Run pod install in the Podfile directory to install the SDK.

1.Configure the Cartfile file.

github "GuanceCloud/datakit-ios" == [latest_version]

2.Executes in the 'Cartfile' directory

carthage update --platform iOS

If you get the error "Building universal frameworks with common architectures is not possible. for: arm64" error, Follow the prompts to add the --use-xcframeworks parameter.

carthage update --platform iOS --use-xcframeworks

The generated xcframework is used in the same way as the normal Framework. Add the compile-generated library to the project project.

FTMobileAgent:Add to the main project Target

FTMobileExtension:Add to Widget Extension Target

3.Select TARGETS -> Build Setting -> Other Linker Flags add -ObjC

4.support: FTMobileAgent:>=1.3.4-beta.2 FTMobileExtension: >=1.4.0-beta.1

1.Select PROJECT -> Package Dependency ,Click + under 'Packages'.

2.Enter https://github.com/GuanceCloud/datakit-ios.git in the search box on the page that pops up.

3.After Xcode successfully obtains the package, the SDK configuration page is displayed.

Dependency Rule :suggest you to choose Up to Next Major Version .

Add To Project :Select a supported project.

Click the 'Add Package' button and wait for the load to complete.

4.In the pop-up window 'Choose Package Products for datakit-ios', select the Target that needs to Add the SDK and click the' Add Package 'button. At this time, the SDK has been added successfully.

FTMobileSDK:Add to the main project Target

FTMobileExtension:Add to Widget Extension Target

If your project is managed by SPM, add the SDK as a dependency and add 'dependencies' to 'Package.swift'.

//  main project
dependencies: [
.package(name: "FTMobileSDK", url: "https://github.com/GuanceCloud/datakit-ios.git",.upToNextMajor(from: "[latest_version]"))
]

5.support:>= 1.4.0-beta.1 .

Add Header File

//CocoaPods、SPM 
#import "FTMobileSDK.h"
//Carthage 
#import <FTMobileSDK/FTMobileSDK.h>
import FTMobileSDK

SDK Initialization

Basic Configuration

-(BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
     // Use Datakit Address
     //FTMobileConfig *config = [[FTMobileConfig alloc]initWithDatakitUrl:datakitUrl];
     // Use DataWay Address
    FTMobileConfig *config = [[FTMobileConfig alloc]initWithDatawayUrl:datawayUrl clientToken:clientToken];
    config.enableSDKDebugLog = YES;
    //Start SDK
    [FTMobileAgent startWithConfigOptions:config];

   //...
    return YES;
}
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
       // Use Datakit Address
       //let config = FTMobileConfig(datakitUrl: url)
       // Use DataWay Address
     let config = FTMobileConfig(datawayUrl: datawayUrl, clientToken: clientToken)
     config.enableSDKDebugLog = true
     FTMobileAgent.start(withConfigOptions: config)
     //...
     return true
}
Fields Type Required Meaning Attention
datakitUrl NSString Yes Datakit Address The url of the Datakit address, example: http://10.0.0.1:9529, port 9529. Datakit url address needs to be accessible by the device where the SDK is installed.
datawayUrl NSString Yes Dataway Address The url of the Dataway address,example:http://10.0.0.1:9528,port 9528,Note: The installed SDK device must be able to access this address. Note: choose either DataKit or DataWay configuration, not both.
clientToken NSString Yes Authentication token It needs to be configured simultaneously with the datawayUrl
enableSDKDebugLog BOOL No Whether to turn on debug mode Default is NO, enable to print SDK run log
env NSString No Set the acquisition environment Default prod, support for custom. It can also be set using the -setEnvWithType: method based on the 'FTEnv' enumeration.
FTEnv
FTEnvProd: prod
FTEnvGray: gray
FTEnvPre :pre
FTEnvCommon :common
FTEnvLocal: local
service NSString No Set Service Name Impact the service field data in Log and RUM, which is set to df_rum_ios by default.
globalContext NSDictionary No Add SDK global properties Adding rules can be found here
groupIdentifiers NSArray No An array of AppGroups identifiers corresponding to the Widget Extensions to be collected If Widget Extensions data collection is enabled, You must set App Groups, And configure the Identifier to this property

RUM Configuration

    //开启 rum
    FTRumConfig *rumConfig = [[FTRumConfig alloc]initWithAppid:appid];
    rumConfig.samplerate = 80;
    rumConfig.enableTrackAppCrash = YES;
    rumConfig.enableTrackAppANR = YES;
    rumConfig.enableTrackAppFreeze = YES;
    rumConfig.enableTraceUserAction = YES;
    rumConfig.enableTraceUserView = YES;
    rumConfig.enableTraceUserResource = YES;
    rumConfig.errorMonitorType = FTErrorMonitorAll;
    rumConfig.deviceMetricsMonitorType = FTDeviceMetricsMonitorAll;
    rumConfig.monitorFrequency = FTMonitorFrequencyRare;
    [[FTMobileAgent sharedInstance] startRumWithConfigOptions:rumConfig];
    let rumConfig = FTRumConfig(appid: appid)
    rumConfig.enableTraceUserAction = true
    rumConfig.enableTrackAppANR = true
    rumConfig.enableTraceUserView = true
    rumConfig.enableTraceUserResource = true
    rumConfig.enableTrackAppCrash = true
    rumConfig.enableTrackAppFreeze = true
    rumConfig.errorMonitorType = .all
    rumConfig.deviceMetricsMonitorType = .all
    rumConfig.monitorFrequency = .rare
    FTMobileAgent.sharedInstance().startRum(withConfigOptions: rumConfig)
Fields Type Required Meaning Attention
appid NSString No Set Rum AppId Corresponding to setting RUM appid to enable RUM collection, get appid method
samplerate int No Set acquisition rate The collection rate ranges from >= 0 to <= 100. The default value is 100
enableTrackAppCrash BOOL No Set whether crash need to be collected Default NO
enableTrackAppANR BOOL No Collect ANR stuck unresponsive events Default NO
enableTrackAppFreeze BOOL No Collect UI jamming events Default NO
enableTraceUserView BOOL No Set whether to track user View actions Default NO
enableTraceUserAction BOOL No Set whether to track user Action actions Default NO
enableTraceUserResource BOOL No Set whether to track user network requests Default NO
resourceUrlHandler FTResourceUrlHandler No Configure Reousrce filter No filtering by default. Return :NO for no filtering and YES for filtering.
errorMonitorType FTErrorMonitorType No Error Event Monitoring Supplementary Type Add monitoring information to the collected crash data.
FTErrorMonitorType
FTErrorMonitorAll:all
FTErrorMonitorBattery:battery power
FTErrorMonitorMemory:total memory, memory usage
FTErrorMonitorCpu:CPU usage
deviceMetricsMonitorType FTDeviceMetricsMonitorType No The performance monitoring type of the view Add the monitoring item information to the collected View data。
FTDeviceMetricsMonitorType
FTDeviceMetricsMonitorAll:all
FTDeviceMetricsMonitorMemory:average memory, maximum memory
FTDeviceMetricsMonitorCpu:The maximum and average number of CPU ticks
FTDeviceMetricsMonitorFps:fps minimum frame rate, average frame rate
monitorFrequency FTMonitorFrequency No View's Performance Monitoring Sampling Period Configure 'monitorFrequency' to set the sampling period for View monitor information.
FTMonitorFrequency
FTMonitorFrequencyDefault:500ms (default)
FTMonitorFrequencyFrequent:100ms
FTMonitorFrequencyRare:1000ms
globalContext NSDictionary No Add Rum global properties Adding rules can be found here

Log Configuration

    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)
Fields Type Required Meaning Attention
samplerate int No Set acquisition rate The collection rate ranges from >= 0 to <= 100. The default value is 100
enableCustomLog BOOL No Whether to upload custom logs Default NO
printCustomLogToConsole BOOL No Sets whether to output custom logs to the console Default NO
Custom log print format
logLevelFilter NSArray No Set the state array of the custom logs to be collected Default full collection
enableLinkRumData BOOL No Whether to associate logger data with rum Default NO
discardType FTLogCacheDiscard No (the latest data is discarded by default) Setting the log deprecation policy Default FTDiscard
FTLogCacheDiscard:
FTDiscard:Default,When the number of log data exceeds the maximum value (5000), the appended data is discarded
FTDiscardOldest:When the log data exceeds the maximum value, the old data is discarded
globalContext NSDictionary No Add log global properties Adding rules can be found here

Trace Configuration

   //开启 trace
   FTTraceConfig *traceConfig = [[FTTraceConfig alloc]init];
   traceConfig.enableLinkRumData = YES;
     traceConfig.enableAutoTrace = YES;
   traceConfig.networkTraceType = FTNetworkTraceTypeDDtrace;
   [[FTMobileAgent sharedInstance] startTraceWithConfigOptions:traceConfig];
   let traceConfig = FTTraceConfig.init()
   traceConfig.enableLinkRumData = true
   traceConfig.enableAutoTrace = true
   FTMobileAgent.sharedInstance().startTrace(withConfigOptions: traceConfig)
Fields Type Required Meaning Attention
samplerate int No Set acquisition rate The collection rate ranges from >= 0 to <= 100. The default value is 100
networkTraceType NS_ENUM No Set the type of tracing Default is DDTrace, currently support Zipkin, Jaeger, DDTrace, Skywalking (8.0+), TraceParent (W3C), if you access OpenTelemetry to choose the corresponding trace type, please pay attention to check the supported types and agent-related configuration
enableAutoTrace BOOL No Set whether to enable automatic http trace Default NO,currently only NSURLSession is supported
enableLinkRumData BOOL No Whether to associate Trace data with rum Default NO

RUM

You can configure FTRUMConfig to enable automatic mode or add it manually. Rum related data can be passed in through the FTExternalDataManager singleton with the following API.

View

Method

/// Create View
///
/// Called before the '-startViewWithName' method, which is used to record the loading time of the page. If the loading time cannot be obtained, this method may not be called.
/// - Parameters:
///  - viewName: Current View Name
///  - loadTime: The loading time of this view(ns)
-(void)onCreateView:(NSString *)viewName loadTime:(NSNumber *)loadTime;

/// view start
///
/// - Parameters:
///  - viewName: Current View Name
-(void)startViewWithName:(NSString *)viewName;

/// view start
/// - Parameters:
///  - viewName: Current View Name
///  - property: Extra Property (optional)
-(void)startViewWithName:(NSString *)viewName property:(nullable NSDictionary *)property;

/// view stop
-(void)stopView;

/// view stop
/// - Parameter property: Extra Property (optional)
-(void)stopViewWithProperty:(nullable NSDictionary *)property;
/// Create View
///
/// Called before the '-startViewWithName' method, which is used to record the loading time of the page. If the loading time cannot be obtained, this method may not be called.
/// - Parameters:
///  - viewName: Current View Name
///  - loadTime: The loading time of this view (ns)
open func onCreateView(_ viewName: String, loadTime: NSNumber)

/// view start
///
/// - Parameters:
///  - viewName: Current View Name
open func startView(withName viewName: String)

/// view start
/// - Parameters:
///  - viewName: Current View Name
///  - property: Extra Property (optional)
open func startView(withName viewName: String, property: [AnyHashable : Any]?)

/// view stop
open func stopView() 

/// view stop
/// - Parameter property: Extra Property (optional)
open func stopView(withProperty property: [AnyHashable : Any]?)

Code Example

- (void)viewDidAppear:(BOOL)animated{
  [super viewDidAppear:animated];
  // Scene 1:
  [[FTExternalDataManager sharedManager] startViewWithName:@"TestVC"];  

  // Secne 2:  extra property
  [[FTExternalDataManager sharedManager] startViewWithName:@"TestVC" property:@{@"custom_key":@"custom_value"}];  
}
-(void)viewDidDisappear:(BOOL)animated{
  [super viewDidDisappear:animated];
  // Scene 1:
  [[FTExternalDataManager sharedManager] stopView];  

  // Secne 2:  extra property
  [[FTExternalDataManager sharedManager] stopViewWithProperty:@{@"custom_key":@"custom_value"}];
}
override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)
    // Scene 1:
    FTExternalDataManager.shared().startView(withName: "TestVC")
    // Secne 2:  extra property
    FTExternalDataManager.shared().startView(withName: "TestVC",property: ["custom_key":"custom_value"])
}
override func viewDidDisappear(_ animated: Bool) {
    super.viewDidDisappear(animated)
    // Scene 1:
    FTExternalDataManager.shared().stopView()
    // Secne 2:  extra property
    FTExternalDataManager.shared().stopView(withProperty: ["custom_key":"custom_value"])
}

Action

Method

/// add action
///
/// - Parameters:
///   - actionName: action name
- (void)addClickActionWithName:(NSString *)actionName;

/// add action
/// - Parameters:
///   - actionName: action name
///   - property: extra property (optional)
- (void)addClickActionWithName:(NSString *)actionName property:(nullable NSDictionary *)property;

/// add action
///
/// - Parameters:
///   - actionName: action name
///   - actionType: action type
- (void)addActionName:(NSString *)actionName actionType:(NSString *)actionType;

/// add action
/// - Parameters:
///   - actionName: action name
///   - actionType: action type
///   - property: extra property (optional)
- (void)addActionName:(NSString *)actionName actionType:(NSString *)actionType property:(nullable NSDictionary *)property;
/// add action
///
/// - Parameters:
///   - actionName: action name
func addClickAction(withName: String)

/// add action
/// - Parameters:
///   - actionName: action name
///   - property: extra property (optional)
func addClickAction(withName: String, property: [AnyHashable : Any]?)

/// add action
///
/// - Parameters:
///   - actionName: action name
///   - actionType: action type
func addActionName(String, actionType: String)

/// add action
/// - Parameters:
///   - actionName: action name
///   - actionType: action type
///   - property: extra property (optional)
func addActionName(String, actionType: String, property: [AnyHashable : Any]?)

Code Example

// Secne 1:  
[[FTExternalDataManager sharedManager] addActionName:@"UITableViewCell click" actionType:@"click"];
// Secne 2:  extra property
[[FTExternalDataManager sharedManager]  addActionName:@"UITableViewCell click" actionType:@"click" property:@{@"custom_key":@"custom_value"}];
// Secne 1:  
FTExternalDataManager.shared().addActionName("custom_action", actionType: "click")
// Secne 2:  extra property
FTExternalDataManager.shared().addActionName("custom_action", actionType: "click",property: ["custom_key":"custom_value"])

Error

Method

/// add error data
///
/// - Parameters:
///   - type: error type
///   - message: error message detail
///   - stack: error log content
- (void)addErrorWithType:(NSString *)type message:(NSString *)message stack:(NSString *)stack;

/// add error data
/// - Parameters:
///   - type: error type
///   - message: error message detail
///   - stack: error log content
///   - property: extra property (optional)
- (void)addErrorWithType:(NSString *)type message:(NSString *)message stack:(NSString *)stack property:(nullable NSDictionary *)property;

/// add error data
/// - Parameters:
///   - type: error type
///   - state: application running state
///   - message: error message detail
///   - stack: error log content
///   - property: extra property (optional)
- (void)addErrorWithType:(NSString *)type state:(FTAppState)state  message:(NSString *)message stack:(NSString *)stack property:(nullable NSDictionary *)property;
/// add error data
///
/// - Parameters:
///   - type: error type
///   - message: error message detail
///   - stack: error log content
func addError(withType: String, message: String, stack: String)

/// add error data
/// - Parameters:
///   - type: error type
///   - message: error message detail
///   - stack: error log content
///   - property: extra property (optional)
func addError(withType: String, message: String, stack: String, property: [AnyHashable : Any]?)

/// add error data
/// - Parameters:
///   - type: error type
///   - state: application running state
///   - message: error message detail
///   - stack: error log content
///   - property: extra property (optional)
open func addError(withType type: String, state: FTAppState, message: String, stack: String, property: [AnyHashable : Any]?)

Code Example

// Secne 1: 
[[FTExternalDataManager sharedManager] addErrorWithType:@"type" message:@"message" stack:@"stack"];
// Secne 2:  extra property
[[FTExternalDataManager sharedManager] addErrorWithType:@"ios_crash" message:@"crash_message" stack:@"crash_stack" property:@{@"custom_key":@"custom_value"}];
// Secne 3:  extra property
[[FTExternalDataManager sharedManager] addErrorWithType:@"ios_crash" state:FTAppStateUnknown message:@"crash_message" stack:@"crash_stack" property:@{@"custom_key":@"custom_value"}];
// Secne 1: 
FTExternalDataManager.shared().addError(withType: "custom_type", message: "custom_message", stack: "custom_stack")
// Secne 2:  extra property
FTExternalDataManager.shared().addError(withType: "custom_type", message: "custom_message", stack: "custom_stack",property: ["custom_key":"custom_value"])
// Secne 3:  extra property     
FTExternalDataManager.shared().addError(withType: "custom_type", state: .unknown, message: "custom_message", stack: "custom_stack", property: ["custom_key":"custom_value"])

LongTask

Method

/// add long task data
///
/// - Parameters:
///   - stack: stack or log content
///   - duration: Duration, in nanoseconds.
- (void)addLongTaskWithStack:(NSString *)stack duration:(NSNumber *)duration;

/// add long task data
/// - Parameters:
///   - stack: stack or log content
///   - duration: Duration, in nanoseconds.
///   - property: extra property (optional)
- (void)addLongTaskWithStack:(NSString *)stack duration:(NSNumber *)duration property:(nullable NSDictionary *)property;
/// add long task data
///
/// - Parameters:
///   - stack: stack or log content
///   - duration: Duration, in nanoseconds.
func addLongTask(withStack: String, duration: NSNumber)

/// add long task data
/// - Parameters:
///   - stack: stack or log content
///   - duration: Duration, in nanoseconds.
///   - property: extra property (optional)
func addLongTask(withStack: String, duration: NSNumber, property: [AnyHashable : Any]?)

Code Example

// Secne 1: 
[[FTExternalDataManager sharedManager] addLongTaskWithStack:@"stack string" duration:@1000000000];
// Secne 2:  extra property
[[FTExternalDataManager sharedManager] addLongTaskWithStack:@"stack string" duration:@1000000000 property:@{@"custom_key":@"custom_value"}];
// Secne 1: 
FTExternalDataManager.shared().addLongTask(withStack: "stack string", duration: 1000000000)
// Secne 2:  extra property
FTExternalDataManager.shared().addLongTask(withStack: "stack string", duration: 1000000000 ,property: [["custom_key":"custom_value"]])

Resource

Method

/// resource start
///
/// - Parameters:
///   - key: resource Id ,unique every request
- (void)startResourceWithKey:(NSString *)key;

/// resource start
/// - Parameters:
///   - key: resource Id ,unique every request
///   - property: extra property
- (void)startResourceWithKey:(NSString *)key property:(nullable NSDictionary *)property;

/// resource stop
///
/// - Parameters:
///   - key: resource Id ,unique every request
- (void)stopResourceWithKey:(NSString *)key;

/// resource stop
/// - Parameters:
///   - key: resource Id ,unique every request
///   - property: extra property
- (void)stopResourceWithKey:(NSString *)key property:(nullable NSDictionary *)property;

/// append network metrics and content data
///
/// - Parameters:
///   - key: resource Id ,unique every request
///   - metrics: request performance attributes
///   - content: request data
- (void)addResourceWithKey:(NSString *)key metrics:(nullable FTResourceMetricsModel *)metrics content:(FTResourceContentModel *)content;
/// resource start
///
/// - Parameters:
///   - key: resource Id ,unique every request
open func startResource(withKey key: String)

/// resource start
/// - Parameters:
///   - key: resource Id ,unique every request
///   - property: extra property
open func startResource(withKey key: String, property: [AnyHashable : Any]?)

/// resource stop
///
/// - Parameters:
///   - key: resource Id ,unique every request
open func stopResource(withKey key: String)

/// resource stop
/// - Parameters:
///   - key: resource Id ,unique every request
///   - property: extra property
open func stopResource(withKey key: String, property: [AnyHashable : Any]?)

/// append network metrics and content data
///
/// - Parameters:
///   - key: resource Id ,unique every request
///   - metrics: request performance attributes
///   - content: request data
open func addResource(withKey key: String, metrics: FTResourceMetricsModel?, content: FTResourceContentModel)

Code Example

//step 1: Before the network request starts
[[FTExternalDataManager sharedManager] startResourceWithKey:key];

//step 2:Request completed
[[FTExternalDataManager sharedManager] stopResourceWithKey:key];

//step 3:① Add resource data
//FTResourceContentModel 
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 the performance data of the network request can be obtained
//FTResourceMetricsModel
//ios native. Get NSURLSessionTaskMetrics data, directly use the initialization method of FTResourceMetricsModel
FTResourceMetricsModel *metricsModel = [[FTResourceMetricsModel alloc]initWithTaskMetrics:metrics];

//other platforms. All time data in nanoseconds
FTResourceMetricsModel *metricsModel = [[FTResourceMetricsModel alloc]init];

// step 4:add resource: If there is no performance data, the metrics parameter is set to nil
[[FTExternalDataManager sharedManager] addResourceWithKey:key metrics:metricsModel content:content];
//step 1: Before the network request starts
FTExternalDataManager.shared().startResource(withKey: key)

//step 2:Request completed
FTExternalDataManager.shared().stopResource(withKey: resource.key)

//step 3:① Add resource data
let contentModel = FTResourceContentModel(request: task.currentRequest!, response: task.response as? HTTPURLResponse, data: resource.data, error: error)

//② If the performance data of the network request can be obtained
//FTResourceMetricsModel
//ios native. Get NSURLSessionTaskMetrics data, directly use the initialization method of FTResourceMetricsModel
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 there is no performance data, the metrics parameter is set to nil
FTExternalDataManager.shared().addResource(withKey: resource.key, metrics: metricsModel, content: contentModel)

Logging

Method

//  FTMobileAgent.h
//  FTMobileSDK

/// add log
/// @param content Log content, which can be a json string
/// @param status  Log Level (info、warning、error、critical、ok). 
-(void)logging:(NSString *)content status:(FTStatus)status;

/// add log
/// @param content Log content, which can be a json string
/// @param status  Log Level (info、warning、error、critical、ok). 
/// @param property Extra Property (optional)
-(void)logging:(NSString *)content status:(FTLogStatus)status property:(nullable NSDictionary *)property;
//
//  FTLogger.h
//  FTMobileSDK

/// add info type log
/// - Parameters:
///   - content: Log content, which can be a json string
///   - property: Extra Property (optional)
-(void)info:(NSString *)content property:(nullable NSDictionary *)property;

/// add warning type log
/// - Parameters:
///   - content: Log content, which can be a json string
///   - property: Extra Property (optional)
-(void)warning:(NSString *)content property:(nullable NSDictionary *)property;

/// add error type log
/// - Parameters:
///   - content: Log content, which can be a json string
///   - property: Extra Property (optional)
-(void)error:(NSString *)content  property:(nullable NSDictionary *)property;

/// add critical type log
/// - Parameters:
///   - content: Log content, which can be a json string
///   - property: Extra Property (optional)
-(void)critical:(NSString *)content property:(nullable NSDictionary *)property;

/// add ok type log
/// - Parameters:
///   - content: Log content, which can be a json string
///   - property: Extra Property (optional)
-(void)ok:(NSString *)content property:(nullable NSDictionary *)property;
open class FTMobileAgent : NSObject {
/// add log
///
/// - Parameters:
///   - content: Log content, can be a json string
///   - status: Log Level (info、warning、error、critical、ok). 
open func logging(_ content: String, status: FTLogStatus)

/// add log
/// - Parameters:
///   - content: Log content, can be a json string
///   - status: Log Level (info、warning、error、critical、ok). 
///   - property: Extra Property (optional)
open func logging(_ content: String, status: FTLogStatus, property: [AnyHashable : Any]?)
}
open class FTLogger : NSObject, FTLoggerProtocol {}
public protocol FTLoggerProtocol : NSObjectProtocol {
/// add info type log
/// - Parameters:
///   - content: Log content, can be a json string
///   - property: Extra Property (optional)
optional func info(_ content: String, property: [AnyHashable : Any]?)

/// add warning type log
/// - Parameters:
///   - content: Log content, can be a json string
///   - property: Extra Property (optional)
optional func warning(_ content: String, property: [AnyHashable : Any]?)

/// add error type log
/// - Parameters:
///   - content:  Log content, can be a json string
///   - property: Extra Property (optional)
optional func error(_ content: String, property: [AnyHashable : Any]?)

/// add critical type log
/// - Parameters:
///   - content:  Log content, can be a json string
///   - property: Extra Property (optional)
optional func critical(_ content: String, property: [AnyHashable : Any]?)

/// add ok type log
/// - Parameters:
///   - content: Log content, can be a json string
///   - property: Extra Property (optional)
optional func ok(_ content: String, property: [AnyHashable : Any]?)
}

Log level

/// log level
typedef NS_ENUM(NSInteger, FTLogStatus) {
    /// info
    FTStatusInfo         = 0,
    /// warning
    FTStatusWarning,
    /// error
    FTStatusError,
    /// critical
    FTStatusCritical,
    /// ok
    FTStatusOk,
};
/// log level
public enum FTLogStatus : Int, @unchecked Sendable {
    /// info
    case statusInfo = 0
    /// warning
    case statusWarning = 1
    /// error
    case statusError = 2
    /// critical
    case statusCritical = 3
    /// ok
    case statusOk = 4
}

Code Example

// Method 1:Use FTMobileAgent
// Note: Ensure that the SDK has been successfully initialized at the time of use, otherwise failure will be asserted in the test environment resulting in a crash.
[[FTMobileAgent sharedInstance] logging:@"test_custom" status:FTStatusInfo];

// Method 2:Use FTLogger (recommend)
// If the SDK is not initialized successfully, calling the methods in FTLogger to add custom logs will fail, but there will be no assertion failure crash.
[[FTLogger sharedInstance] info:@"test" property:@{@"custom_key":@"custom_value"}];
// Method 1:Use FTMobileAgent
// Note: Ensure that the SDK has been successfully initialized at the time of use, otherwise failure will be asserted in the test environment resulting in a crash.
FTMobileAgent.sharedInstance().logging("contentStr", status: .statusInfo, property:["custom_key":"custom_value"])

// Method 2:Use FTLogger (recommend)
// If the SDK is not initialized successfully, calling the methods in FTLogger to add custom logs will fail, but there will be no assertion failure crash.
FTLogger.shared().info("contentStr", property: ["custom_key":"custom_value"])

Print Custom Log To Console

Set 'printCustomLogToConsole = YES' to enable the output of 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] [IOS APP] [INFO] content ,{K=V,...,Kn=Vn}

2023-06-29 13:47:56.960021+0800 App[64731:44595791]:os_log Specifies the standard prefix of log output (< xcode 15);

[IOS APP]:The prefix is used to distinguish the custom log output by the SDK;

[INFO]:Customize the log level;

content:Customize log content;

{K=V,...,Kn=Vn}:Extra Property 。

You can FTTraceConfig configuration to turn on automatic mode, or manually add. Trace related data, through the FTTraceManager singleton, to pass in, the relevant API as follows.

NSString *key = [[NSUUID UUID]UUIDString];
NSURL *url = [NSURL URLWithString:@"http://www.baidu.com"];
//manual operation required: Get trace header 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: Get trace header 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 tracing Network by forwarding URLSession Delegate

Note: This method does not work with Swift URLSession async/await APIs

SDK provides a class FTURLSessionDelegate that requires you to forward the URLSession delegate to FTURLSessionDelegate to help the SDK collect data about the Network.

The related data of collecting Network is divided into RUM-Resource and Network link Tracing in SDK.

RUM-Resource

  • enableTraceUserResource of FTRUMConfig can be enabled. The auto-tracking logic ignores the current URLSession'request;
  • Support for adding custom properties.

Network link Tracing

  • enableAutoTrace of FTTraceConfig can be enabled. When setting up custom link tracking, the auto-tracking logic will ignore the current URLSession request.

Here are three usage examples to suit different user scenarios:

Method 1

Set the URLSession delegate to an instance of FTURLSessionDelegate.

id<NSURLSessionDelegate> delegate = [[FTURLSessionDelegate alloc]init];
// To add custom RUM resource properties, it is recommended that the tag name be prefixed with the project abbreviation, such as' 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};
            };
// Provide a block to modify a URL request.Can be used for custom link tracing.
delegate.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;
        };            
NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration] delegate:delegate delegateQueue:nil];
let delegate = FTURLSessionDelegate.init()
// To add custom RUM resource properties, it is recommended that the tag name be prefixed with the project abbreviation, such as' 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
        }
// Provide a block to modify a URL request.Can be used for custom link tracing.
delegate.requestInterceptor = { request in
            var mutableRequest = request
            if let traceHeader = FTExternalDataManager.shared().getTraceHeader(with: request.url!){
                for (key,value) in traceHeader {
                    mutableRequest.setValue(value as? String, forHTTPHeaderField: key as! String)
                }
            }
            return mutableRequest
        }        
let session =  URLSession.init(configuration: URLSessionConfiguration.default, delegate:delegate 
, delegateQueue: nil)

Method 2

Set the URLSession delegate to be a subclass of FTURLSessionDelegate.

@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];
        // To add custom RUM resource properties, it is recommended that the tag name be prefixed with the project abbreviation, such as' 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};
    };
        // Provide a block to modify a URL request.Can be used for custom link tracing.
       self.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;
        }; 
    }
    return self;
}
-(void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didFinishCollectingMetrics:(NSURLSessionTaskMetrics *)metrics{
    // must call super
    [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()
        // To add custom RUM resource properties, it is recommended that the tag name be prefixed with the project abbreviation, such as' 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
        }
        //Provide a block to modify a URL request .Can be used for custom link tracing.
        requestInterceptor = { request in
            var mutableRequest = request
            if let traceHeader = FTExternalDataManager.shared().getTraceHeader(with: request.url!){
                for (key,value) in traceHeader {
                    mutableRequest.setValue(value as? String, forHTTPHeaderField: key as! String)
                }
            }
            return mutableRequest
        }
    }
    }

    override func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) {
        // must call super
        super.urlSession(session, task: task, didFinishCollecting: metrics)
        // Your own logic
        // ......
    }
}

Method 3

Make URLSession delegate object implementing FTURLSessionDelegateProviding agreement, Declare a FTftURLSessionDelegate property (readwrite) called ftURLSessionDelegate and implement the get method for ftURLSessionDelegate.

The class implementing FTURLSessionDelegateProviding must ensure that following method calls are forwarded to ftURLSessionDelegate:

-URLSession:dataTask:didReceiveData:

-URLSession:task:didCompleteWithError:

-URLSession:task:didFinishCollectingMetrics:

@interface InstrumentationPropertyClass:NSObject<NSURLSessionDataDelegate,FTURLSessionDelegateProviding>
@property (nonatomic, strong) FTURLSessionDelegate *ftURLSessionDelegate;
@end
@implementation InstrumentationPropertyClass

- (nonnull FTURLSessionDelegate *)ftURLSessionDelegate {
    if(!_ftURLSessionDelegate){
        _ftURLSessionDelegate = [[FTURLSessionDelegate alloc]init];
        //To add custom RUM resource properties, it is recommended that the tag name be prefixed with the project abbreviation, such as' df_tag_name '.
        _ftURLSessionDelegate.provider =  ^NSDictionary * _Nullable(NSURLRequest *request, NSURLResponse *response, NSData *data, NSError *error) {
                NSString *body = [[NSString alloc] initWithData:request.HTTPBody encoding:NSUTF8StringEncoding];
                return @{@"df_requestbody":body};
            };
            // Tells the `ftURLSessionDelegate` to modify a URL request.Can be used for custom link tracing.
        _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;
        }; 
    }
    return _ftURLSessionDelegate;
}
// The following methods must be implemented
- (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)
        // To add custom RUM resource properties, it is recommended that the tag name be prefixed with the project abbreviation, such as' 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
        }
        // Tells the `ftURLSessionDelegate` to modify a URL request.Can be used for custom link tracing.
        ftURLSessionDelegate.requestInterceptor = { request in
            var mutableRequest = request
            if let traceHeader = FTExternalDataManager.shared().getTraceHeader(with: request.url!){
                for (key,value) in traceHeader {
                    mutableRequest.setValue(value as? String, forHTTPHeaderField: key as! String)
                }
            }
            return mutableRequest
        }
    }
    // 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 Information Binding and Unbinding

Method

/// Bind user information
///
/// - Parameters:
///   - Id:  user id
- (void)bindUserWithUserID:(NSString *)userId;

/// Bind user information
///
/// - Parameters:
///   - Id:  user id
///   - userName: user name
///   - userEmailL: user email
- (void)bindUserWithUserID:(NSString *)Id userName:(nullable NSString *)userName userEmail:(nullable NSString *)userEmail;

/// Bind user information
///
/// - Parameters:
///   - Id:  user id
///   - userName: user name
///   - userEmail: user email
///   - extra: extar infomation
- (void)bindUserWithUserID:(NSString *)Id userName:(nullable NSString *)userName userEmail:(nullable NSString *)userEmail extra:(nullable NSDictionary *)extra;

/// Unbind user information
- (void)unbindUser;
/// Bind user information
///
/// - Parameters:
///   - Id:  user id
open func bindUser(withUserID userId: String)

/// Bind user information
///
/// - Parameters:
///   - Id:  user id
///   - userName: user name
///   - userEmailL: user email
open func bindUser(withUserID Id: String, userName: String?, userEmail: String?)

/// Bind user information
///
/// - Parameters:
///   - Id:  user id
///   - userName: user name
///   - userEmail: user email
///   - extra: extar infomation
open func bindUser(withUserID Id: String, userName: String?, userEmail: String?, extra: [AnyHashable : Any]?)

/// Unbind user information
open func unbindUser()

Code Example

// bind user info after log in
[[FTMobileAgent sharedInstance] bindUserWithUserID:USERID];
// or
[[FTMobileAgent sharedInstance] bindUserWithUserID:USERID userName:USERNAME userEmail:USEREMAIL];
// or
[[FTMobileAgent sharedInstance] bindUserWithUserID:USERID userName:USERNAME userEmail:USEREMAIL extra:@{EXTRA_KEY:EXTRA_VALUE}];

// clear user data after log out
[[FTMobileAgent sharedInstance] unbindUser];
// bind user info after log in
FTMobileAgent.sharedInstance().bindUser(withUserID: USERID)
// or
FTMobileAgent.sharedInstance().bindUser(withUserID: USERID, userName: USERNAME, userEmail: USEREMAIL)
// or
FTMobileAgent.sharedInstance().bindUser(withUserID: USERID, userName: USERNAME, userEmail: USEREMAIL,extra:[EXTRA_KEY:EXTRA_VALUE])

// clear user data after log out
FTMobileAgent.sharedInstance().unbindUser()

Close SDK

Using FTMobileAgent to close SDK

Method

/// Close the running object inside the SDK
- (void)shutDown;
/// Close the running object inside the SDK
func shutDown()

Code Example

//If you dynamically change the SDK configuration, you need to close it first to avoid the generation of wrong data
[[FTMobileAgent sharedInstance] shutDown];
//If you dynamically change the SDK configuration, you need to close it first to avoid the generation of wrong data
FTMobileAgent.sharedInstance().shutDown()

Add Custom Tags

Static Use

You can create multiple Configurations to set values using pre-compiled instructions

  1. Create multiple configurations.

  1. Set preset properties to distinguish between Configurations.

  1. Use the pre-compile command.
//Target -> Build Settings -> GCC_PREPROCESSOR_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 set
[[FTMobileAgent sharedInstance] startRumWithConfigOptions:rumConfig];

Dynamic Use

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 is started.

  1. Save it locally by saving a file, e.g. NSUserDefaults, configure it using SDK and add the code to get the tag data in the configuration.
NSString *dynamicTag = [[NSUserDefaults standardUserDefaults] valueForKey:@"DYNAMIC_TAG"]?:@"NO_VALUE";

FTRumConfig *rumConfig = [[FTRumConfig alloc]init];
rumConfig.globalContext = @{@"dynamic_tag":dynamicTag};
... //other set
[[FTMobileAgent sharedInstance] startRumWithConfigOptions:rumConfig];
  1. Add a way to change the file data anywhere.
 [[NSUserDefaults standardUserDefaults] setValue:@"dynamic_tags" forKey:@"DYNAMIC_TAG"];
  1. Finally restart the application to take effect.

Attention

  1. Special key : track_id (configured in RUM for tracking function)

  2. When the user adds a custom tag through globalContext and the SDK has the same tag, the SDK tag will override the user's settings, it is recommended that the tag name add the prefix of the project abbreviation, such as df_tag_name.

  3. Set the globalContext before calling the -startRumWithConfigOptions method to start RUM to take effect.

  4. Custom tags configured in FTMobileConfig will be added to all types of data.

For more details, please see SDK Demo.

Crash Log Symbolization

Upload Symbol Table

Method 1: Script integration into the Xcode project's Target

  1. XCode add custom Run Script Phase:Build Phases -> + -> New Run Script Phase
  2. Copy the script into the build-phase run script of the Xcode project, where you need to set parameters such as < app_id >, < datakit_address >, < env >, ,< version > (the default configured version format of the script is CFBundleShortVersionString).
  3. Script
#Parameters that need to be configured
#<app_id>
FT_APP_ID="YOUR_APP_ID"
# <datakit_address>
FT_DATAKIT_ADDRESS="YOUR_DATAKIT_ADDRESS"
# <env> environment field. value:prod/gray/pre/common/local。Need to be consistent with SDK settings
FT_ENV="common"
# <dataway_token> The token for dataway in the datakit.conf configuration file
FT_TOKEN="YOUR_DATAWAY_TOKEN"
#
#<version> The version format of the script default configuration is: CFBundleShortVersionString, if you modify the default version format, please set this variable. Note: You need to make sure that what you fill in here is consistent with what you set in the SDK.
# FT_VERSION=""
Convenient Configuration Parameters for Multiple Environments

Example: Using preset macros and .xcconfig configuration files

  1. Add preset macros: Target —> Build Settings -> + -> Add User-Defined Setting

  1. Use multiple Xcconfig to implement multiple environments, new Xcconfig

Configure the preset macros in the .xcconfig file.

//If you use cocoapods, you need to add the .xcconfig path of pods to your .xcconfig file. If you don’t know what the path is, you can use the terminal to enter the project folder, execute pod install, the terminal will prompt the path, and set the path After copying, you can use it as follows.

#include "Pods/Target Support Files/Pods-testDemo/Pods-testDemo.debug.xcconfig"

SDK_APP_ID = app_id_common
SDK_ENV = common
SDK_DATAKIT_ADDRESS = http:\$()\xxxxxxxx:9529
SDK_DATAWAY_TOKEN = token
  1. Configuring a custom build environment

  1. Use

In the script

#Parameters that need to be configured
#<app_id>
FT_APP_ID=${SDK_APP_ID}
#<dea_address>
FT_DATAKIT_ADDRESS=${SDK_DATAKIT_ADDRESS}
# <env> environment field. value:prod/gray/pre/common/local。Need to be consistent with SDK settings
FT_ENV=${SDK_ENV}
# The token for dataway in the datakit.conf configuration file
FT_TOKEN=${SDK_DATAWAY_TOKEN}

In a document for a project

Mapping to the Info.plist file

In the file you can use

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

        print("SDK_APP_ID:\(appid)")
        print("SDK_ENV:\(env)")
}

For more details, please see SDK Demo.

Method 2: Terminal run script

Find the .dSYM file in a folder, enter the basic application information, the parent directory path of the .dSYM file, and the output file directory at the command line.

sh FTdSYMUpload.sh <datakit_address> <app_id> <version> <env> <dataway_token> <dSYMBOL_src_dir> <dSYMBOL_dest_dir>

Method 3: Manual upload

Sourcemap Upload

Widget Extension Data Collection

Widget Extension Suppot

  • Logger

  • Trace

  • RUM
  • Manual acquisition (RUM )
  • Automatically collects crash and HTTP Resource data

Note: Because HTTP Resource data is bound to the View, you need to manually collect the View data.

Widget Extension Configuration

Use 'FTExtensionConfig' to configure the automatic switch for Widget Extension data collection and the file sharing Group Identifier. Other configurations use the configurations already set in the main project SDK.

Fields Type Required description
groupIdentifier NSString Yes File sharing Group Identifier
enableSDKDebugLog BOOL No(Deafault NO) enable to print SDK run log
enableTrackAppCrash BOOL No(Deafault NO) Set whether crash need to be collected
enableRUMAutoTraceResource BOOL No(Deafault NO) Set whether to track user network requests
enableTracerAutoTrace BOOL No(Deafault NO) Set whether to enable automatic http trace
memoryMaxCount NSInteger No(Deafault 1000) The maximum number of data saved in the Widget Extension

example:

// In the widget extension
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")

When setting 'FTMobileConfig' in the main project, you must configure 'groupIdentifiers'.

// In the main project
 FTMobileConfig *config = [[FTMobileConfig alloc]initWithMetricsUrl:url];
 config.enableSDKDebugLog = YES;
 config.groupIdentifiers = @[@"group.com.ft.widget.demo"]; 
let config = FTMobileConfig.init(metricsUrl: url)
config.enableSDKDebugLog = true
config.groupIdentifiers = ["group.com.ft.widget.demo"]

Widget Extension SDK Collected Data Uploade

The Widget Extension SDK only implements data collection, and the data upload logic is delivered to the SDK of the main project. The timing of synchronization of the collected data to the main project is user-defined.

Method

//  In the main project
/// Track Widget Extension sdk cached data 
/// - Parameters:
///   - groupIdentifier: groupIdentifier
///   - completion: completion callback
- (void)trackEventFromExtensionWithGroupIdentifier:(NSString *)groupIdentifier completion:(nullable void (^)(NSString *groupIdentifier, NSArray *events)) completion;
/// Track Widget Extension sdk cached data 
/// - Parameters:
///   - groupIdentifier: groupIdentifier
///   - completion: completion callback
open func trackEventFromExtension(withGroupIdentifier groupIdentifier: String, completion: ((String, [Any]) -> Void)? = nil)

Code Example

// In the main project
-(void)applicationDidBecomeActive:(UIApplication *)application{
    [[FTMobileAgent sharedInstance] trackEventFromExtensionWithGroupIdentifier:@"group.identifier" completion:nil];
}
func applicationDidBecomeActive(_ application: UIApplication) {
FTMobileAgent.sharedInstance().trackEventFromExtension(withGroupIdentifier: "group.identifier" )     
}

Frequently Asked Questions

About Crash Log Analysis

In Debug and Release modes at development time, the thread tracebacks captured during Crash are symbolized. The release package does not come with a symbol table, and the key tracebacks of the exception threads will show the mirror names and will not be translated into valid code symbols. The relevant information in the crash log obtained are all hexadecimal memory addresses, which do not locate the crashed code, so the hexadecimal memory addresses need to be parsed into the corresponding classes and methods.

XCode does not generate a dSYM file after compilation?

XCode Release compilation generates dSYM files by default, while Debug compilation does not generate them by default.

Build Settings -> Code Generation -> Generate Debug Symbols -> Yes

Build Settings -> Build Option -> Debug Information Format -> DWARF with dSYM File

How can I upload a symbol table with bitCode turned on?

When you upload your bitcode App to the App Store, check the generation of the declaration symbol file (dSYM file) in the submit dialog.

  • Before configuring the symbol table file, you need to download the dSYM file corresponding to that version from the App Store back locally, and then use a script to process the uploaded symbol table file based on the input parameters.
  • There is no need to integrate the script into the Target of the Xcode project, and do not use the locally generated dSYM file to generate the symbol table file, as the symbol table information is hidden in the locally compiled dSYM file. If you upload a locally generated dSYM file, the result will be a symbol like "__hiden#XXX".

How do I retrieve the dSYM file corresponding to an App that has been published to the App Store?

Apply Distribution options uploaded to App Store Connect dSym File
Don’t include bitcode
Upload symbols
Retrieve via Xcode
Include bitcode
Upload symbols
Retrieve via iTunes Connect
To retrieve it via Xcode, you need to use .bcsymbolmap to obfuscate it.
Include bitcode
Don’t upload symbols
To retrieve it via Xcode, you need to use .bcsymbolmap to obfuscate it.
Don’t include bitcode
Don’t upload symbols
Retrieve via Xcode
Retrieve via Xcode
  1. Xcode -> Window -> Organizer

  2. Select the Archives tab

  1. Find the published archive package, right-click on the corresponding archive package, and select Show in Finder operation

  1. Right-click on the located archive file and select the Show Package Contents action

  1. Select the dSYMs directory, which contains the downloaded dSYM files

Retrieve via iTunes Connect
  1. Login to App Store Connect;
  2. Go to "My Apps"
  3. Select a version in the "App Store" or "TestFlight" and click on "Build Metadata" On this page, click on the button "Download dSYM" to download the dSYM file
.bcsymbolmap de-obfuscation

When you find the dSYM file via Xcode, you can see the BCSymbolMaps directory

Open a terminal and use the following command to de-obfuscate

xcrun dsymutil -symbol-map <BCSymbolMaps_path> <.dSYM_path>

Feedback

Is this page helpful? ×