From 98b3df82bdbef7db17f23ab3965bdf41096e776c Mon Sep 17 00:00:00 2001 From: Daniel Hok Date: Mon, 13 Mar 2023 16:48:50 -0400 Subject: [PATCH] Release version 3.0.0 --- BrazeProject/BrazeProject.tsx | 6 +- BrazeProject/ios/Podfile.lock | 28 ++-- CHANGELOG.md | 24 ++++ README.md | 9 ++ android/build.gradle | 2 +- braze-react-native-sdk.podspec | 6 +- .../BrazeReactBridge/BrazeReactBridge.m | 136 +++++------------- package.json | 2 +- src/index.d.ts | 2 +- 9 files changed, 92 insertions(+), 123 deletions(-) diff --git a/BrazeProject/BrazeProject.tsx b/BrazeProject/BrazeProject.tsx index 2ba8e4d..ba5b4c2 100644 --- a/BrazeProject/BrazeProject.tsx +++ b/BrazeProject/BrazeProject.tsx @@ -437,11 +437,11 @@ export const BrazeProject = (): ReactElement => { } if (cards == null || cards.length === 0) { - console.log('No cached Content Cards Found.'); + console.log('No Content Cards Found.'); return; } - console.log(`${cards.length} cached Content Cards found.`); + console.log(`${cards.length} Content Cards found.`); for (const card of cards) { const cardId = card.id; console.log(`Got content card: ${JSON.stringify(card)}`); @@ -593,7 +593,7 @@ export const BrazeProject = (): ReactElement => { Request Content Cards Refresh - Get Cached Content Cards {'&'} Log interactions + Get Content Cards {'&'} Log interactions {/* News Feed */} diff --git a/BrazeProject/ios/Podfile.lock b/BrazeProject/ios/Podfile.lock index 6371ce3..9933564 100644 --- a/BrazeProject/ios/Podfile.lock +++ b/BrazeProject/ios/Podfile.lock @@ -1,15 +1,15 @@ PODS: - boost (1.76.0) - - braze-react-native-sdk (2.1.0): - - BrazeKit (~> 5.9.1) - - BrazeLocation (~> 5.9.1) - - BrazeUI (~> 5.9.1) + - braze-react-native-sdk (2.2.0): + - BrazeKit (~> 5.11.2) + - BrazeLocation (~> 5.11.2) + - BrazeUI (~> 5.11.2) - React-Core - - BrazeKit (5.9.1) - - BrazeLocation (5.9.1): - - BrazeKit (= 5.9.1) - - BrazeUI (5.9.1): - - BrazeKit (= 5.9.1) + - BrazeKit (5.11.2) + - BrazeLocation (5.11.2): + - BrazeKit (= 5.11.2) + - BrazeUI (5.11.2): + - BrazeKit (= 5.11.2) - CocoaAsyncSocket (7.6.5) - DoubleConversion (1.1.6) - FBLazyVector (0.71.2) @@ -590,10 +590,10 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: boost: 57d2868c099736d80fcd648bf211b4431e51a558 - braze-react-native-sdk: 4a25d1021bf5c994ffb5509161e49a8064f1e8a2 - BrazeKit: 7c8ab19c996bb6447d44a242c14778ad48c9e020 - BrazeLocation: ba8da78df3221b627c56c4b2ad20df2e0795dfb4 - BrazeUI: 39d905f9ded70b6ab9d8ab08d6cfdbbfed4cc282 + braze-react-native-sdk: f7ab95b1b11273530e8a7e9c456761637fb4be4c + BrazeKit: 5502faeac539dffd14ad403afbf7d26943060d20 + BrazeLocation: 3d81f3c36f66939be40ca78ce83a677ff9af1711 + BrazeUI: a1d2bcddbb18c015efd75289584a9a9f7341a38e CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99 DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54 FBLazyVector: d58428b28fe1f5070fe993495b0e2eaf701d3820 @@ -646,4 +646,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: a5a205f87842532db01e7cc5eee03c5db5604934 -COCOAPODS: 1.11.3 +COCOAPODS: 1.12.0 diff --git a/CHANGELOG.md b/CHANGELOG.md index f05a5fa..80df141 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,26 @@ +# 3.0.0 + +> Starting with this release, this SDK will use [Semantic Versioning](https://semver.org/). + +##### ⚠ Breaking +- Fixes the behavior in the iOS bridge introduced in version `2.0.0` when logging clicks for in-app messages, content cards, and news feed cards. Calling `logClick` now only sends a click event for metrics, instead of both sending a click event as well as redirecting to the associated `url` field. + - For instance, to log a content card click and redirect to a URL, you will need two commands: + ``` + Braze.logContentCardClicked(contentCard.id); + + // Your own custom implementation + Linking.openUrl(contentCard.url); + ``` + - This brings the iOS behavior to match version `1.x` and bring parity with Android's behavior. + +##### Fixed +- Fixes an issue in the iOS bridge introduced in `2.0.0` where `getContentCards()` and `getNewsFeedCards()` would return an array of cards with the `url` and `image` fields as `null`. + +##### Changed +- Updates the native iOS bridge to [Braze Swift SDK 5.11.2](https://github.com/braze-inc/braze-swift-sdk/blob/main/CHANGELOG.md#5112). +- Updates the native Android bridge to [Braze Android SDK 24.3.0](https://github.com/Appboy/appboy-android-sdk/blob/master/CHANGELOG.md#2430). +- Updates `getContentCards` on the iOS bridge to initiate a refresh before returning the array of Content Cards. This brings parity with the Android bridge behavior. + # 2.1.0 ##### Added @@ -22,6 +45,7 @@ ##### ⚠ Breaking - The Braze React Native SDK npm package has moved from `react-native-appboy-sdk` to `@braze/react-native-sdk`. - Renames `AppboyReactBridge` and `AppboyReactUtils` to `BrazeReactBridge` and `BrazeReactUtils`, respectively. +- This version requires React Native `0.68` or higher. - Updates the native iOS bridge to use the new Swift SDK [version 5.9.1](https://github.com/braze-inc/braze-swift-sdk/blob/main/CHANGELOG.md#591). - During migration, update your project with the following changes in your iOS integration: - To initialize Braze, [follow these integration steps](https://braze-inc.github.io/braze-swift-sdk/tutorials/braze/a2-configure-braze) to create a `configuration` object. Then, add this code to complete the setup: diff --git a/README.md b/README.md index 672eada..eed1380 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,15 @@ Effective marketing automation is an essential part of successfully scaling and - [Braze User Guide](https://www.braze.com/docs/user_guide/introduction) - [Initial React Native SDK Setup](https://www.braze.com/docs/developer_guide/platform_integration_guides/react_native/react_sdk_setup/) +## Version Support + +| Braze Plugin | React Native | +| ------------ | ------------ | +| 2.0.0+ | >= 0.68 | +| <= 1.41.0 | <= 0.71 | + +> Support for React Native 0.69+ (New Architecture) is on the feature roadmap. + ## Braze Expo Plugin If you're using Expo, you can install our plugin to integrate the React Native SDK without any native code. See the [Braze Expo Plugin Github](https://github.com/braze-inc/braze-expo-plugin) for more details. diff --git a/android/build.gradle b/android/build.gradle index 5ca8333..d0a5d87 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -28,7 +28,7 @@ android { } dependencies { - api 'com.appboy:android-sdk-ui:24.2.0' + api 'com.appboy:android-sdk-ui:24.3.0' api 'com.facebook.react:react-native:+' implementation "org.jetbrains.kotlin:kotlin-stdlib:1.6.0" } diff --git a/braze-react-native-sdk.podspec b/braze-react-native-sdk.podspec index 1871a90..a33b480 100644 --- a/braze-react-native-sdk.podspec +++ b/braze-react-native-sdk.podspec @@ -18,9 +18,9 @@ Pod::Spec.new do |s| s.preserve_paths = 'LICENSE.md', 'README.md', 'package.json', 'index.js', 'iOS/replace-at-import-statements.sh' s.source_files = 'iOS/**/*.{h,m}' - s.dependency 'BrazeKit', '~> 5.9.1' - s.dependency 'BrazeLocation', '~> 5.9.1' - s.dependency 'BrazeUI', '~> 5.9.1' + s.dependency 'BrazeKit', '~> 5.11.2' + s.dependency 'BrazeLocation', '~> 5.11.2' + s.dependency 'BrazeUI', '~> 5.11.2' s.dependency 'React-Core' diff --git a/iOS/BrazeReactBridge/BrazeReactBridge/BrazeReactBridge.m b/iOS/BrazeReactBridge/BrazeReactBridge/BrazeReactBridge.m index b54d41f..7a13737 100644 --- a/iOS/BrazeReactBridge/BrazeReactBridge/BrazeReactBridge.m +++ b/iOS/BrazeReactBridge/BrazeReactBridge/BrazeReactBridge.m @@ -464,12 +464,20 @@ - (NSArray *)parseArray:(NSArray *)array { } - (NSArray *)getMappedNewsFeedCards { - NSArray *cards = braze.newsFeed.cards; - NSMutableArray *mappedCards = [NSMutableArray arrayWithCapacity:[cards count]]; - [cards enumerateObjectsUsingBlock:^(id card, NSUInteger idx, BOOL *stop) { - [mappedCards addObject:RCTFormatNewsFeedCard(card)]; + NSArray *newsFeedCards = braze.newsFeed.cards; + NSMutableArray *mappedNewsFeedCards = [NSMutableArray arrayWithCapacity:[newsFeedCards count]]; + [newsFeedCards enumerateObjectsUsingBlock:^(BRZNewsFeedCard *card, NSUInteger idx, BOOL *stop) { + NSError* error = nil; + id cardJSON = [NSJSONSerialization JSONObjectWithData:[card json] + options:NSJSONReadingMutableContainers + error:&error]; + if (error) { + RCTLogInfo(@"Unable to parse News Feed Card: %@, error: %@. Skipping.", card, error); + } else { + [mappedNewsFeedCards addObject:cardJSON]; + } }]; - return mappedCards; + return mappedNewsFeedCards; } - (nullable BRZNewsFeedCard *)getNewsFeedCardById:(NSString *)idString { @@ -483,44 +491,6 @@ - (nullable BRZNewsFeedCard *)getNewsFeedCardById:(NSString *)idString { return nil; } -static NSDictionary *RCTFormatNewsFeedCard(BRZNewsFeedCard *card) { - NSMutableDictionary *newsFeedCardData = [NSMutableDictionary dictionary]; - newsFeedCardData[@"id"] = card.identifier; - newsFeedCardData[@"created"] = @(card.created); - newsFeedCardData[@"expiresAt"] = @(card.expires); - newsFeedCardData[@"viewed"] = @(card.viewed); - newsFeedCardData[@"url"] = RCTNullIfNil(card.url); - newsFeedCardData[@"openURLInWebView"] = @(card.useWebView); - newsFeedCardData[@"extras"] = card.extras ? RCTJSONClean(card.extras) : @{}; - - switch (card.type) { - case BRZNewsFeedCardTypeBanner: - newsFeedCardData[@"image"] = card.image; - newsFeedCardData[@"imageAspectRatio"] = @(card.imageAspectRatio); - newsFeedCardData[@"domain"] = RCTNullIfNil(card.domain); - newsFeedCardData[@"type"] = @"Banner"; - case BRZNewsFeedCardTypeCaptionedImage: - newsFeedCardData[@"image"] = card.image; - newsFeedCardData[@"imageAspectRatio"] = @(card.imageAspectRatio); - newsFeedCardData[@"title"] = card.title; - newsFeedCardData[@"cardDescription"] = card.cardDescription; - newsFeedCardData[@"domain"] = RCTNullIfNil(card.domain); - newsFeedCardData[@"type"] = @"Captioned"; - case BRZNewsFeedCardTypeClassic: - newsFeedCardData[@"image"] = card.image; - newsFeedCardData[@"cardDescription"] = card.cardDescription; - newsFeedCardData[@"title"] = card.title; - newsFeedCardData[@"domain"] = card.domain; - newsFeedCardData[@"type"] = @"Classic"; - case BRZNewsFeedCardTypeTextAnnouncement: - newsFeedCardData[@"title"] = card.title; - newsFeedCardData[@"cardDescription"] = card.cardDescription; - newsFeedCardData[@"domain"] = RCTNullIfNil(card.domain); - newsFeedCardData[@"type"] = @"TextAnnouncement"; - } - return newsFeedCardData; -} - #pragma mark - Content Cards /// Returns the content card for the associated id, or nil if not found. @@ -545,7 +515,29 @@ - (nullable BRZContentCardRaw *)getContentCardById:(NSString *)idString { RCT_REMAP_METHOD(getContentCards, getContentCardsWithResolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { RCTLogInfo(@"getContentCards called"); - resolve([self getMappedContentCards]); + + [braze.contentCards requestRefreshWithCompletion:^( + NSArray *_Nullable cards, + NSError *_Nullable refreshError) { + if (refreshError) { + RCTLogInfo(@"Error during Content Card refresh: %@", refreshError); + } + + NSMutableArray *mappedContentCards = [NSMutableArray arrayWithCapacity:[cards count]]; + [cards enumerateObjectsUsingBlock:^(BRZContentCardRaw *card, NSUInteger idx, BOOL *stop) { + NSError* parsingError = nil; + id cardJSON = [NSJSONSerialization JSONObjectWithData:[card json] + options:NSJSONReadingMutableContainers + error:&parsingError]; + if (parsingError) { + RCTLogInfo(@"Unable to parse Content Card: %@, error: %@. Skipping.", card, parsingError); + } else { + [mappedContentCards addObject:cardJSON]; + } + }]; + + resolve(mappedContentCards); + }]; } RCT_EXPORT_METHOD(logContentCardClicked:(NSString *)idString) { @@ -572,61 +564,6 @@ - (nullable BRZContentCardRaw *)getContentCardById:(NSString *)idString { } } -- (NSArray *)getMappedContentCards { - NSArray *cards = braze.contentCards.cards; - NSMutableArray *mappedCards = [NSMutableArray arrayWithCapacity:[cards count]]; - [cards enumerateObjectsUsingBlock:^(id card, NSUInteger idx, BOOL *stop) { - [mappedCards addObject:RCTFormatContentCard(card)]; - }]; - return mappedCards; -} - -static NSDictionary *RCTFormatContentCard(BRZContentCardRaw *card) { - NSMutableDictionary *formattedContentCardData = [NSMutableDictionary dictionary]; - - formattedContentCardData[@"id"] = card.identifier; - formattedContentCardData[@"created"] = @(card.createdAt); - formattedContentCardData[@"expiresAt"] = @(card.expiresAt); - formattedContentCardData[@"viewed"] = @(card.viewed); - formattedContentCardData[@"clicked"] = @(card.clicked); - formattedContentCardData[@"pinned"] = @(card.pinned); - formattedContentCardData[@"dismissed"] = @(card.removed); - formattedContentCardData[@"dismissible"] = @(card.dismissible); - formattedContentCardData[@"url"] = RCTNullIfNil(card.url); - formattedContentCardData[@"openURLInWebView"] = @(card.useWebView); - formattedContentCardData[@"isControl"] = @(NO); - - formattedContentCardData[@"extras"] = card.extras ? RCTJSONClean(card.extras) : @{}; - - switch (card.type) { - case BRZContentCardRawTypeCaptionedImage: - formattedContentCardData[@"image"] = card.image; - formattedContentCardData[@"imageAspectRatio"] = @(card.imageAspectRatio); - formattedContentCardData[@"title"] = card.title; - formattedContentCardData[@"cardDescription"] = card.cardDescription; - formattedContentCardData[@"domain"] = RCTNullIfNil(card.domain); - formattedContentCardData[@"type"] = @"Captioned"; - break; - case BRZContentCardRawTypeBanner: - formattedContentCardData[@"image"] = card.image; - formattedContentCardData[@"imageAspectRatio"] = @(card.imageAspectRatio); - formattedContentCardData[@"type"] = @"Banner"; - break; - case BRZContentCardRawTypeClassic: - formattedContentCardData[@"image"] = RCTNullIfNil(card.image); - formattedContentCardData[@"title"] = card.title; - formattedContentCardData[@"cardDescription"] = card.cardDescription; - formattedContentCardData[@"domain"] = RCTNullIfNil(card.domain); - formattedContentCardData[@"type"] = @"Classic"; - break; - case BRZContentCardRawTypeControl: - formattedContentCardData[@"isControl"] = @(YES); - break; - } - - return formattedContentCardData; -} - #pragma mark - Other bindings RCT_EXPORT_METHOD(wipeData) { @@ -799,7 +736,6 @@ - (BRZInAppMessageRaw *)getInAppMessageFromString:(NSString *)inAppMessageJSONSt NSData *inAppMessageData = [inAppMessageJSONString dataUsingEncoding:NSUTF8StringEncoding]; BRZInAppMessageRaw *message = [BRZInAppMessageRaw decodingWithJson:inAppMessageData]; if (message) { - message.context = [[BRZInAppMessageContext alloc] initWithMessageRaw:message using: braze]; return message; } return nil; diff --git a/package.json b/package.json index 45a66f4..93e5a07 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@braze/react-native-sdk", - "version": "2.1.0", + "version": "3.0.0", "description": "Braze SDK for React Native.", "main": "src/index.js", "types": "src/index.d.ts", diff --git a/src/index.d.ts b/src/index.d.ts index 2d2c13e..8867f66 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -466,7 +466,7 @@ export function logContentCardDismissed(id: string): void; export function logContentCardImpression(id: string): void; /** - * Returns a content cards array + * Performs a refresh and then returns a content cards array. * @returns {Promise} */ export function getContentCards(): Promise;