The Blue Triangle SDK for iOS enables application owners to track their users’ experience so they can focus on user experience issues that impact their business outcomes.
- Performance & Network Timings
- Main Timers
- Network Timers
- Custom Timers
- Errors & Crashes
- Application Not Responding (ANR)
- HTTP Response Codes
- App Crashes
- Device Stats & Session Attributes
- OS/OS Version
- App Version
- Device Type
- Geographical/Country
- CPU Usage
- Memory Warnings
- Memory/Out of Memory
- Hot/Cold Launch Coming Soon
- Network Type
To integrate BlueTriangle using Swift Packages Manager into your iOS project, you need to follow these steps:
Go to File > Add Packages…, enter the package repository URL https://github.com/blue-triangle-tech/btt-swift-sdk.git
, and click Add Package.
Xcode 11 - 12: go to File > Swift Packages > Add Package Dependency… and enter the package repository URL https://github.com/blue-triangle-tech/btt-swift-sdk.git
, then follow the instructions.
To integrate BlueTriangle using CocoaPods into your iOS project, you need to follow these steps:
- Open 'Podfile' in text mode and add following:
pod 'BlueTriangleSDK-Swift'
- Save the Podfile and run the following command in the terminal to install the dependencies:
pod install
In order to use BlueTriangle
, you need to first configure BlueTriangle
SDK. To configure it import BlueTriangle
and call configure function with your siteID. It is recommended to do this in your AppDelegate.application(_:didFinishLaunchingWithOptions:)
OR SceneDelegate.scene(_ scene:, willConnectTo session:, options,connectionOptions:)
method:
BlueTriangle.configure { config in
config.siteID = "<MY_SITE_ID>"
}
If you are using SwiftUI, it is recommended to add an init() constructor in your App struct and add configuration code there as shown below.
import BlueTriangle
import SwiftUI
@main
struct YourApp: App {
init() {
//Configure BlueTriagle with your siteID
BlueTriangle.configure { config in
config.siteID = "<MY_SITE_ID>"
}
//...
}
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
Replace <BTT_SITE_ID>
with your site ID. You can find instructions on how to find your site ID here.
It is the application developers' responsibility to ensure privacy nutrition labels are according to usage of BlueTriangle SDK in your app. For instance if your app uses revenue tracking(Timers cartValue) its app developers responsibility to mention Purchase History in there apps privacy manifest data usage. For more detail see privacy manifest chapter
All UIKit UIViewControllers view counts will be tracked automatically. You can see each view controller name with their count on our dashboard.
SwiftUI views are not captured automatically. You need to call bttTrackScreen() modifier on each view which you want to track. Below example show usage of "bttTrackScreen(_ screenName: String)" to track About Us screen.
struct ContentView: View {
var body: some View {
VStack{
Text("Hello, world!")
}
.bttTrackScreen("Demo_Screen")
}
}
To disable screen tracking, you need to set the enableScreenTracking configuration to false during configuration like bellow, This will ignore UIViewControllers activities and bttTrackScreen() modifier calls.
BlueTriangle.configure { config in
...
config.enableScreenTracking = false
}
Websites shown in webview that are tracked by BlueTriangle can be tracked in the same session as the native app. To achieve this, follow the steps below to configure the WebView:
Implement WKNavigationDelegate protocol and call BTTWebViewTracker.webView(webView, didCommit: navigation) in 'webView(_:didCommit:)' delegate method as follows.
import BlueTriangle
//....
extension YourWebViewController: WKNavigationDelegate{
//....
func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {
//....
//Call BlueTringles 'webView(_:didCommit:)' method
BTTWebViewTracker.webView(webView, didCommit: navigation)
}
}
For more clarity, here is a Webview with UIViewController full example:
import UIKit
import WebKit
//Need to import BlueTriangle
import BlueTriangle
class YourWebViewController: UIViewController {
@IBOutlet weak var webView: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()
//Set navigationDelegate
webView.navigationDelegate = self
//Load Url
if let htmlURL = URL(string: "https://example.com"){
webView.load(URLRequest(url: htmlURL))
}
}
}
//Implement Navigation Delagate
extension YourWebViewController: WKNavigationDelegate {
func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {
//...
//Call BlueTringles 'webView(_:didCommit:)' method
BTTWebViewTracker.webView(webView, didCommit: navigation)
}
}
Webview with SwiftUI full example:
import SwiftUI
import WebKit
//Need to import BlueTriangle
import BlueTriangle
struct YourWebView: UIViewRepresentable {
private let webView = WKWebView()
func makeCoordinator() -> YourWebView.Coordinator {
Coordinator()
}
func makeUIView(context: Context) -> some UIView {
//Set navigationDelegate
webView.navigationDelegate = context.coordinator
//Load Url
if let htmlURL = URL(string: "https://example.com"){
webView.load(URLRequest(url: htmlURL))
}
return webView
}
}
extension YourWebView {
//Implement Navigation Delegate Coordinator
class Coordinator: NSObject, WKNavigationDelegate {
func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {
//...
//Call BlueTringles 'webView(_:didCommit:)' method
BTTWebViewTracker.webView(webView, didCommit: navigation)
}
}
}
The Blue Triangle SDK supports capturing network requests using either the bt-prefixed URLSession methods or the NetworkCaptureSessionDelegate.
Network requests using a URLSession with a NetworkCaptureSessionDelegate or made with one of the bt-prefixed URLSession methods will be associated with the last main timer to have been started at the time a request completes. Note that requests are only captured after at least one main timer has been started and they are not associated with a timer until the request ends.
You can use NetworkCaptureSessionDelegate or a subclass as your URLSession delegate to gather information about network requests when network capture is enabled:
let session = URLSession(
configuration: .default,
delegate: NetworkCaptureSessionDelegate(),
delegateQueue: nil)
let timer = BlueTriangle.startTimer(page: Page(pageName: "MY_PAGE"))
...
let (data, response) = try await session.data(from: URL(string: "https://example.com")!)
If you have already implemented and set URLSessionDelegate to URLSession, you can call NetworkCaptureSessionDelegate objects urlSession(session: task: didFinishCollecting:) method:
func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) {
//Your code ...
let sessionDelegate = NetworkCaptureSessionDelegate()
sessionDelegate.urlSession(session, task: task, didFinishCollecting: metrics)
}
Alternatively, use bt-prefixed URLSession methods to capture network requests:
Standard | Network Capture |
---|---|
URLSession.dataTask(with:completionHandler:) | URLSession.btDataTask(with:completionHandler:) |
URLSession.data(for:delegate:) | URLSession.btData(for:delegate:) |
URLSession.dataTaskPublisher(for:) | URLSession.btDataTaskPublisher(for:) |
Use these methods just as you would their standard counterparts:
let timer = BlueTriangle.startTimer(page: Page(pageName: "MY_PAGE"))
...
URLSession.shared.btDataTask(with: URL(string: "https://example.com")!) { data, response, error in
// ...
}.resume()
For other network capture requirements, captured requests can be manually created and submitted to the tracker.
If you have the URL, method, and requestBodyLength in the request, and httpStatusCode, responseBodyLength, and contentType in the response
let tracker = NetworkCaptureTracker.init(url: "https://example.com", method: "post", requestBodylength: 9130)
tracker.submit(200, responseBodyLength: 11120, contentType: "json")
If you have urlRequest in request and urlResponse in response
let tracker = NetworkCaptureTracker.init(request: urlRequest)
tracker.submit(urlResponse)
where urlRequest and urlResponse are of URLRequest and URLResponse types, respectively.
If you encounter an error during a network call
let tracker = NetworkCaptureTracker.init(url: "https://example.com", method: "post", requestBodylength: 9130)
tracker.failed(error)
OR
let tracker = NetworkCaptureTracker.init(request: urlRequest)
tracker.failed(error)
Network sample rate indicates the percentage of session network requests that are captured. For example a value of 0.05 means that network capture will be randomly enabled for 5% of user sessions. Network sample rate value should be between 0.0 to 1.0 representing fraction value of percent 0 to 100. The default networkSampleRate value is 0.05, i.e only 5% of sessions network request are captured.
To change the network capture sample rate set value of the 'config.networkSampleRate' to 0.5 to set is to 50%.
BlueTriangle.configure { config in
config.siteID = "<MY_SITE_ID>"
config.networkSampleRate = 0.5
...
}
To disable network capture set 0.0 to 'config.networkSampleRate' during configuration.
It is recommended to have 100% sample rate while developing/debugging. By setting 'config.networkSampleRate' to 1.0 during configuration.
The following fields can be used to identify and segment users for optimized analytics contextualization. They can be configured in the SDK and modified in the app in real time, and they show in the Blue Triangle portal as parameters for reporting.
Field | Implication |
---|---|
abTestID="MY_AB_TEST_ID" | Capture a variable that allows us to understand a live AB test of two variants in the app. |
campaignMedium="MY_CAMPAIGN_MEDIUM" | Understand the general reason the journey started (email, paid search, organic search, etc) |
campaignName="MY_CAMPAIGN_NAME" | Understand the campaign name that started the journey. |
campaignSource="MY_CAMPAIGN_SOURCE" | Understanding the type of marketing campaign. |
dataCenter="MY_DATA_CENTER" | Understand if you have multiple data centers that serve your customers you can group data by them. |
trafficSegmentName="MY_SEGMENT_NAME" | This can be used to segment environment type. For instance, we can use this to understand if you have beta vs prod but both are live versions of the app. |
BlueTriangle.configure { config in
config.abTestID = "MY_AB_TEST_ID"
config.campaignMedium = "MY_CAMPAIGN_MEDIUM"
config.campaignName = "MY_CAMPAIGN_NAME"
config.campaignSource = "MY_CAMPAIGN_SOURCE"
config.dataCenter = "MY_DATA_CANTER "
config.trafficSegmentName = "MY_TRAFFIC_SEGEMENT_NAME"
}
While Screen Views are automatically tracked upon installation, Custom Timers can also be configured if needed. The automated timers capture are following these events:
To measure the duration of a user interaction, initialize a Page
object describing that interaction and pass it to BlueTriangle.startTimer(page:timerType)
to receive a running timer instance.
let page = Page(pageName: "MY_PAGE")
let timer = BlueTriangle.startTimer(page: page)
If you need to defer the start of the timer, pass your Page
instance to BlueTriangle.makeTimer(page:timerType)
and call the timer's start()
method when you are ready to start timing:
let page = Page(pageName: "MY_PAGE")
let timer = BlueTriangle.makeTimer(page: page)
...
timer.start()
In both cases, pass your timer to BlueTriangle.endTimer(_:purchaseConfirmation:)
to send it to the Blue Triangle server.
BlueTriangle.endTimer(timer)
Running timers are automatically stopped when passed to BlueTriangle.endTimer(_:purchaseConfirmation:)
, though you can end timing earlier by calling the timer's end()
method.
timer.end()
...
// You must still pass the timer to `BlueTriangle.endTimer(_:)` to send it to the Blue Triangle server
BlueTriangle.endTimer(timer)
For timers that are associated with checkout, create a PurchaseConfirmation
object to pass along with the timer to BlueTriangle.endTimer(_:purchaseConfirmation:)
:
timer.end()
let purchaseConfirmation = PurchaseConfirmation(cartValue: 99.00)
BlueTriangle.endTimer(timer, purchaseConfirmation: purchaseConfirmation)
Upon a customer checkout, it is possible to configure the following data parameters for the event.
Brand Value
let timer = BlueTriangle.startTimer( page: Page( pageName: "SignUp", brandValue: 100.0))
BlueTriangle.endTimer(timer)
Cart Value, Cart Count, Cart Count Checkout, Order Number, Order Time
let timer = BlueTriangle.startTimer(
page: Page(
pageName: "Confirmation"))
BlueTriangle.endTimer(
timer,
purchaseConfirmation: PurchaseConfirmation(
cartValue:99.0,
cartCount: 2,
cartCountCheckout : 2,
orderNumber: "ORD-123345"))
BlueTriangle tracks Apps responsiveness by monitoring main THREAD USAGE. If any task blocking main thread for extended period of time causing app not responding, will be tracked as ANR Morning. By default this time interval is 5 Sec I.e. if any task blocking main thread more then 5 sec will be triggered as ANRWorning. This timinterval can be changed using "ANRWarningTimeInterval" Property below.
BlueTriangle.configure { config in
...
config.ANRWarningTimeInterval = 3
}
You can disable it by setting "ANRMonitoring" configuration property to "false" during configuration.
BlueTriangle.configure { config in
...
config.ANRMonitoring = false
}
Blue Triangle track ios reported low memory warning. By monitoring UIApplication.didReceiveMemoryWarningNotification Notification.
You can disable it by setting "enableMemoryWarning" configuration property to "false" during configuration.
BlueTriangle.configure { config in
...
config.enableMemoryWarning = false
}
Memory usage is the amount of memory used by the code during the Timer interval. This is measured in number of bytes.
Against each timer, 3 Memory measurements are being sent, minimum, maximum and average.
Memory usage refers to the amount memory (RAM) that is currently being used by application to store and manage data. In analytics.rcv payload data json, 'minMemory', 'maxMemory' and 'avgMemory' are being used to send the respective memory usage.
To set the interval (in seconds) at which the Memory usage is being captured, set the following field:
BlueTriangle.configure { config in
...
config.performanceMonitorSampleRate = 1
}
To disable Memory usage set the following field:
BlueTriangle.configure { config in
...
config.isPerformanceMonitorEnabled = false
}
CPU Usage is the amount of CPU being used by the code during the Timer interval. This is measured in the form of 0-100%.
Against each timer, 3 CPU measurements are being sent, minimum, maximum and average.
CPU usage is being reported by xcode as X.100% format [where X is number of cores], it typically means that the system is utilizing the CPU resources heavily. To express this in a 0% to 100% format, Blue Triangle calculates the CPU usage by dividing number of CPU cores. This will give you a percentage value between 0% and 100%.
0% to 100% format = Total current CPU usage on Instruments / Number of CPU cores.
For example, if you have 4 CPU cores and your current usage is 300%. then actual BTT CPU usage 300% / 4 = 75%. This indicates that CPU is being utilized at 75% of its total capacity.
To set the interval (in s) at which the CPU usage is being captured, set the following field in BlueTriangleConfiguration:
BlueTriangle.configure { config in
...
config.performanceMonitorSampleRate = 1
}
To disable CPU usage set the following field in BlueTriangleConfiguration:
BlueTriangle.configure { config in
...
config.isPerformanceMonitorEnabled = false
}
Offline caching is a feature that allows the BTT SDK to keep track of timers and other analytics data while the app is in offline mode. i.e, the BTT SDK cannot send data back to Blue Triangle.
There is a memory limit as well as an expiration duration put on the cached data. If the cache exceeds the memory limit then additional tracker data will be added only after removing some older, cached data (first in, first out). Similarly, cache data that has been stored for longer than the expiration duration would be discarded and won't be sent to the tracker server.
Memory limit and Expiry Duration can be set by using configuration property cacheMemoryLimit and cacheExpiryDuration as shown below:
BlueTriangle.configure { config in
...
config.cacheMemoryLimit = 50 * 1024 (Bytes)
config.cacheExpiryDuration = 50 * 60 * 1000 (Millisecond)
}
By default, the cacheMemoryLimit is set to 48 hours and cacheExpiryDuration is set to 30 MB.
BlueTriangle SDK allows capturing of network state data. Network state refers to the availability of any network interfaces on the device. Network interfaces include wifi, ethernet, cellular, etc. Once Network state capturing is enabled, the Network state is associated with all Timers, Errors and Network Requests captured by the SDK. This feature is enabled by default.
You can disable it by setting enableTrackingNetworkState property to "false" during configuration.
BlueTriangle.configure { config in
...
config.enableTrackingNetworkState = false
}
BlueTriangle tracks app launch performance. Launch time refers to the duration it takes for an app to become ready for user interaction after it has been started. BlueTriangle automatically tracks both hot launch and cold launch.
A cold launch is launch when app process was already not in main memory. This can happen because iOS or user terminated your apps process or its first time launch after install/update/reboot.
The BlueTriangle SDK measures the cold launch latency, which is the time between the process start time and end of 'applicationDidBecomeActive(:)'. So that cold launch time is cumulative time taken to load the process and time taken by 'application(:didFinishLaunchingWithOptions:)', 'applicationWillEnterForeground(:)' and 'applicationDidBecomeActive(:)'.
A hot launch is launch when app process was already in main memory. This can happen when user launches the app from the background.
The BlueTriangle SDK measures the hot launch latency, which is the time between the end of 'applicationWillEnterForeground(:)' and end of 'applicationDidBecomeActive(:)'. So that hot launch time taken by 'applicationDidBecomeActive(:)'.
When user lock the device while app was on screen and unlocks it iOS gives background and forground notification. Hence unclocking followed by lock while app was active is tracked as Hot Launch
You can disable it by setting "enableLaunchTime" configuration property to "false" during configuration. like
BlueTriangle.configure { config in
...
config.enableLaunchTime = false
}
Log onto your account on the Blue Triangle Portal, head over to "Native App -> Performance overview" and see that you can observe some data.
To test Memory Warning, In the iOS Simulator, you can generate a memory warning using the following steps:
- Launch Simulator
- Go to XCode 'Debug' menu
- Select 'Simulate Memory Warning' to generate memory warning
To test ANR Tracking, You can declare and call the following function on the main thread:
func testANRTracking(){ let startTime = Date() while true { if (Date().timeIntervalSince1970 - startTime.timeIntervalSince1970) > 30 { break } } }
To test Crash Tracking, you can declare and call the following function:
func testCrashTracking() { let array = NSArray() array.object(at: 99) }