Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
228 changes: 228 additions & 0 deletions MIGRATION_GUIDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
# Migration Guide from 2.x.x to 3.0.0

Mindbox SDK 3.0 adds support for React Native New Architecture and no longer supports the old React Native architecture.

This guide describes the required changes when migrating from Mindbox SDK 2.x.x to 3.x.x
## Requirements

- React Native `>=0.76.0`
- React `>=18.0.0`
- React Native New Architecture enabled
- Android min SDK `24`
- iOS `15.1`

## Breaking Changes

### React Native

- The SDK now requires React Native New Architecture.
- The minimum supported React Native version has been raised to `>=0.76.0`.
- The minimum supported React version has been raised to `>=18.0.0`.
- The SDK now uses TurboModule/codegen integration through `NativeMindboxSdk`.
- Deprecated JS APIs have been removed:
- `getToken`
- `updateToken`
- `updateNotificationPermissionStatus`

### Android

- The SDK no longer supports the old React Native architecture. If `newArchEnabled=false`, the build fails.
- `MindboxJsDelivery` is now a Kotlin `object`.
- `MindboxJsDelivery.Shared.getInstance(context)` has been removed.
- Client integrations that pass `Context` or `ReactContext` to `MindboxJsDelivery` must be migrated.
- The minimum Android SDK version has been raised from `21` to `24`.

### iOS

- The SDK no longer supports the old React Native architecture. If `RCT_NEW_ARCH_ENABLED != 1`, the build fails.
- The minimum iOS version has been raised from `12.0` to `15.1`.
- Manual calls to `MindboxJsDelivery` from client code are no longer required.

## React Native API Migration

### `getToken`

`getToken` has been removed. Use `getTokens` instead. The method returns push tokens collected by the SDK.

Before:

```typescript
MindboxSdk.getToken((token: string) => {
// Use FCM/APNS token
})
```

After:

```typescript
MindboxSdk.getTokens((tokens: string) => {
Comment thread
sergeysozinov marked this conversation as resolved.
// Use push tokens returned by the SDK
})
```

### `updateToken`

`updateToken` has been removed. Use native Mindbox SDK methods to pass push tokens.

Android:

```kotlin

Mindbox.updatePushToken(context, token)
```

iOS:

```swift
Mindbox.shared.apnsTokenUpdate(deviceToken: deviceToken)
```

### `updateNotificationPermissionStatus`

`updateNotificationPermissionStatus` has been removed. Use `refreshNotificationPermissionStatus` instead.

Before:

```typescript
MindboxSdk.updateNotificationPermissionStatus(granted)
```

After:

```typescript
MindboxSdk.refreshNotificationPermissionStatus()
```

## Android Migration

Choose one of the options below. Option 1 is recommended.

### Option 1. Simplified Integration With Auto Init

Add the following metadata to the `application` block in `android/app/src/main/AndroidManifest.xml`:

```xml
<meta-data
android:name="com.mindbox.sdk.AUTO_INIT_ENABLED"
android:value="true" />
```

Remove Mindbox client integration code from `MainActivity` and `MainApplication`.

In particular, remove manual code that:

- initializes or stores `MindboxJsDelivery`;
- calls `MindboxJsDelivery.Shared.getInstance(context)`;
- passes `ReactContext` to Mindbox push-click handling;
- manually calls `Mindbox.initPushServices(...)` only for React Native lifecycle integration.

After this change, the SDK handles React Native push-click delivery internally.

### Option 2. Simplified Integration Without AndroidManifest Changes

If you do not want to add `AUTO_INIT_ENABLED` to `AndroidManifest.xml`, remove Mindbox-specific code from `MainActivity` and update `MainApplication`.

Before:

```kotlin
Mindbox.initPushServices(
this,
listOf(MindboxFirebase, MindboxHuawei, MindboxRuStore)
)
```

After:

```kotlin
import com.mindboxsdk.initPushServicesForReactNative

Mindbox.initPushServicesForReactNative(
this,
listOf(MindboxFirebase, MindboxHuawei, MindboxRuStore)
)
```

