From 1547589fb4fc0857018b9c7510cafa667ac429b9 Mon Sep 17 00:00:00 2001 From: Evan C Masseau <5167687+evan-masseau@users.noreply.github.com> Date: Mon, 12 Feb 2024 13:12:52 -0500 Subject: [PATCH 1/4] Fix mistaken duplicate open push section (#95) Co-authored-by: Evan Masseau <> --- README.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/README.md b/README.md index faff9f9..29e8202 100644 --- a/README.md +++ b/README.md @@ -247,11 +247,6 @@ parameters in the event properties. To track push opens, you will need to follow - [Android](https://github.com/klaviyo/klaviyo-android-sdk#Tracking-Open-Events) - [iOS](https://github.com/klaviyo/klaviyo-swift-sdk#Tracking-Open-Events) -#### Tracking Open Events -To track push notification opens, you must call `Klaviyo.handlePush(intent)` when your app is launched from an intent. -This method will check if the app was opened from a notification originating from Klaviyo and if so, create an -`Opened Push` event with required message tracking parameters. For example: - #### Deep Linking [Deep Links](https://help.klaviyo.com/hc/en-us/articles/14750403974043) allow you to navigate to a particular page within your app in response to the user opening a notification. Familiarize yourself with the From 09471e951ae92b2fd25e57e5c716df9848febf34 Mon Sep 17 00:00:00 2001 From: Evan C Masseau <5167687+evan-masseau@users.noreply.github.com> Date: Mon, 12 Feb 2024 13:13:08 -0500 Subject: [PATCH 2/4] Remove local.properties from git index (#96) * remove local.properties from git index, changes were still being tracked despite gitignore * missed variable --------- Co-authored-by: Evan Masseau <> --- android/build.gradle | 6 +++--- android/{local.properties => local.properties.template} | 5 +++-- .../android/{local.properties => local.properties.template} | 3 ++- 3 files changed, 8 insertions(+), 6 deletions(-) rename android/{local.properties => local.properties.template} (60%) rename example/android/{local.properties => local.properties.template} (71%) diff --git a/android/build.gradle b/android/build.gradle index 1146662..83602eb 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -107,14 +107,14 @@ def localProperties = new Properties() if (rootProject.file("local.properties").canRead()) { localProperties.load(new FileInputStream(rootProject.file("local.properties"))) } -def reactNativeVersion = localProperties['reactNativeAndroidVersion'] ?: "" +def reactNativeAndroidVersion = localProperties['reactNativeAndroidVersion'] ?: "" dependencies { - if (reactNativeVersion) { + if (reactNativeAndroidVersion) { // For local development of the SDK code, specify the react-android version to use // So that the SDK can be built and .kt files are linted against a real version of react-native - implementation "com.facebook.react:react-android:$reactNativeVersion" + implementation "com.facebook.react:react-android:$reactNativeAndroidVersion" } else { // Production build / once embedded in a react-native app, // the react-native version gets loaded in from the application dependencies. diff --git a/android/local.properties b/android/local.properties.template similarity index 60% rename from android/local.properties rename to android/local.properties.template index 509f9fd..847a095 100644 --- a/android/local.properties +++ b/android/local.properties.template @@ -1,7 +1,8 @@ ## Changes in this file must *NOT* be tracked by Version Control Systems, # as it contains information specific to your local configuration. # Only the base template should be checked in to VCS. +# Copy this file to `local.properties` to set your local overrides -## Uncomment for local SDK development, so that gradle can locate the +## Used for local SDK development, so that gradle can locate the # correct RN version outside the context of an application -#reactNativeAndroidVersion=0.73.1 +reactNativeAndroidVersion=0.73.1 diff --git a/example/android/local.properties b/example/android/local.properties.template similarity index 71% rename from example/android/local.properties rename to example/android/local.properties.template index 76dc8c8..50404eb 100644 --- a/example/android/local.properties +++ b/example/android/local.properties.template @@ -2,5 +2,6 @@ # as it contains information specific to your local configuration, # such as your Klaviyo company ID aka "Public API Key". # Only the base template should be checked in to VCS. +# Copy this file to `local.properties` to set your local overrides -#publicApiKey=YOUR_PUBLIC_API_KEY +publicApiKey=YOUR_PUBLIC_API_KEY From 815a893da1a44c771ba427546166736546127724 Mon Sep 17 00:00:00 2001 From: Ajay Subramanya <118314354+ajaysubra@users.noreply.github.com> Date: Thu, 15 Feb 2024 12:11:36 -0600 Subject: [PATCH 3/4] Update README.md (#105) * Update README.md * Update README.md --- README.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 29e8202..7c06866 100644 --- a/README.md +++ b/README.md @@ -255,16 +255,17 @@ instructions below. - [Android](https://github.com/klaviyo/klaviyo-android-sdk#Deep-Linking) instructions for handling intent filters - [iOS](https://github.com/klaviyo/klaviyo-swift-sdk#Deep-Linking) As shown in the native SDK documentation, you can follow option 1 or 2. - With option 1, when you get the callback, you can handle it as follows: + + With option 1, when you handle the open url (in [`application(_:open:options)`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623112-application)), + you call the linking code block above similar to what you would do with option 1. + + With option 2, when you get the `deepLinkHandler`, you can handle it as follows: ```objective-c - [RCTLinkingManager application:application openURL:url options:options] + [RCTLinkingManager application:UIApplication.sharedApplication openURL: url options: @{}]; ``` - Since you won't have `options`, you can just pass in an empty dictionary for that parameter. - - With option 2, when you handle the open url (in [`application(_:open:options)`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623112-application)), - you call the linking code block above similar to what you would do with option 1. + For application, you can pass in an instance of `UIApplication` and since you won't have `options`, you can just pass in an empty dictionary for that parameter. In your React Native code, you can handle the deep link as follows: From ed9a92c3bb90fc168870fb1b7ea17d2e87253c92 Mon Sep 17 00:00:00 2001 From: Ajay Subramanya <118314354+ajaysubra@users.noreply.github.com> Date: Thu, 15 Feb 2024 12:11:52 -0600 Subject: [PATCH 4/4] Implemented deep links to call react native in example iOS app (#104) * 0.1.1 * implemented deep linking calls to react native * removed unused method * removed package lock * added deep linking hanlder --- .../AppDelegate.mm | 27 ++++++++++++++----- .../PushNotificationsHelper.swift | 23 +++++++--------- example/src/App.tsx | 12 +++++++++ 3 files changed, 42 insertions(+), 20 deletions(-) diff --git a/example/ios/KlaviyoReactNativeSdkExample/AppDelegate.mm b/example/ios/KlaviyoReactNativeSdkExample/AppDelegate.mm index 60c5e90..7b66561 100644 --- a/example/ios/KlaviyoReactNativeSdkExample/AppDelegate.mm +++ b/example/ios/KlaviyoReactNativeSdkExample/AppDelegate.mm @@ -1,6 +1,8 @@ #import "AppDelegate.h" #import +#import + @implementation AppDelegate @@ -31,9 +33,8 @@ - (void)application:(UIApplication *)application didRegisterForRemoteNotificatio [PushNotificationsHelper setPushTokenWithToken:deviceToken]; if (isDebug) { - NSString *token = [[deviceToken description] stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]]; - token = [token stringByReplacingOccurrencesOfString:@" " withString:@""]; - NSLog(@"Device Token: %@", token); + NSString *token = [self stringFromDeviceToken:deviceToken]; + NSLog(@"Device Token: %@", token); } } @@ -50,7 +51,11 @@ - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotif - (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)())completionHandler { // Installation Step 10: call `handleReceivingPushWithResponse` method and pass in the below arguments. Note that handleReceivingPushWithResponse calls our SDK and is // something that has to be implemented in your app as well. - [PushNotificationsHelper handleReceivingPushWithResponse:response completionHandler:completionHandler]; + // furthur, if you want to intercept urls instead of them being routed to the system and system calling `application:openURL:options:` you can implement the `deepLinkHandler` below + [PushNotificationsHelper handleReceivingPushWithResponse:response completionHandler:completionHandler deepLinkHandler:^(NSURL * _Nonnull url) { + NSLog(@"URL is %@", url); + [RCTLinkingManager application:UIApplication.sharedApplication openURL: url options: @{}]; + }]; if (isDebug) { UIAlertController *alert = [UIAlertController @@ -90,12 +95,11 @@ - (void)userNotificationCenter:(UNUserNotificationCenter *)center // Installation Step 13: Implement this method to receive deep link. There are some addition setup steps needed as mentioned in the readme here - // https://github.com/klaviyo/klaviyo-swift-sdk?tab=readme-ov-file#deep-linking -// additionally routing to the right screen in the app based on the url is also something that should be handled +// Calling `RCTLinkingManager` is required for your react native listeners to be called - (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary *)options { - return [PushNotificationsHelper handleDeepLinksWithUrl:url]; + return [RCTLinkingManager application:app openURL:url options:options]; } - - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge { return [self getBundleURL]; @@ -110,4 +114,13 @@ - (NSURL *)getBundleURL #endif } +- (NSString *)stringFromDeviceToken:(NSData *)deviceToken { + const unsigned char *tokenBytes = (const unsigned char *)[deviceToken bytes]; + NSMutableString *token = [NSMutableString stringWithCapacity:([deviceToken length] * 2)]; + for (NSUInteger i = 0; i < [deviceToken length]; i++) { + [token appendFormat:@"%02x", tokenBytes[i]]; + } + return token; +} + @end diff --git a/example/ios/KlaviyoReactNativeSdkExample/PushNotificationsHelper.swift b/example/ios/KlaviyoReactNativeSdkExample/PushNotificationsHelper.swift index 81b5c04..b808f89 100644 --- a/example/ios/KlaviyoReactNativeSdkExample/PushNotificationsHelper.swift +++ b/example/ios/KlaviyoReactNativeSdkExample/PushNotificationsHelper.swift @@ -33,21 +33,18 @@ class PushNotificationsHelper: NSObject { } @objc - static func handleReceivingPush(response: UNNotificationResponse, completionHandler: @escaping () -> Void ) { - let handled = KlaviyoSDK().handle(notificationResponse: response, withCompletionHandler: completionHandler) + static func handleReceivingPush( + response: UNNotificationResponse, + completionHandler: @escaping () -> Void, + deepLinkHandler: ((URL) -> Void)? = nil + ) { + let handled = KlaviyoSDK().handle( + notificationResponse: response, + withCompletionHandler: completionHandler, + deepLinkHandler: deepLinkHandler + ) if !handled { completionHandler() } } - - @objc - static func handleDeepLinks(url: URL) -> Bool { - guard let components = NSURLComponents(url: url, resolvingAgainstBaseURL: true) - else { - print("Invalid deep linking URL") - return false - } - print("components: \(components.debugDescription)") - return true - } } diff --git a/example/src/App.tsx b/example/src/App.tsx index 7c586ff..e5a9d49 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -1,10 +1,22 @@ import * as React from 'react'; +import { useEffect } from 'react'; + import { Button, View } from 'react-native'; import { type AppViewInterface, appViews } from './AppViewInterface'; import { styles } from './Styles'; +import { Linking } from 'react-native'; export default function App() { + useEffect(() => { + Linking.addEventListener('url', ({ url }) => { + console.log('Event Listener: url', url); + }); + Linking.getInitialURL().then((url) => { + console.log('Initial Url: url', url); + }); + }, []); + return ( <>