# Mobile App Monitoring for iOS
Applications Manager Mobile APM for iOS lets you track the performance of your native mobile applications on actual end-user devices. For example, a news reader application may perform the following operations internally:
- Navigate to a table view to show a list of articles
- Load the list of articles using a REST API call
- Cache the list of articles in an SQLite database
- Download a thumbnail for each article
- Cache the thumbnails to the filesystem
- Build a complex UI such as a custom table view cell style
All of the above are potentially long-running operations that impact user experience so it's important to benchmark and optimize them across various devices. Applications Manager Mobile APM gathers and aggregates metrics from all your users across the globe by embedding an APM agent in your applications in the form of a library.
The APM agent measures the execution time of your code using transactions and components. In the previous example, the entire sequence of operations from starting navigation to rendering the final UI can be considered a transaction. The individual operations can be grouped into different component types such as HTTP, SQLite, filesystem, UI etc. Simple operations can be measured using just transactions, while complex operations can be measured using transactions with components.
Browse through the following topics to learn more about configuring Mobile App Monitor for iOS:
- [Integrate MEAPMInsight using Swift Package Manager](#integrate)
- [Configure MEAPMInsight](#configure)
- [Start a transaction](#start)
- [Group operations within a transaction using components](#group)
- [Flush the data](#flush)
- [Custom APIs](#custom)
## Integrate MEAPMInsight using Swift Package Manager
Follow the steps given below to integrate MEAPMInsight package from repository using Swift Package Manager:
1. Open your project in **Xcode**.
2. Select **File → Add Packages**… from the menu bar.
3. Enter the package repository URL: [https://github.com/ManageEngine/MEAPMInsight](https://github.com/ManageEngine/MEAPMInsight)
4. Set the **Dependency Rule** to **Up to Next Major Version.**
5. Select the **application target** where the package should be added.
6. Click **Add Package**.
## Configure MEAPMInsight
After adding MEAPMInsight using Swift Package Manager, complete the following configuration steps:
### 1. Create a Mobile App Monitor
- Create a [Mobile App Monitor](https://www.manageengine.com/products/applications_manager/help/configuring-mobile-app-monitor.html) in the MangeEngine Applications Manager.
- Download the generated configuration (**apm_config.plist**) file.
### 2. Add configuration file to your Xcode project
- Drag the downloaded configuration (**apm_config.plist**) file into the root of your Xcode project.
- In the dialog:
- Set **Action** to **Copy files to destination**.
- Select the required **application target**.
- Click **Finish**.
### 3. Self-signed certificate (if applicable)
If your DEM Collector uses a self-signed SSL certificate:
#### i. Add Certificate to Xcode Project:
- Copy the apm_cert.cer from DEM Collector installed directory **/conf/sslcerts** and move it to the server were Xcode project is opened.
- Drag **apm_cert.cer** into the root of your Xcode project.
- In the dialog:
- Set **Action** to **Copy files to destination**.
- Select the required **application target**.
- Click **Finish**.
This allows the MEAPMInsight SDK to securely communicate with the DEM Collector.
#### ii. Add App Transport Security Exception
When using a self-signed certificate, you must allow an exception in **Info.plist** file for the DEM collector domain. To do so, open the **Info.plist** file and add the following configuration:
```xml
NSAppTransportSecurity
NSExceptionDomains
[collectordomain].com
NSExceptionAllowsInsecureHTTPLoads
```
Replace **[collectordomain].com** with your DEM collector domain.
### 4. Initialize the SDK in `AppDelegate` class
Initialize the SDK in your `AppDelegate` class using the following code to start monitoring:
```swift
import MEAPMInsight
@main
class AppDelegate: UIApplicationDelegate {
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
#if DEBUG
APMInsight.setDebugLoggingEnabled(true)
APMInsight.setEnvironment("debug")
#endif
APMInsight.startMonitoring()
// Below are Optional
APMInsight.enableErrorReporting()
// Pass array of URLs to ignore from HTTP tracking
APMInsight.enableHttpTracking([
"https://api.example.com/analytics",
"https://cdn.example.com"
])
return true
}
}
```
**Important:** To ensure crash reports are properly symbolicated, set **Debug Information Format** to **DWARF with dSYM file** in the Build Settings. Make sure this setting is applied to your release builds before distributing/shipping your app.
## Start a transaction
You should typically start a transaction before a long-running operation and stop it when the operation is complete. Transactions are thread-safe and can be started and stopped from different threads. A transaction object can only be started and stopped once. Transactions with the same name are averaged across the application. Thus, when the same operation is executed multiple times using the same transaction name, the average execution time is recorded.
```swift
func listArticles() {
let transaction = APMInsight.startTransaction(withName: "List Articles")
// Perform long-running operation
APMInsight.stopTransaction(transaction)
}
```
## Group operations within a transaction using components
You can group operations within a transaction into different types of components. You can use one of the predefined types such as APMHTTPComponent, APMSQLiteComponent, APMUIComponent etc., or specify your own type. Components are thread-safe and can be started and stopped from different threads. A component object can only be started and stopped once. A component object cannot be stopped after its parent transaction has already been stopped. Multiple components in a transaction can overlap and run in parallel. Components with the same name within a transaction are averaged.
```swift
func listArticles() {
let transaction = APMInsight.startTransaction(withName: "List Articles")
let httpComponent = transaction?.startComponent(withType: APMHttpComponent)
let articlesComponent = transaction?.startComponent(withType: "Download Articles")
// Download articles
transaction?.stopComponent(articlesComponent)
for article in articles {
let thumbnailComponent = transaction?.startComponent(withType: "Download Thumbnail")
// Download thumbnail
transaction?.stopComponent(thumbnailComponent)
}
transaction?.stopComponent(httpComponent)
APMInsight.stopTransaction(transaction)
}
```
In the above example, the total time taken by HTTP operations (downloading articles and thumbnails) are measured by APMHttpComponent and the time taken for just the articles is measured by "Download Articles". The time taken for each thumbnail is averaged and recorded by "Download Thumbnail" since it executes multiple times within a loop.
## Flush the data
Sometimes it's desirable to manually flush recently recorded data. You may want to do this if you record transactions just before your application is terminated. If you've set a large upload interval (the default is 30 seconds), you should manually flush data whenever appropriate in case the application is terminated before the next upload interval.
```swift
APMInsight.flush()
```
## Custom APIs
Custom APIs are used to set dynamic values for user IDs, track custom events, capture screens, and more. This document will explain the various types of custom APIs available and the syntax to use them. Following are the list of available APIs:
1. [Initializing the SDK](#initializeSDK)
2. [Custom User ID](#customUserID)
3. [Transactions](#transactions)
4. [HTTP Call](#httpCall)
5. [Screen](#screen)
6. [Environment](#environment)
7. [Flush](#flush1)
8. [Exclude HTTP Calls](#excludeHTTPCalls)
9. [Exclude Screens](#excludeScreens)
### 1. Initializing the SDK
You can use the API below to initialize the SDK with a custom upload interval. By default, the data upload interval is set to 30 seconds.
**Syntax for Swift:**
```swift
APMInsight.startMonitoring()
```
**Syntax for Objective-C:**
```objectivec
[APMInsight startMonitoring];
```
### 2. Custom User ID
By default, a unique user ID is generated by the SDK. If you want to customize user IDs, you can do so by implementing the following syntax. This comes in handy when you want to track metrics or debug issues specific to a particular user.
**Note:** Do not pass any Personally Identifiable Information (PII) such as email addresses, phone numbers, or names to this function. Use anonymized or non-sensitive identifiers only.
**Syntax for Swift:**
```swift
APMInsight.setUserId("user123")
```
**Syntax for Objective-C:**
```objectivec
[APMInsight setUserId:@"user123"];
```
### 3. Transactions
You can use the API below to track custom events with application-specific timing.
**Syntax for Swift:**
```swift
let transaction = APMInsight.startTransaction(withName: "List Articles")
let httpComponent = transaction?.startComponent(withType: APMHttpComponent)
let articlesComponent = transaction?.startComponent(withType: "Download Articles")
// Download articles
transaction?.stopComponent(articlesComponent)
for article in articles {
let thumbnailComponent = transaction?.startComponent(withType: "Download Thumbnail")
// Download thumbnail
transaction?.stopComponent(thumbnailComponent)
}
transaction?.stopComponent(httpComponent)
APMInsight.stopTransaction(transaction)
```
**Syntax for Objective-C:**
```objectivec
APMInsightTransaction *transaction = [APMInsight startTransactionWithName:@"List Articles"];
APMInsightComponent *httpComponent = [transaction startComponentWithType:APMHttpComponent];
APMInsightComponent *articlesComponent = [transaction startComponentWithType:@"Download Articles"];
// Download articles
[transaction stopComponent:articlesComponent];
for (Article *article in articles) {
APMInsightComponent *thumbnailComponent = [transaction startComponentWithType:@"Download Thumbnail"];
// Download thumbnail
[transaction stopComponent:thumbnailComponent];
}
[transaction stopComponent:httpComponent];
[APMInsight stopTransaction:transaction];
```
### 4. HTTP Call
By default, network requests that use the following networking libraries are automatically captured.
- *NSUrlSession*
- *NSUrlConnection*
If you want to add more HTTP calls, use the API below:
**Syntax for Swift:**
```swift
/*!
Add a http call. This method is thread-safe.
@param name The http call name.
@param duration The http call load time in milliseconds.
@param startTime The http call start time in milliseconds.
@param respCode The http call response code.
@param httpMethod The http call method.
*/
APMInsight.addHttpCall("example.com", withTime: 30, startTime:1642750130310,
respCode: 200, httpMethod: "GET")
```
**Syntax for Objective-C:**
```objectivec
/*!
Add a http call. This method is thread-safe.
@param name The http call name.
@param duration The http call load time in milliseconds.
@param startTime The http call start time in milliseconds.
@param respCode The http call response code.
@param httpMethod The http call method.
*/
[APMInsight addHttpCall:@"example.com" withTime:30 startTime: 1642750130310
respCode:200 httpMethod:@"GET"];
```
### 5. Screen
By default, all screens are captured; however, if you want to add a missing screen, use the API given below.
**Syntax for Swift:**
```swift
/*!
Add a screen. This method is thread-safe.
@param screen The screen name.
@param duration The screen load time.
@param startTime The screen start time.
*/
APMInsight.addScreen("DetailScreen", withTime: 60, startTime: "1642750130310")
```
**Syntax for Objective-C:**
```objectivec
/*!
Add a screen. This method is thread-safe.
@param screen The screen name.
@param duration The screen load time.
@param startTime The screen start time.
*/
[APMInsight addScreen:@"DetailScreen" withTime:60 startTime:@"1642750130310"];
```
#### SwiftUI View Tracking
By default, SwiftUI views are not tracked automatically. To track SwiftUI views, you need to manually add the `.trackScreen()` modifier to your views.
**Syntax for swiftUI:**
```swift
import SwiftUI
struct MainView: View {
var body: some View {
NavigationView {
VStack {
NavigationLink("Go to Detail", destination: DetailView())
}
.trackScreen("MainView")
}
}
}
struct DetailView: View {
var body: some View {
VStack {
Text("Detail Screen")
}
.trackScreen("DetailView")
}
}
```
**Note:**
- The `.trackScreen()` modifier should be added to each SwiftUI view you want to track.
- The screen name parameter should be a descriptive string that identifies the view.
### 6. Environment
You can use the API below to set custom environment details for filtering data across various environment setups like development, debug, production, or release.
**Syntax for Swift:**
```swift
/*!
Sets the environment name for the APM agent.
@param environment A custom environment name (for example, "debug", "release", or "beta-release")
*/
APMInsight.setEnvironment("release")
```
**Syntax for Objective-C:**
```objectivec
/*!
Sets the environment name for the APM agent.
@param environment A custom environment name (for example, "debug", "release", or "beta-release")
*/
[APMInsight setEnvironment:@"release"];
```
### 7. Flush
You can use the API below to immediately upload recorded data to the DEM Collector instead of waiting for the next upload interval. By default, the flush interval is set to 60 seconds.
**Syntax for Swift:**
```swift
APMInsight.flush()
```
**Syntax for Objective-C:**
```objectivec
[APMInsight flush];
```
**Note:** If there's no network available, data will be held in the queue until the next upload interval. If you've configured a long upload interval, you may need to manually flush data.
### 8. Exclude HTTP Calls
You can exclude custom URLs from tracking by passing a list of URLs as an argument to the API below.
**Syntax for Swift:**
```swift
APMInsight.enableHttpTracking(["example.com", "demo.com"])
```
**Syntax for Objective-C:**
```objectivec
[APMInsight enableHttpTracking:@[@"example.com", @"demo.com"]];
```
### 9. Exclude Screens
You can exclude custom screens from tracking by passing a list of screens as an argument to the API below.
**Syntax for Swift:**
```swift
APMInsight.ignoreScreens(["DetailScreen", "HomeScreen"])
```
**Syntax for Objective-C:**
```objectivec
[APMInsight ignoreScreens:@[@"DetailScreen", @"HomeScreen"]];
```