This initializes push services and registers the React Native lifecycle listener required by the SDK.

### Option 3. Manual MainActivity Migration

If you need to keep manual push-click handling in `MainActivity`, update the integration to the new `MindboxJsDelivery` API.

Before:

```kotlin
private var jsDelivery: MindboxJsDelivery? = null

private fun initializeAndSendIntent(context: ReactContext) {
jsDelivery = MindboxJsDelivery.Shared.getInstance(context)
jsDelivery?.sendPushClicked(intent)
}
```

After:

```kotlin
class MainActivity : ReactActivity() {
private fun handlePushIntent(intent: Intent) {
Mindbox.onNewIntent(intent)
Mindbox.onPushClicked(applicationContext, intent)
MindboxJsDelivery.sendPushClicked(intent)
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
handlePushIntent(intent)
}

override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
handlePushIntent(intent)
}
}
```

## iOS Migration

Existing iOS integrations can continue to work after enabling React Native New Architecture and updating the minimum iOS version. However, the recommended approach is to simplify `AppDelegate` and let the SDK handle notification delivery.

### Recommended Simplified Integration

Keep only the Mindbox-related code shown below in `AppDelegate`.

All other Mindbox code in `AppDelegate` can be removed, including direct calls to `MindboxJsDelivery`.

Add SDK configuration during application startup:

```swift
@main
class AppDelegate: RCTAppDelegate, UNUserNotificationCenterDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
) -> Bool {
....
....
// Set the notification center delegate before configuring Mindbox.
// MindboxApp.configure reads the current delegate during setup.
UNUserNotificationCenter.current().delegate = self
MindboxApp.configure(launchOptions: launchOptions)
....
}
}
```

Warning: if your app calls `MindboxJsDelivery` or `MindboxJSDelivery` directly, remove that code. Manual calls are no longer required. Push-click and in-app event delivery are handled by the SDK.

## Migration Checklist

