This document describes the integration of the Insight iOS SDK with your iOS application.
## Introduction
Insight iOS SDK collects location information and event data to track user interactions within the iOS application where it's installed. This data can be used for analyzing user behavior, improving user experience, and generating insights.
* You need to create a *Insight Access Key* for the iOS platform if not created already.
> **Note:** Make sure that you declare that "your app uses IDFA" on App Store Connect if you are using IDFA integration. Otherwise, your app may be rejected on review. Insight iOS SDK collects IDFA from the device when permitted by the user, in full compliance with the Apple [requirements](https://developer.apple.com/documentation/apptrackingtransparency). Insight doesn't trigger the permission dialog; you need to trigger this if needed.
## 3. Integration
### Adding the Insight iOS SDK
The Insight iOS SDK can be added via Swift Package Manager, CocoaPods, or manually.
Copy "InsightSDK.xcframework" folder by dragging and dropping into your app using project navigator in Xcode. After copying the xcframework into your app, the color of the xcframework shall be blue.
##### Mark Embed and Sign
Go to project settings "Frameworks, Libraries and Embedded Content" section. Choose "Embed & Sign" for InsightSDK.xcframework.
The SDK asks the user for no permission. Your app decides whether to ask for location and notification permissions, and the SDK uses a permission only after the user grants it. Location is optional: without it the SDK works and sends no location.
1.**A purpose string in `Info.plist`** for each kind of location permission your app requests: `NSLocationWhenInUseUsageDescription`, and `NSLocationAlwaysAndWhenInUseUsageDescription` if you also request Always. Without it, iOS shows the user no prompt, and App Store Connect can reject the build.
2.**Location in your app's privacy declarations.** The SDK sends the device location to Insight once the user allows it. Declare Precise Location, linked to the user, for analytics, in your app's privacy manifest (`PrivacyInfo.xcprivacy`) and in the App Privacy details in App Store Connect.
With Always permission, the SDK also receives location updates while your app is in the background. In the subsections below, we provide sample description text.
This descriptor is used for iOS 11 and later. It provides permission for both when in use and when killed.
Sample Text: *"This application uses your location in order to inform you about interesting offers nearby. We advise you to choose the `Always` option to get the offers even when you are not using the application!"*
You can insert it into your `Info.plist` file in the following syntax.
This descriptor is used in all iOS versions. It provides permission for only when the application is in use. We advise you to warn your users about very low performance when receiving nearby offers.
Sample Text: *"This application uses your location in order to inform you about interesting offers nearby. Allowing location when in use only may result in poor performance in finding nearby offers!"*
You can insert it into your `Info.plist` file in the following syntax.
```xml
<key>NSLocationWhenInUseUsageDescription</key>
<string><# insert when in use usage description text #></string>
```
## 4. Usage
In order to utilize the capabilities of the Insight iOS SDK, it needs to be started at some point in your application's lifecycle.
Insight iOS SDK requires the **Insight Platform Access Key**. You may need to create one for the iOS platform at the *Insight Publisher Portal*. If you do not have an account at the *Insight Publisher Portal*, please contact us at sdk@insight.apps.sccc.sa. You can set your key in your application's `Info.plist` file with the `Insight Platform Access Key` key:
For SwiftUI applications (iOS 14+), you need to create an `AppDelegate.swift` file and use `@UIApplicationDelegateAdaptor` to ensure the SDK initializes properly.
#### Create AppDelegate.swift
Create a new file called `AppDelegate.swift` with your SDK initialization:
The `user` parameter allows you to enhance the audience data by providing information about the primary user (subscriber) of your application. You can give any information that makes the subscriber unique in your application's understanding. The `User` class contains the following:
| userId | String? | "42" | Unique identifier of the user |
| gender | UserGender? | .female | Gender of the user (.female or .male) |
| yearOfBirth | Int? | 1999 | Year of birth of the user |
| email | String? | "mail@me.com" | Email address of the user |
| displayName | String? | "John Doe" | Display name of the user |
| phoneNumber | String? | "+96653612345" | Mobile phone number of the user |
| isPIIHashed | Bool? | true | Whether to hash user info |
| other | Dictionary<String,String>? | ["key": "value"] | Extra information for the user |
> **Note:** `email` and `phoneNumber` details are never sent in plain-text to the *Insight Platform* if the `isPIIHashed` property is set to true (default true). These values are always irreversibly hashed so that no personally identifiable information is stored.
### Stopping, restarting and changing configuration
`start(config:user:completion:)` covers the first launch. A settings screen that changes the user or the API key needs the calls below. Call each of them on `Insight.shared`.
#### Stopping
`stop()` and `stop(completion:)` return at once. From the call, the SDK collects nothing. Event logging, crash reporting, location monitoring, screen tracking, network performance tracking and screen recording stop. An event that you log answers its callback with an `APIError` whose `code` is `-4`, and the SDK does not store it.
Then the SDK sends what it holds for at most 5 seconds, in the background. It sends the stored crash reports first, then the queued events and the queued screen recordings together. Whatever did not leave stays queued and goes out after your next `start`. The stop deletes nothing.
`stop(completion:)` calls `completion` once, on the main queue, when the SDK has sent what it could. This happens at most 5 seconds after the call, or at once when the SDK was not started. The SDK does not cancel a request that is still in progress at 5 seconds. A second `stop` before the report joins the first and gets the same report. A `start` or `restart` that you call before the report waits for it and runs after it. After the report, the SDK sends nothing until your next start succeeds.
For screen recording, a stop ends the open recording and adds its last part to the queue that the stop sends. The next `start` turns screen recording on again.
```swift
importInsightSDK
Insight.shared.stop(completion:{
debugPrint("InsightSDK stopped")
})
```
#### Changing the user
The SDK sends the `User` when a session starts. To send a changed user, such as a new display name, call `restart(user:config:)` with the new `User`. The SDK stops, waits for the stop's report and starts again with the new `User`. A call to `stop()` and then to `start(config:user:completion:)` does the same, because the start waits for the stop's report.
To use a new API key, pass a new `SdkConfiguration` to `restart(user:config:)`. The SDK stops, then starts with the new configuration once the stop has reported. The change deletes nothing. New events, crash reports and screen recordings go out with the new key. What the SDK queued under the old key keeps going out with the old key.
`restart(user:config:)` stops the SDK and starts it again once the stop has reported. Both parameters are optional. A `nil` user or configuration means the one from the most recent start. When the SDK is not running, `restart` only starts it. The stop sends what the SDK holds for at most 5 seconds and keeps the rest, as [Stopping](#stopping) describes.
Events that your app logs between the `restart` call and the end of the new start wait in memory, in order. When the start ends, the SDK logs them. If event logging is off at that time, each of them gets the error `-4`. A plain `stop()` still refuses events with `-4`.
```swift
importInsightSDK
Insight.shared.restart()
```
### Clearing stored data
Each method below erases one part of what the SDK keeps on the device. None of them calls `stop()`. Call each of them on `Insight.shared`:
| Method | What it clears | What it keeps |
|--------|----------------|---------------|
| `clearStoredData()` | The stored crash reports and every stored screen recording. It also stops a recording upload in progress. | Queued events and health messages. |
| `clearHealthMessages()` | The messages that you logged with `logHealthMessage(_:)`. | Everything else. |
| `clearScreenRecordings()` | Every stored screen recording and its upload queue. It also stops an upload in progress. It works before `start`. | Everything else. |
| `clearRequestQueues()` | Every queued event that waits for upload, for every API key. | Everything else, screen recordings included. |
`clearStoredData()` and `clearScreenRecordings()` wait for the main thread when you call them from another thread. Data that the server already has needs a server-side deletion.
`flushBatchRequests()` erases nothing. It asks the SDK to send the queued events now and returns at once. Before a start succeeds, and between `stop()` and the next successful start, it sends nothing.
Insight iOS SDK tracks the performance of the HTTP requests that your app makes with `URLSession`. You can monitor API response times and status codes.
#### URLSession integration
You write no code. The SDK tracks requests while your server settings turn network performance tracking on. It measures the requests that your app starts with these `URLSession` methods:
-`dataTask(with:)` and `dataTask(with:completionHandler:)`, with a `URLRequest` or a `URL`
-`uploadTask(with:from:completionHandler:)`
-`downloadTask(with:completionHandler:)`, with a `URLRequest`
The SDK adds its measurement to these methods at run time, through method swizzling. It measures requests on `URLSession.shared` and on every session that your app or a library creates. Your session delegate still gets each call. After `stop()`, the SDK measures nothing until the next start.
#### What gets tracked
The network performance tracking captures:
- **Request URL and method** (GET, POST, etc.)
- **Response time** (total duration from request start to completion)
- **HTTP status codes** (200, 404, 500, etc.)
- **Request and response sizes**
The SDK removes the user name, password, query and fragment from each URL. For a download task, the response size is 0. The SDK records a request only when it gets an HTTP response, so a request that fails with no response leaves no record.
#### Which hosts get measured
The Insight platform settings can name the hosts to measure. When they name any, the SDK measures requests to those hosts alone and skips the rest. When they name none, the SDK measures requests to every host. In both cases, the SDK skips the Insight host that it sends its own data to. It also skips every URL whose scheme is not `http` or `https`.
> **Note:** The SDK sends each measurement to the Insight platform.
### Screen recording (preview)
> **Preview:** Screen recording is in preview. Its behavior can change in a
> later release.
#### Overview
**Screen recording** captures masked frames of your app's screens, so your
team can replay a user session. It runs on iOS 15 or later, in UIKit and
SwiftUI apps. A frame costs about 70 ms of main-thread time on an iPhone 15
Pro Max. The SDK limits that work to 100 ms in each second.
Screen recording is off until your app's server settings turn it on. The
same settings control the privacy level and the frame quality. Your app has
nothing to configure, and the SDK has no local switch. When the settings
turn recording on, the SDK starts to record at once. Some other changes to
the server settings apply at the next app launch.
#### What you need to do
In most apps, you do not need to write code. The SDK captures a new screen
when a view controller appears, a presentation closes, or the key window
changes. In SwiftUI, it also captures one when a `NavigationStack` pushes or
pops, a `TabView` changes tab, or a sheet or cover appears.
Some changes give the SDK no signal. An example is SwiftUI content that your
app swaps on state, with an `if` or a `switch` in a view body. The SDK then
records the new content on its normal schedule, at most once per second by
default. A screen that the user leaves within that second can have no frame.
To record a new screen at once, log a screen view event when it appears. The
SDK treats the next frame as a new screen and records it outside the
To record a change inside one screen, such as a view that your app swaps on
a state change, call `requestScreenRecordingCapture()`:
```swift
importInsightSDK
Insight.shared.requestScreenRecordingCapture()
```
The SDK captures the screen at once. The normal capture limits and privacy
rules still apply. A call before `start`, while the SDK is stopped, or while
the app is in the background does nothing.
#### API reference
The table lists every public screen recording API:
| API | Use it to | Notes |
|---|---|---|
| `var screenRecordingMask: Bool` | Mask a UIKit view and its subviews. | A property on `UIView`, also in Objective-C. The default is `false`. The SDK also withholds taps on the view. |
| `var screenRecordingUnmask: Bool` | Show the text and images of a UIKit view and its subviews. | A property on `UIView`, also in Objective-C. The default is `false`. It never unmasks always-masked content. |
| `screen-recording-mask` in `accessibilityIdentifier` | Mask a UIKit view through its identifier. | The identifier can hold other text too. It works as `screenRecordingMask` does. |
| `screen-recording-unmask` in `accessibilityIdentifier` | Unmask a UIKit view through its identifier. | The identifier can hold other text too. It works as `screenRecordingUnmask` does. |
| `func screenRecordingMask() -> some View` | Mask a SwiftUI view and its children. | A SwiftUI modifier. The SDK also withholds taps on the view. |
| `func screenRecordingUnmask() -> some View` | Show the text and images of a SwiftUI view and its children. | A SwiftUI modifier. It never unmasks always-masked content. |
| `func requestScreenRecordingCapture()` | Capture the screen after a change that the SDK cannot see. | Call it on `Insight.shared` from any thread. It returns at once. |
| `func clearScreenRecordings()` | Delete every screen recording that the device stores. | Call it on `Insight.shared` from any thread. It also stops an upload in progress, and it works before `start`. |
| `func clearStoredData()` | Delete the SDK's stored user data on the device. | It deletes every stored recording and the pending crash reports. Data that the server already has needs a server-side deletion. |
| `InsightEventOtherScreenView(screenClass: String, screenName: String, parameters: [String: Value] = [:])` | Mark a new screen that the SDK cannot see. | Log it with `Insight.shared.logEvent(_:callback:)`. The recording does not show `screenName`, but a server rule for named screens matches it. |
> **Note:** `clearScreenRecordings()` and `clearStoredData()` wait for the
> main thread when you call them from another thread.
Use the `screenRecordingMask` or `screenRecordingUnmask` property to mark a
Due to Apple's ongoing privacy enhancements, several features have been deprecated or require special permissions:
### IDFA
Since iOS 14, Apple has introduced new privacy features that require user permission to access the IDFA. You'll need to request permission from the user before you can access the identifier.
- Add the `NSUserTrackingUsageDescription` key to your Info.plist file. This is the message that will be shown to the user when requesting IDFA access permission:
```xml
<key>NSUserTrackingUsageDescription</key>
<string>This identifier will be used to deliver personalized ads to you.</string>
```
Remember that users can choose to deny this permission, in which case you won't be able to access their IDFA.
Insight SDK doesn't immediately collect the IDFA when initialized. It waits for ATT permission to be granted. On the next start of the SDK, it will collect this information if the user has granted the permission.
Starting with iOS 16, Apple restricted access to the user-defined device name for privacy reasons. This was done to prevent apps from potentially identifying users or devices without explicit permission.
For apps that genuinely need access to the device name (like device management or networking apps), Apple provides an entitlement called `Access device name` (com.apple.developer.device-information.user-assigned-device-name). However, this entitlement requires approval from Apple, and you need to justify why your app needs this information.
If you don't have this entitlement, your app will only get generic device names like "iPhone" or "iPad".
[Reference](https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_developer_device-information_user-assigned-device-name) contains relevant information from Apple.
If you have acquired permissions, Insight SDK will collect this information.
Starting iOS 16, carrier name and code information are deprecated. [Reference 1](https://developer.apple.com/documentation/coretelephony/cttelephonynetworkinfo/servicesubscribercellularproviders) and [Reference 2](https://forums.developer.apple.com/forums/thread/714876) contains relevant information from Apple.