Skip to content

Commit 2a2758e

Browse files
committed
Release 2.2.0
1 parent ce7f759 commit 2a2758e

File tree

186 files changed

+364
-216
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

186 files changed

+364
-216
lines changed

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,15 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [2.2.0] - 2025-08-05
9+
10+
### Added
11+
- Introduced custom masking controls for improved privacy management.
12+
13+
### Fixed
14+
- Fixed an issue where manual unmasking did not work during session capture.
15+
- Resolved a session recording issue affecting SwiftUI views.
16+
817
## [2.1.0] - 2025-06-26
918

1019
### Added

DevRevSDK.doccarchive.zip

27.7 KB
Binary file not shown.

DevRevSDK.podspec

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
Pod::Spec.new do |spec|
22
spec.name = "DevRevSDK"
3-
spec.version = "2.1.0"
3+
spec.version = "2.2.0"
44
spec.summary = "DevRev SDK, used for integrating DevRev services into your iOS app."
55
spec.homepage = "https://devrev.ai"
66
spec.license = "Apache 2.0"
77
spec.author = { "DevRev" => "[email protected]" }
88
spec.platform = :ios, "15.0"
99
spec.source = {
10-
http: "https://github.com/devrev/devrev-sdk-ios/releases/download/v2.1.0/DevRevSDK.xcframework.zip",
10+
http: "https://github.com/devrev/devrev-sdk-ios/releases/download/v2.2.0/DevRevSDK.xcframework.zip",
1111
type: :zip,
1212
headers: [
1313
"Accept: application/octet-stream",

DevRevSDK.xcframework.zip

89.6 KB
Binary file not shown.

Package.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ let package = Package(
1818
targets: [
1919
.binaryTarget(
2020
name: "DevRevSDK",
21-
url: "https://github.com/devrev/devrev-sdk-ios/releases/download/v2.1.0/DevRevSDK.xcframework.zip",
22-
checksum: "cc6f553629b64728bd493ccd12ef21b26863d7e864e50ebe054af58a271fabee"
21+
url: "https://github.com/devrev/devrev-sdk-ios/releases/download/v2.2.0/DevRevSDK.xcframework.zip",
22+
checksum: "5815085578a6c2a24b5018ca9eeb94c0b0679a2cea8b7e23f4a940956738c0ae"
2323
)
2424
]
2525
)

README.md

Lines changed: 159 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,17 @@ DevRev SDK, used for integrating DevRev services into your iOS app.
1717
- [Anonymous identification](#anonymous-identification)
1818
- [Unverified identification](#unverified-identification)
1919
- [Verified identification](#verified-identification)
20+
- [Generate an AAT](#generate-an-aat)
21+
- [Exchange your AAT for a session token](#exchange-your-aat-for-a-session-token)
22+
- [Identifying the verified user](#identifying-the-verified-user)
2023
- [Updating the user](#updating-the-user)
2124
- [Logout](#logout)
2225
- [Examples](#examples)
26+
- [Identity model](#identity-model)
27+
- [Properties](#properties)
28+
- [UserTraits](#usertraits)
29+
- [OrganizationTraits](#organizationtraits)
30+
- [AccountTraits](#accounttraits)
2331
- [PLuG support chat](#plug-support-chat)
2432
- [UIKit](#uikit)
2533
- [Examples](#examples-1)
@@ -36,37 +44,37 @@ DevRev SDK, used for integrating DevRev services into your iOS app.
3644
- [Session recording](#session-recording)
3745
- [Session properties](#session-properties)
3846
- [Masking sensitive data](#masking-sensitive-data)
39-
- [Timers](#timers)
47+
- [Custom masking provider](#custom-masking-provider)
4048
- [Example](#example-2)
41-
- [Screen tracking](#screen-tracking)
49+
- [Timers](#timers)
4250
- [Example](#example-3)
51+
- [Screen tracking](#screen-tracking)
52+
- [Example](#example-4)
4353
- [Push notifications](#push-notifications)
4454
- [Configuration](#configuration)
4555
- [Register for push notifications](#register-for-push-notifications)
46-
- [Example](#example-4)
47-
- [Unregister from push notifications](#unregister-from-push-notifications)
4856
- [Example](#example-5)
49-
- [Handle push notifications](#handle-push-notifications)
57+
- [Unregister from push notifications](#unregister-from-push-notifications)
5058
- [Example](#example-6)
59+
- [Handle push notifications](#handle-push-notifications)
60+
- [Example](#example-7)
5161
- [Sample app](#sample-app)
5262
- [Troubleshooting](#troubleshooting)
5363
- [Migration guide](#migration-guide)
5464

5565
## Quickstart
5666
### Requirements
57-
- Xcode 16.0 or higher (latest stable version available on the App Store)
58-
- Swift 5.9 or later
59-
- Set the minimum deployment target for your iOS application as iOS 15
67+
- Xcode 16.0 or later (latest stable version available on the App Store).
68+
- Swift 5.9 or later.
69+
- Set the minimum deployment target for your iOS application as iOS 15.
6070

6171
### Integration
62-
6372
The DevRev SDK can be integrated using either Swift Package Manager (SPM) or CocoaPods.
6473

6574
> [!CAUTION]
6675
> We recommend integrating the DevRev SDK using Swift Package Manager. CocoaPods is in [maintenance mode](https://blog.cocoapods.org/CocoaPods-Support-Plans/) since August 2024 and will be [deprecated in the future](https://blog.cocoapods.org/CocoaPods-Specs-Repo/).
6776
6877
#### Swift Package Manager (Recommended)
69-
7078
You can integrate the DevRev SDK in your project as a Swift Package Manager (SPM) package.
7179

7280
To integrate the DevRev SDK into your project using SPM:
@@ -80,7 +88,6 @@ To integrate the DevRev SDK into your project using SPM:
8088
Now you should be able to import and use the DevRev SDK in your project.
8189

8290
#### CocoaPods
83-
8491
To integrate the DevRev SDK using CocoaPods:
8592

8693
1. Add the following to your `Podfile`:
@@ -93,7 +100,6 @@ To integrate the DevRev SDK using CocoaPods:
93100
This will install the DevRev SDK in your project, making it ready for use.
94101

95102
### Set up the DevRev SDK
96-
97103
1. Open the DevRev web app at [https://app.devrev.ai](https://app.devrev.ai) and go to the **Settings** page.
98104
2. Under **PLuG settings** copy the value under **Your unique App ID**.
99105
3. After obtaining the credentials, you can configure the DevRev SDK in your app.
@@ -126,7 +132,6 @@ To access certain features of the DevRev SDK, user identification is required.
126132

127133
The identification function should be placed appropriately in your app after the user logs in. If you have the user information available at app launch, call the function after the `DevRev.configure(appID:)` method.
128134

129-
130135
> [!IMPORTANT]
131136
> If you haven't previously identified the user, the DevRev SDK will automatically create an anonymous user for you immediately after the SDK is configured.
132137
@@ -150,8 +155,50 @@ DevRev.identifyUnverifiedUser(_:)
150155
The function accepts the `DevRev.Identity` structure, with the user identifier (`userID`) as the only required property, all other properties are optional.
151156

152157
#### Verified identification
153-
The verified identification method is used to identify the user with a unique identifier and verify the user's identity with the DevRev backend.
158+
The verified identification method is used to identify users with an identifier unique to your system within the DevRev platform. The verification is done through a token exchange process between you and the DevRev backend.
159+
160+
The steps to identify a verified user are as follows:
161+
1. Generate an AAT for your system (preferably through your backend).
162+
2. Exchange your AAT for a session token for each user of your system.
163+
3. Pass the user identifier and the exchanged session token to the `DevRev.identifyVerifiedUser(_:sessionToken:)` method.
164+
165+
> [!CAUTION]
166+
> For security reasons we **strongly recommend** that the token exchange is executed on your backend to prevent exposing your application access token (AAT).
167+
168+
##### Generate an AAT
169+
1. Open the DevRev web app at [https://app.devrev.ai](https://app.devrev.ai) and go to the **Settings** page.
170+
2. Open the **PLuG Tokens** page.
171+
3. Under the **Application access tokens** panel, click **New token** and copy the token that's displayed.
172+
173+
> [!IMPORTANT]
174+
> Ensure that you copy the generated application access token, as you cannot view it again.
175+
176+
##### Exchange your AAT for a session token
177+
In order to proceed with identifying the user, you need to exchange your AAT for a session token. This step will help you identify a user of your own system within the DevRev platform.
178+
179+
Here is a simple example of an API request to the DevRev backend to exchange your AAT for a session token:
180+
> [!CAUTION]
181+
> Make sure that you replace the `<AAT>` and `<YOUR_USER_ID>` with the actual values.
182+
```bash
183+
curl \
184+
--location 'https://api.devrev.ai/auth-tokens.create' \
185+
--header 'accept: application/json, text/plain, */*' \
186+
--header 'content-type: application/json' \
187+
--header 'authorization: <AAT>' \
188+
--data '{
189+
"rev_info": {
190+
"user_ref": "<YOUR_USER_ID>"
191+
}
192+
}'
193+
```
194+
195+
The response of the API call will contain a session token that you can use with the verified identification method in your app.
154196

197+
> [!NOTE]
198+
> As a good practice, **your** app should retrieve the exchanged session token from **your** backend at app launch or any relevant app lifecycle event.
199+
200+
##### Identifying the verified user
201+
Pass the user identifier and the exchanged session token to the verified identification method:
155202
```swift
156203
DevRev.identifyVerifiedUser(_:sessionToken:)
157204
```
@@ -200,6 +247,70 @@ await DevRev.identifyVerifiedUser("[email protected]", sessionToken: "bar-1337")
200247
await DevRev.updateUser(Identity(organizationID: "foo-bar-1337"))
201248
```
202249

250+
#### Identity model
251+
The `Identity` class is used to provide user, organization, and account information when identifying users or updating their details. This class is used primarily with the `identifyUnverifiedUser(_:)` and `updateUser(_:)` methods.
252+
253+
##### Properties
254+
The `Identity` class contains the following properties:
255+
256+
| Property | Type | Required | Description |
257+
|----------|------|----------|-------------|
258+
| `userID` | `String` || A unique identifier for the user |
259+
| `organizationID` | `String?` || An identifier for the user's organization |
260+
| `accountID` | `String?` || An identifier for the user's account |
261+
| `userTraits` | `UserTraits?` || Additional information about the user |
262+
| `organizationTraits` | `OrganizationTraits?` || Additional information about the organization |
263+
| `accountTraits` | `AccountTraits?` || Additional information about the account |
264+
265+
> [!NOTE]
266+
> The custom fields properties defined as part of the user, organization and account traits, must be configured in the DevRev web app **before** they can be used. See [Object customization](https://devrev.ai/docs/product/object-customization) for more information.
267+
268+
###### UserTraits
269+
The `UserTraits` class contains detailed information about the user:
270+
271+
> [!NOTE]
272+
> All properties in `UserTraits` are optional.
273+
274+
| Property | Type | Description |
275+
|----------|------|-------------|
276+
| `displayName` | `String?` | The displayed name of the user |
277+
| `email` | `String?` | The user's email address |
278+
| `fullName` | `String?` | The user's full name |
279+
| `userDescription` | `String?` | A description of the user |
280+
| `phoneNumbers` | `[String]?` | Array of the user's phone numbers |
281+
| `customFields` | `[String: Any]?` | Dictionary of custom fields configured in DevRev |
282+
283+
###### OrganizationTraits
284+
The `OrganizationTraits` class contains detailed information about the organization:
285+
286+
> [!NOTE]
287+
> All properties in `OrganizationTraits` are optional.
288+
289+
| Property | Type | Description |
290+
|----------|------|-------------|
291+
| `displayName` | `String?` | The displayed name of the organization |
292+
| `domain` | `String?` | The organization's domain |
293+
| `organizationDescription` | `String?` | A description of the organization |
294+
| `phoneNumbers` | `[String]?` | Array of the organization's phone numbers |
295+
| `tier` | `String?` | The organization's tier or plan level |
296+
| `customFields` | `[String: Any]?` | Dictionary of custom fields configured in DevRev |
297+
298+
###### AccountTraits
299+
The `AccountTraits` class contains detailed information about the account:
300+
301+
> [!NOTE]
302+
> All properties in `AccountTraits` are optional.
303+
304+
| Property | Type | Description |
305+
|----------|------|-------------|
306+
| `displayName` | `String?` | The displayed name of the account |
307+
| `domains` | `[String]?` | Array of domains associated with the account |
308+
| `accountDescription` | `String?` | A description of the account |
309+
| `phoneNumbers` | `[String]?` | Array of the account's phone numbers |
310+
| `websites` | `[String]?` | Array of websites associated with the account |
311+
| `tier` | `String?` | The account's tier or plan level |
312+
| `customFields` | `[String: Any]?` | Dictionary of custom fields configured in DevRev |
313+
203314
### PLuG support chat
204315
#### UIKit
205316
The support chat feature can be shown as a modal screen from a specific view controller or the top-most one, or can be pushed onto a navigation stack. 
@@ -369,6 +480,40 @@ If any previously masked views need to be unmasked, you can use the following me
369480
DevRev.unmarkSensitiveViews(_:)
370481
```
371482

483+
#### Custom masking provider
484+
For advanced use cases, you can provide a custom masking provider to specify exactly which regions of the UI should be masked in snapshots.
485+
486+
You can implement your own masking logic by conforming to the `DevRev.MaskLocationProviding` protocol and setting your custom object as the masking provider. This allows you to specify explicit regions to be masked or to skip snapshots entirely.
487+
488+
- `DevRev.setMaskingLocationProvider(_ provider: DevRev.MaskLocationProviding)`: Sets the external view masking provider used to determine which areas of the UI should be masked for privacy during snapshots. The provider must conform to the `DevRev.MaskLocationProviding` protocol.
489+
- `DevRev.MaskLocationProviding`: Protocol for providing explicit masking locations for UI snapshots.
490+
- `DevRev.SnapshotMask`: Describes the regions of a snapshot to be masked.
491+
- `DevRev.SnapshotMask.Location`: Describes a masked region.
492+
493+
##### Example
494+
```swift
495+
import Foundation
496+
import UIKit
497+
import DevRevSDK
498+
499+
class MyMaskingProvider: NSObject, DevRev.MaskLocationProviding {
500+
func provideSnapshotMask(_ completionHandler: @escaping (DevRev.SnapshotMask) -> Void) {
501+
// Example: Mask a specific region
502+
let region = CGRect(x: 10, y: 10, width: 100, height: 40)
503+
let location = DevRev.SnapshotMask.Location(location: region)
504+
let mask = DevRev.SnapshotMask(locations: [location], shouldSkip: false)
505+
completionHandler(mask)
506+
}
507+
}
508+
```
509+
510+
```swift
511+
DevRev.setMaskingLocationProvider(MyMaskingProvider())
512+
```
513+
514+
> [!NOTE]
515+
> Setting a new provider will override any previously set masking location provider.
516+
372517
#### Timers
373518
The DevRev SDK offers a timer mechanism to measure the time spent on specific tasks, allowing you to track events such as response time, loading time, or any other duration-based metrics.
374519

Samples/Configuration/Project/ProjectDebug.xcconfig

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,3 @@ GCC_PREPROCESSOR_DEFINITIONS = DEBUG=1 $(inherited)
77
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE
88
SWIFT_ACTIVE_COMPILATION_CONDITIONS = $(inherited) DEBUG
99
SWIFT_OPTIMIZATION_LEVEL = -Onone
10-
SWIFT_COMPILATION_MODE =

Samples/SampleSwiftUI/Components/AsyncButton.swift

Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -14,28 +14,16 @@ struct AsyncButton<Label: View>: View {
1414
Button(
1515
role: role,
1616
action: {
17-
if actionOptions
18-
.contains(
19-
.disableButton
20-
) {
17+
if actionOptions.contains(.disableButton) {
2118
isDisabled = true
2219
}
2320

2421
Task {
25-
var progressViewTask: Task<
26-
Void,
27-
Error
28-
>?
22+
var progressViewTask: Task<Void, Error>?
2923

30-
if actionOptions
31-
.contains(
32-
.showProgressView
33-
) {
24+
if actionOptions.contains(.showProgressView) {
3425
progressViewTask = Task {
35-
try await Task
36-
.sleep(
37-
nanoseconds: 150_000_000
38-
)
26+
try await Task.sleep(nanoseconds: 150_000_000)
3927
showProgressView = true
4028
}
4129
}
@@ -74,9 +62,7 @@ extension AsyncButton where Label == Text {
7462
init(
7563
text: String,
7664
role: ButtonRole? = nil,
77-
actionOptions: Set<ActionOption> = Set(
78-
ActionOption.allCases
79-
),
65+
actionOptions: Set<ActionOption> = .init(ActionOption.allCases),
8066
action: @escaping () async -> Void
8167
) {
8268
self.init(
@@ -93,9 +79,7 @@ extension AsyncButton where Label == Text {
9379
extension AsyncButton where Label == Image {
9480
init(
9581
systemImageName: String,
96-
actionOptions: Set<ActionOption> = Set(
97-
ActionOption.allCases
98-
),
82+
actionOptions: Set<ActionOption> = .init(ActionOption.allCases),
9983
action: @escaping () async -> Void
10084
) {
10185
self.init(

Samples/SampleSwiftUI/Components/RefreshButton.swift

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,10 @@ struct RefreshButton: View {
44
let action: () async -> Void
55

66
var body: some View {
7-
Button(action: {
8-
Task {
9-
await action()
10-
}
11-
}) {
12-
Image(systemName: "arrow.clockwise")
13-
}
7+
AsyncButton(
8+
action: action,
9+
label: { Image(systemName: "arrow.clockwise") }
10+
)
1411
}
1512
}
1613

0 commit comments

Comments
 (0)