- Enable React Native New Architecture.
- Update React Native to `>=0.76.0`.
- Update React to `>=18.0.0`.
- Update Android min SDK to `24`.
- Update iOS deployment target to `15.1`.
- Remove usages of `getToken`.
- Replace `getToken` with `getTokens`.
- Remove usages of `updateToken`.
- Replace `updateNotificationPermissionStatus` with `refreshNotificationPermissionStatus`.
- Migrate Android push-click integration to one of the supported options.
- Remove direct `MindboxJsDelivery` calls from iOS client code.
5 changes: 5 additions & 0 deletions android/src/main/java/com/mindboxsdk/MindboxSdkModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,11 @@ class MindboxSdkModule(
payload.optString("previousUuid", "")
)
}
if (payload.has("operationsDomain")) {
configurationBuilder.operationsDomain(
payload.optString("operationsDomain", "")
)
Comment thread
sergeysozinov marked this conversation as resolved.
}
val configuration = configurationBuilder.build()
val handler = Handler(context.mainLooper)
handler.post {
Expand Down
2 changes: 2 additions & 0 deletions ios/MindboxSdkImpl.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ struct PayloadData: Codable {
var shouldCreateCustomer: Bool?
var previousInstallId: String?
var previousUuid: String?
var operationsDomain: String?
}

public typealias ResolveBlock = (Any?) -> Void
Expand Down Expand Up @@ -48,6 +49,7 @@ public final class MindboxSdkImpl: NSObject {
let configuration = try MBConfiguration(
endpoint: payload.endpointId,
domain: payload.domain,
operationsDomain: payload.operationsDomain,
previousInstallationId: payload.previousInstallId,
previousDeviceUUID: payload.previousUuid,
subscribeCustomerIfCreated: payload.subscribeCustomerIfCreated ?? false,
Expand Down
48 changes: 48 additions & 0 deletions src/__tests__/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,54 @@ describe('Testing Mindbox RN SDK', () => {
expect(MindboxSdk.initialized).toBeTruthy()
})

it('initialize passes operationsDomain to native when provided', async () => {
const {
NativeModules: { MindboxSdk: MindboxSdkNative },
} = require('react-native')
const MindboxSdk = require('../index').default

expect.assertions(1)

await MindboxSdk.initialize({
...initializationData,
operationsDomain: 'anonymizer.example.com',
})

const calledWith = (MindboxSdkNative.initialize as jest.Mock).mock.calls.slice(-1)[0][0]
expect(JSON.parse(calledWith)).toMatchObject({ operationsDomain: 'anonymizer.example.com' })
})

it('initialize does not include operationsDomain in payload when not provided', async () => {
const {
NativeModules: { MindboxSdk: MindboxSdkNative },
} = require('react-native')
const MindboxSdk = require('../index').default

expect.assertions(1)

await MindboxSdk.initialize(initializationData)

const calledWith = (MindboxSdkNative.initialize as jest.Mock).mock.calls.slice(-1)[0][0]
expect(JSON.parse(calledWith)).not.toHaveProperty('operationsDomain')
})

it('initialize does not include operationsDomain in payload when passed as empty string', async () => {
const {
NativeModules: { MindboxSdk: MindboxSdkNative },
} = require('react-native')
const MindboxSdk = require('../index').default

expect.assertions(1)

await MindboxSdk.initialize({
...initializationData,
operationsDomain: '',
})

const calledWith = (MindboxSdkNative.initialize as jest.Mock).mock.calls.slice(-1)[0][0]
expect(JSON.parse(calledWith)).not.toHaveProperty('operationsDomain')
})

it('getDeviceUUID method works correctly', async () => {
const MindboxSdk = require('../index').default

Expand Down
20 changes: 6 additions & 14 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ class MindboxSdkClass {
* shouldCreateCustomer: true,
* previousInstallId: '',
* previousUuid: '',
* operationsDomain: 'anonymizer.example.com',
* });
*/
public async initialize(initializationData: InitializationData) {
Expand All @@ -104,7 +105,7 @@ class MindboxSdkClass {
throw new Error('Wrong initialization data!')
}

const { domain, endpointId, subscribeCustomerIfCreated, shouldCreateCustomer, previousInstallId, previousUuid } = initializationData
const { domain, endpointId, subscribeCustomerIfCreated, shouldCreateCustomer, previousInstallId, previousUuid, operationsDomain } = initializationData

if (!domain || !endpointId) {
this._initializing = false
Expand Down Expand Up @@ -132,6 +133,10 @@ class MindboxSdkClass {
payload.previousUuid = previousUuid
}

if (typeof operationsDomain !== 'undefined' && operationsDomain.length > 0) {
payload.operationsDomain = operationsDomain
Comment thread
sergeysozinov marked this conversation as resolved.
}

try {
const payloadString = JSON.stringify(payload)
this._initialized = await MindboxSdkNative.initialize(payloadString)
Expand Down Expand Up @@ -356,19 +361,6 @@ class MindboxSdkClass {
return MindboxSdkNative.pushDelivered(uniqKey)
}

/**
* This method is kept for backward compatibility. The `granted` argument is ignored.
* The SDK reads the current system authorization status and, if it differs
* from the last known value, sends an update to the backend.
*
* @param granted current permission status
* @deprecated Use `refreshNotificationPermissionStatus()` instead.
*/
public updateNotificationPermissionStatus(granted: Boolean) {
console.warn(`updateNotificationPermissionStatus(granted=${String(granted)}) is deprecated. Use refreshNotificationPermissionStatus instead.`)
return MindboxSdkNative.refreshNotificationPermissionStatus()
}

/**
* Checks the current system authorization status for push notifications
* and reports any changes to Mindbox.
Expand Down
1 change: 1 addition & 0 deletions src/types/InitializationData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ export type InitializationData = {
shouldCreateCustomer?: boolean
previousInstallId?: string
previousUuid?: string
operationsDomain?: string
}
Loading