Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,9 @@ docs/
# Local Netlify folder
.netlify
.metals/

# Agents
.agent/
.claude/
.cursor/
.pi/
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
## Unreleased

### Updates
- Added `Iterable.registerDeviceToken(token)` to re-enable push for the current device.
- Android accepts an FCM token string.
- iOS accepts a continuous hex string representation of the APNS token.

## 3.0.0

### Updates
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,11 @@ public void disableDeviceForCurrentUser() {
IterableApi.getInstance().disablePush();
}

public void registerDeviceToken(String token) {
IterableLogger.v(TAG, "registerDeviceToken");
IterableApi.getInstance().registerDeviceToken(token);
}

public void handleAppLink(String uri, Promise promise) {
IterableLogger.printInfo();
promise.resolve(IterableApi.getInstance().handleAppLink(uri));
Expand Down
5 changes: 5 additions & 0 deletions android/src/newarch/java/com/RNIterableAPIModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,11 @@ public void disableDeviceForCurrentUser() {
moduleImpl.disableDeviceForCurrentUser();
}

@Override
public void registerDeviceToken(String token) {
moduleImpl.registerDeviceToken(token);
}

@Override
public void handleAppLink(String uri, Promise promise) {
moduleImpl.handleAppLink(uri, promise);
Expand Down
5 changes: 5 additions & 0 deletions android/src/oldarch/java/com/RNIterableAPIModule.java
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,11 @@ public void disableDeviceForCurrentUser() {
moduleImpl.disableDeviceForCurrentUser();
}

@ReactMethod
public void registerDeviceToken(String token) {
moduleImpl.registerDeviceToken(token);
}

@ReactMethod
public void handleAppLink(String uri, Promise promise) {
moduleImpl.handleAppLink(uri, promise);
Expand Down
5 changes: 2 additions & 3 deletions example/ios/ReactNativeSdkExample.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
77E3B5772EA71A4B001449CE /* IterableJwtGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77E3B5742EA71A4B001449CE /* IterableJwtGenerator.swift */; };
77E3B5782EA71A4B001449CE /* JwtTokenModule.mm in Sources */ = {isa = PBXBuildFile; fileRef = 77E3B5752EA71A4B001449CE /* JwtTokenModule.mm */; };
77E3B5792EA71A4B001449CE /* JwtTokenModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 77E3B5762EA71A4B001449CE /* JwtTokenModule.swift */; };
7C8CB9778D44155D232C3690 /* libPods-ReactNativeSdkExample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FC8A71BC6B8F9B2B3CF98A77 /* libPods-ReactNativeSdkExample.a */; };
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
A3A40C20801B8F02005FA4C0 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 1FC6B09E65A7BD9F6864C5D8 /* PrivacyInfo.xcprivacy */; };
DDD9C96E1785FEF10EEE61A5 /* libPods-ReactNativeSdkExample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 627B7C7165CE568DB5CB8F50 /* libPods-ReactNativeSdkExample.a */; };
Expand Down Expand Up @@ -439,7 +438,7 @@
"-ObjC",
"-lc++",
);
PRODUCT_BUNDLE_IDENTIFIER = iterable.reactnativesdk.example;
PRODUCT_BUNDLE_IDENTIFIER = com.iterable.reactnativesdk.example;
PRODUCT_NAME = ReactNativeSdkExample;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SWIFT_OBJC_BRIDGING_HEADER = "ReactNativeSdkExample-Bridging-Header.h";
Expand Down Expand Up @@ -470,7 +469,7 @@
"-ObjC",
"-lc++",
);
PRODUCT_BUNDLE_IDENTIFIER = iterable.reactnativesdk.example;
PRODUCT_BUNDLE_IDENTIFIER = com.iterable.reactnativesdk.example;
PRODUCT_NAME = ReactNativeSdkExample;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SWIFT_OBJC_BRIDGING_HEADER = "ReactNativeSdkExample-Bridging-Header.h";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict/>
<dict>
<key>aps-environment</key>
<string>development</string>
</dict>
</plist>
8 changes: 8 additions & 0 deletions ios/RNIterableAPI/RNIterableAPI.mm
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,10 @@ - (void)disableDeviceForCurrentUser {
[_swiftAPI disableDeviceForCurrentUser];
}

- (void)registerDeviceToken:(NSString *)token {
[_swiftAPI registerDeviceToken:token];
}

