Skip to content

Commit 2f690f4

Browse files
committed
feat(SDK-363): expose registerDeviceToken in React Native bridge
- Add Iterable.registerDeviceToken(token) public API - Add IterableApi.registerDeviceToken bridge proxy - Add TurboModule spec entry in NativeRNIterableAPI.ts - Add iOS Swift/Obj-C++ glue with hex-to-Data conversion for APNS tokens - Add Android impl + old/new architecture stubs for FCM token pass-through - Update MockRNIterableAPI and unit tests - Add CHANGELOG entry under Unreleased
1 parent df0ba48 commit 2f690f4

12 files changed

Lines changed: 111 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
## Unreleased
2+
3+
### Updates
4+
- Added `Iterable.registerDeviceToken(token)` to re-enable push for the current device.
5+
- Android accepts an FCM token string.
6+
- iOS accepts a continuous hex string representation of the APNS token.
7+
18
## 3.0.0
29

310
### Updates

android/src/main/java/com/iterable/reactnative/RNIterableAPIModuleImpl.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,11 @@ public void disableDeviceForCurrentUser() {
373373
IterableApi.getInstance().disablePush();
374374
}
375375

376+
public void registerDeviceToken(String token) {
377+
IterableLogger.v(TAG, "registerDeviceToken");
378+
IterableApi.getInstance().registerDeviceToken(token);
379+
}
380+
376381
public void handleAppLink(String uri, Promise promise) {
377382
IterableLogger.printInfo();
378383
promise.resolve(IterableApi.getInstance().handleAppLink(uri));

android/src/newarch/java/com/RNIterableAPIModule.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,11 @@ public void disableDeviceForCurrentUser() {
137137
moduleImpl.disableDeviceForCurrentUser();
138138
}
139139

140+
@Override
141+
public void registerDeviceToken(String token) {
142+
moduleImpl.registerDeviceToken(token);
143+
}
144+
140145
@Override
141146
public void handleAppLink(String uri, Promise promise) {
142147
moduleImpl.handleAppLink(uri, promise);

android/src/oldarch/java/com/RNIterableAPIModule.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,11 @@ public void disableDeviceForCurrentUser() {
138138
moduleImpl.disableDeviceForCurrentUser();
139139
}
140140

141+
@ReactMethod
142+
public void registerDeviceToken(String token) {
143+
moduleImpl.registerDeviceToken(token);
144+
}
145+
141146
@ReactMethod
142147
public void handleAppLink(String uri, Promise promise) {
143148
moduleImpl.handleAppLink(uri, promise);

ios/RNIterableAPI/RNIterableAPI.mm

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,10 @@ - (void)disableDeviceForCurrentUser {
230230
[_swiftAPI disableDeviceForCurrentUser];
231231
}
232232

233+
- (void)registerDeviceToken:(NSString *)token {
234+
[_swiftAPI registerDeviceToken:token];
235+
}
236+
233237
- (void)getLastPushPayload:(RCTPromiseResolveBlock)resolve
234238
reject:(RCTPromiseRejectBlock)reject {
235239
[_swiftAPI getLastPushPayload:resolve rejecter:reject];
@@ -512,6 +516,10 @@ - (void)wakeApp {
512516
[_swiftAPI disableDeviceForCurrentUser];
513517
}
514518

519+
RCT_EXPORT_METHOD(registerDeviceToken : (NSString *)token) {
520+
[_swiftAPI registerDeviceToken:token];
521+
}
522+
515523
RCT_EXPORT_METHOD(getLastPushPayload : (RCTPromiseResolveBlock)
516524
resolve reject : (RCTPromiseRejectBlock)reject) {
517525
[_swiftAPI getLastPushPayload:resolve rejecter:reject];

ios/RNIterableAPI/ReactIterableAPI.swift

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,16 @@ import React
144144
IterableAPI.disableDeviceForCurrentUser()
145145
}
146146

147+
@objc(registerDeviceToken:)
148+
public func registerDeviceToken(token: String) {
149+
ITBInfo()
150+
guard let tokenData = data(fromHex: token) else {
151+
ITBError("Could not convert token to Data: invalid hex string")
152+
return
153+
}
154+
IterableAPI.register(token: tokenData)
155+
}
156+
147157
@objc(getLastPushPayload:rejecter:)
148158
public func getLastPushPayload(resolver: RCTPromiseResolveBlock, rejecter: RCTPromiseRejectBlock)
149159
{
@@ -599,6 +609,18 @@ import React
599609

600610
private let inboxSessionManager = InboxSessionManager()
601611

612+
private func data(fromHex hex: String) -> Data? {
613+
var data = Data()
614+
var chars = hex.makeIterator()
615+
while let high = chars.next(), let low = chars.next() {
616+
guard let highValue = high.hexDigitValue, let lowValue = low.hexDigitValue else {
617+
return nil
618+
}
619+
data.append(UInt8(highValue << 4 | lowValue))
620+
}
621+
return data
622+
}
623+
602624
@objc func initialize(
603625
withApiKey apiKey: String,
604626
config configDict: NSDictionary,

src/__mocks__/MockRNIterableAPI.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ export class MockRNIterableAPI {
3434

3535
static disableDeviceForCurrentUser = jest.fn();
3636

37+
static registerDeviceToken = jest.fn((token: string): void => {
38+
MockRNIterableAPI.token = token;
39+
});
40+
3741
static trackPushOpenWithCampaignId = jest.fn();
3842

3943
static updateCart = jest.fn();

src/api/NativeRNIterableAPI.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ export interface Spec extends TurboModule {
120120

121121
// Device management
122122
disableDeviceForCurrentUser(): void;
123+
registerDeviceToken(token: string): void;
123124
getLastPushPayload(): Promise<{
124125
[key: string]: string | number | boolean;
125126
} | null>;

src/core/classes/Iterable.test.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,17 @@ describe('Iterable', () => {
142142
});
143143
});
144144

145+
describe('registerDeviceToken', () => {
146+
it('should register the device token for the current user', () => {
147+
// GIVEN a device token
148+
const token = 'test-device-token';
149+
// WHEN Iterable.registerDeviceToken is called
150+
Iterable.registerDeviceToken(token);
151+
// THEN corresponding method is called on RNIterableAPI
152+
expect(MockRNIterableAPI.registerDeviceToken).toBeCalledWith(token);
153+
});
154+
});
155+
145156
describe('getLastPushPayload', () => {
146157
it('should return the last push payload', async () => {
147158
const result = { var1: 'val1', var2: true };

src/core/classes/Iterable.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,26 @@ export class Iterable {
345345
IterableApi.disableDeviceForCurrentUser();
346346
}
347347

348+
/**
349+
* Register the device's token for the current user, re-enabling push notifications.
350+
*
351+
* @param token - The device token to register.
352+
* On Android, pass the Firebase Cloud Messaging (FCM) token string.
353+
* On iOS, pass the Apple Push Notification service (APNS) token as a continuous hex string.
354+
*
355+
* @example
356+
* ```typescript
357+
* // Android
358+
* Iterable.registerDeviceToken('fcm-token-string');
359+
*
360+
* // iOS
361+
* Iterable.registerDeviceToken('abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234');
362+
* ```
363+
*/
364+
static registerDeviceToken(token: string) {
365+
IterableApi.registerDeviceToken(token);
366+
}
367+
348368
/**
349369
* Get the payload of the last push notification with which the user
350370
* opened the application (by clicking an action button, etc.).

0 commit comments

Comments
 (0)