- (void)getLastPushPayload:(RCTPromiseResolveBlock)resolve
reject:(RCTPromiseRejectBlock)reject {
[_swiftAPI getLastPushPayload:resolve rejecter:reject];
Expand Down Expand Up @@ -512,6 +516,10 @@ - (void)wakeApp {
[_swiftAPI disableDeviceForCurrentUser];
}

RCT_EXPORT_METHOD(registerDeviceToken : (NSString *)token) {
[_swiftAPI registerDeviceToken:token];
}

RCT_EXPORT_METHOD(getLastPushPayload : (RCTPromiseResolveBlock)
resolve reject : (RCTPromiseRejectBlock)reject) {
[_swiftAPI getLastPushPayload:resolve rejecter:reject];
Expand Down
23 changes: 23 additions & 0 deletions ios/RNIterableAPI/ReactIterableAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,16 @@ import React
IterableAPI.disableDeviceForCurrentUser()
}

@objc(registerDeviceToken:)
public func registerDeviceToken(token: String) {
ITBInfo()
guard let tokenData = data(fromHex: token) else {
Comment thread
jferrao-itrbl marked this conversation as resolved.
ITBError("Could not convert token to Data: invalid hex string")
return
}
IterableAPI.register(token: tokenData)
}

@objc(getLastPushPayload:rejecter:)
public func getLastPushPayload(resolver: RCTPromiseResolveBlock, rejecter: RCTPromiseRejectBlock)
{
Expand Down Expand Up @@ -599,6 +609,19 @@ import React

private let inboxSessionManager = InboxSessionManager()

private func data(fromHex hex: String) -> Data? {
Comment thread
jferrao-itrbl marked this conversation as resolved.
guard !hex.isEmpty, hex.count.isMultiple(of: 2) else { return nil }
var data = Data(capacity: hex.count / 2)
var chars = hex.makeIterator()
while let high = chars.next(), let low = chars.next() {
guard let highValue = high.hexDigitValue, let lowValue = low.hexDigitValue else {
Comment thread
jferrao-itrbl marked this conversation as resolved.
return nil
}
data.append(UInt8(highValue << 4 | lowValue))
}
return data
}

@objc func initialize(
withApiKey apiKey: String,
config configDict: NSDictionary,
Expand Down
4 changes: 4 additions & 0 deletions src/__mocks__/MockRNIterableAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ export class MockRNIterableAPI {

static disableDeviceForCurrentUser = jest.fn();

static registerDeviceToken = jest.fn((token: string): void => {
MockRNIterableAPI.token = token;
});

static trackPushOpenWithCampaignId = jest.fn();

static updateCart = jest.fn();
Expand Down
1 change: 1 addition & 0 deletions src/api/NativeRNIterableAPI.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ export interface Spec extends TurboModule {

// Device management
disableDeviceForCurrentUser(): void;
registerDeviceToken(token: string): void;
getLastPushPayload(): Promise<{
[key: string]: string | number | boolean;
} | null>;
Expand Down
11 changes: 11 additions & 0 deletions src/core/classes/Iterable.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,17 @@ describe('Iterable', () => {
});
});

describe('registerDeviceToken', () => {
it('should register the device token for the current user', () => {
// GIVEN a device token
const token = 'test-device-token';
// WHEN Iterable.registerDeviceToken is called
Iterable.registerDeviceToken(token);
// THEN corresponding method is called on RNIterableAPI
expect(MockRNIterableAPI.registerDeviceToken).toBeCalledWith(token);
});
});

describe('getLastPushPayload', () => {
it('should return the last push payload', async () => {
const result = { var1: 'val1', var2: true };
Expand Down
20 changes: 20 additions & 0 deletions src/core/classes/Iterable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,26 @@ export class Iterable {
IterableApi.disableDeviceForCurrentUser();
}

/**
* Register the device's token for the current user, re-enabling push notifications.
*
* @param token - The device token to register.
* On Android, pass the Firebase Cloud Messaging (FCM) token string.
* On iOS, pass the Apple Push Notification service (APNS) token as a continuous hex string.
*
* @example
* ```typescript
* // Android
* Iterable.registerDeviceToken('fcm-token-string');
*
* // iOS
* Iterable.registerDeviceToken('abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234abcd1234');
* ```
*/
static registerDeviceToken(token: string) {
IterableApi.registerDeviceToken(token);
}

/**
* Get the payload of the last push notification with which the user
* opened the application (by clicking an action button, etc.).
Expand Down
13 changes: 13 additions & 0 deletions src/core/classes/IterableApi.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,19 @@ describe('IterableApi', () => {
});
});

describe('registerDeviceToken', () => {
it('should call RNIterableAPI.registerDeviceToken with the token', () => {
// GIVEN a device token
const token = 'test-device-token';

// WHEN registerDeviceToken is called
IterableApi.registerDeviceToken(token);

// THEN RNIterableAPI.registerDeviceToken is called with the token
expect(MockRNIterableAPI.registerDeviceToken).toBeCalledWith(token);
});
});

describe('updateUser', () => {
it('should call RNIterableAPI.updateUser with data fields and merge flag', () => {
// GIVEN data fields and merge flag
Expand Down
10 changes: 10 additions & 0 deletions src/core/classes/IterableApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,16 @@ export class IterableApi {
return RNIterableAPI.disableDeviceForCurrentUser();
}

/**
* Register the device token for the current user, re-enabling push notifications.
*
* @param token - On Android, the FCM token string. On iOS, a continuous hex string representation of the APNS token.
*/
static registerDeviceToken(token: string) {
IterableLogger.log('registerDeviceToken');
return RNIterableAPI.registerDeviceToken(token);
}

/**
* Save data to the current user's Iterable profile.
*
Expand Down
Loading