Skip to content

Commit fc56408

Browse files
Version 2.0.0 Staging Branch (#217)
* Bump plist, package.json, xml to 1.3.0 * Chore | Yarn lockfile (#223) * Had to run a yarn install to update lockfile (and husky i guess... still don't really know what husky is other than a dog) * Husky got removed from dev dependencies causing new setups to be unable to add pre-commit hooks * Required now --------- Co-authored-by: Evan Masseau <> * IAF | Update to the pending native releases (#222) * Get the code compiling * Use configure-sdk script to set to latest native SDK commit hashes for pending releases. * Fix the config script to add all three pods. Update again * pods must be added inside this block, so i added a comment to target the command to. with this, the ios sample compiles --------- Co-authored-by: Evan Masseau <> * Disable local copy on switch to remote! * Fix configure local script for swift SDK (#226) * Was too fixated on the git branch/commit path, didn't notice I hadn't fixed the local SDK script * comments --------- Co-authored-by: Evan Masseau <> * IAF | Android pre-release updates (#228) * Android cleanup * forgot to switch to latest android sdk commit for compiling in CI --------- Co-authored-by: Evan Masseau <> * Bump example to the latest swift sdk. * IAF | Android constants fix (#229) ProfileKey class now has a companion object inside, which broke our constants extractor. This fixes it so that we're filtering down by type <ProfileKey> Co-authored-by: Evan Masseau <> * Export FormConfiguration type (#232) * SDK Version bumps: React to 2.0.0, Android to 4.0.0, Swift to 5.0.0 (#231) * bump to major versions * add in ios, fix some script errors since I didn't fully test this one (oops) * Update to use final release numbers * Rerun with published pods * use bundler --------- Co-authored-by: Evan Masseau <> Co-authored-by: Isobelle Lim <[email protected]> * Update README and add MIGRATION_GUIDE (#230) * Update README and add MIGRATION_GUIDE * generalize useEffect callout * Bump to major version * Undo version bumps * Update to match iOS/Android READMEs --------- Co-authored-by: Evan C Masseau <[email protected]> Co-authored-by: Evan Masseau <>
1 parent e74ffb6 commit fc56408

25 files changed

Lines changed: 1247 additions & 1111 deletions

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ on:
44
pull_request:
55
branches:
66
- master
7+
- rel/*
78

89
permissions:
910
contents: write

.husky/pre-commit

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,12 @@ call_lefthook()
5151
elif command -v mint >/dev/null 2>&1
5252
then
5353
mint run csjones/lefthook-plugin "$@"
54+
elif uv run lefthook -h >/dev/null 2>&1
55+
then
56+
uv run lefthook "$@"
57+
elif mise exec -- lefthook -h >/dev/null 2>&1
58+
then
59+
mise exec -- lefthook "$@"
5460
else
5561
echo "Can't find lefthook in PATH"
5662
fi

.husky/prepare-commit-msg

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,12 @@ call_lefthook()
5151
elif command -v mint >/dev/null 2>&1
5252
then
5353
mint run csjones/lefthook-plugin "$@"
54+
elif uv run lefthook -h >/dev/null 2>&1
55+
then
56+
uv run lefthook "$@"
57+
elif mise exec -- lefthook -h >/dev/null 2>&1
58+
then
59+
mise exec -- lefthook "$@"
5460
else
5561
echo "Can't find lefthook in PATH"
5662
fi

MIGRATION_GUIDE.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# React Native SDK Migration Guide
2+
3+
This guide outlines how developers can migrate from older versions of our SDK to newer ones.
4+
5+
## Migrating to v2.0.0
6+
7+
### In-App Forms
8+
9+
As a result of changes summarized below, you may wish to revisit the logic of when you call `registerForInAppForms()` when upgrading from 1.2.0, particularly if you were registering than once per application session. Consult the [README](./README.md#in-app-forms) for the latest integration instructions.
10+
11+
#### Updated behaviors
12+
13+
- In version 1.2.0, calling `registerForInAppForms()` functioned like a "fetch" that would check if a form was available and if yes, display it. Version 2.0.0 changes this behavior so that `registerForInAppForms()` sets up a persistent listener that will be ready to display a form if and when one is targeted to the current profile.
14+
- A deep link from an In-App Form will now be issued _after_ the form has closed, instead of during the close animation in order to prevent a race condition if the host application expects the form to be closed before handling the deep link.
15+
16+
#### Configurable In-App Form session timeout
17+
18+
Introduced a configurable session timeout for In-App Forms, which defaults to 60 minutes, as an optional argument to `registerForInAppForms()`.
19+
20+
#### New `unregisterFromInAppForms()` method
21+
22+
Because the `registerForInAppForms()` method now functions as a persistent listener rather than a "fetch", we've introduced an [`unregisterFromInAppForms()` method](./README.md#unregister-from-in-app-forms) so you can stop listening for In-App Forms at appropriate times, such as when a user logs out.

README.md

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@
3737
- [In-App Forms](#in-app-forms)
3838
- [Prerequisites](#prerequisites-1)
3939
- [Setup](#setup-1)
40-
- [Behavior](#behavior)
40+
- [In-App Forms Session Configuration](#in-app-forms-session-configuration)
41+
- [Unregistering from In-App Forms](#unregistering-from-in-app-forms)
4142
- [Troubleshooting](#troubleshooting)
4243
- [Contributing](#contributing)
4344
- [License](#license)
@@ -498,16 +499,28 @@ Klaviyo messages can also include key-value pairs (custom data) for both standar
498499

499500
## In-App Forms
500501

501-
[In-app forms](https://help.klaviyo.com/hc/en-us/articles/34567685177883) are messages displayed to mobile app users while they are actively using an app. You can create new in-app forms in a drag-and-drop editor in the Sign-Up Forms tab in Klaviyo. Follow the instructions in this section to integrate forms with your app. The SDK will display forms according to their targeting and behavior settings and collect delivery and engagement analytics automatically.
502+
[In-App Forms](https://help.klaviyo.com/hc/en-us/articles/34567685177883) are messages displayed to mobile app users while they are actively using an app. You can create new In-App Forms in a drag-and-drop editor in the Sign-Up Forms tab in Klaviyo. Follow the instructions in this section to integrate forms with your app. The SDK will display forms according to their targeting and behavior settings and collect delivery and engagement analytics automatically.
503+
504+
Beginning with version 2.0.0, In-App Forms supports advanced targeting and segmentation. In your Klaviyo account, you can configure forms to target or exclude specific lists or segments, and the form will only be shown to users matching those criteria, based on their profile identifiers set via the Klaviyo SDK API.
502505

503506
### Prerequisites
504507

505508
- Using Version 1.2.0 and higher
506509
- Import the Klaviyo module
510+
- We strongly recommend using the latest version of the SDK to ensure compatibility with the latest In-App Forms features. The minimum SDK version supporting In-App Forms is 1.2.0, and a feature matrix is provided below. Forms that leverage unsupported features will not appear in your app until you update to a version that supports those features.
511+
- Please read the [migration guide](MIGRATION_GUIDE.md) if you are upgrading from 1.2.0 to understanding changes to In-App Forms behavior.
512+
513+
| Feature | Minimum SDK Version |
514+
| ------------------ | ------------------- |
515+
| Basic In-App Forms | 1.2.0+ |
516+
| Time Delay | 2.0.0 |
517+
| Audience Targeting | 2.0.0 |
507518

508519
### Setup
509520

510-
To display in-app forms, add the following code to your application
521+
To configure your app to display In-App Forms, call `Klaviyo.registerForInAppForms()` after initializing the SDK with your public API key. Once registered, the SDK may launch an overlay view at any time to present a form according to its targeting and behavior settings configured in your Klaviyo account.
522+
523+
For the best user experience, we recommend registering after any splash screen or loading animations have completed. Depending on your app's architecture, this might be in a particular Screen's useEffect.
511524

512525
```
513526
import { Klaviyo } from "klaviyo-react-native-sdk";
@@ -517,20 +530,32 @@ import { Klaviyo } from "klaviyo-react-native-sdk";
517530
Klaviyo.registerForInAppForms();
518531
```
519532

520-
### Behavior
533+
#### In-App Forms Session Configuration
534+
535+
A "session" is considered to be a logical unit of user engagement with the app, defined as a series of foreground interactions that occur within a continuous or near-continuous time window. This is an important concept for In-App Forms, as we want to ensure that a user will not see a form multiple times within a single session.
536+
537+
A session will time out after a specified period of inactivity. When a user launches the app, if the time between the previous interaction with the app and the current one exceeds the specified timeout, we will consider this a new session.
538+
539+
This timeout has a default value of 3600 seconds (1 hour), but it can be customized. To do so, pass an `FormConfiguration` object to the `registerForInAppForms()` method. For example, to set a session timeout of 30 minutes:
540+
541+
```
542+
import { Klaviyo } from "klaviyo-react-native-sdk";
543+
544+
let config: FormConfiguration = { sessionTimeoutDuration: 1800 }
545+
Klaviyo.registerForInAppForms(config);
546+
```
521547

522-
Once `registerForInAppForms()` is called, the SDK will load form data for your account and display no more than one form within 15 seconds, based on form targeting and behavior settings.
548+
### Unregistering from In-App Forms
523549

524-
You can call `registerForInAppForms()` any time after initializing with your public API key to control when and where in your app's UI a form can appear. It is safe to register multiple times per application session. The SDK will internally prevent multiple forms appearing at once.
550+
If at any point you need to prevent the SDK from displaying In-App Forms, e.g. when the user logs out, you may call:
525551

526-
Consider how often you want to register for forms. For example, registering from a lifecycle event is advisable so that the user has multiple opportunities to see your messaging if they are browsing your app for a prolonged period. However, be advised the form will be shown as soon as it is ready, so you may still need to condition this based on the user's context within your application. Future versions of this product will provide more control in this regard.
552+
```
553+
import { Klaviyo } from "klaviyo-react-native-sdk";
527554
528-
| Callback | Description |
529-
| ---------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
530-
| `const handleAppStateChange = async (nextAppState: string) => { Klaviyo.registerForInAppForms(); }` | Anytime the app is foregrounded, check for forms and show if available. |
531-
| `function App(): JSX.Element { useEffect(() => { Klaviyo.registerForInAppForms(); }};` | Show a form upon initial app launch. |
555+
Klaviyo.unregisterFromInAppForms(config);
556+
```
532557

533-
**Note**: At this time, when device orientation changes any currently visible form is closed and will not be re-displayed automatically.
558+
Note that after unregistering, the next call to `registerForInAppForms()` will be considered a new session by the SDK.
534559

535560
## Troubleshooting
536561

android/gradle.properties

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,9 @@ org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryErro
1111
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
1212
# org.gradle.parallel=true
1313
#Tue Dec 19 15:08:27 EST 2023
14-
KlaviyoReactNativeSdk_klaviyoAndroidSdkVersion=3.3.1
14+
KlaviyoReactNativeSdk_klaviyoAndroidSdkVersion=4.0.0
1515
KlaviyoReactNativeSdk_kotlinVersion=1.8.0
1616
KlaviyoReactNativeSdk_minSdkVersion=23
1717
KlaviyoReactNativeSdk_targetSdkVersion=31
1818
KlaviyoReactNativeSdk_compileSdkVersion=31
1919
KlaviyoReactNativeSdk_ndkversion=21.4.7075529
20-
21-
# For local SDK development: set to true in your local.properties file
22-
# you must set separately for the example app and the library
23-
localCompositeBuild=false
24-
localSdkPath=../../klaviyo-android-sdk

android/settings.gradle

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,30 @@
11
// Substitute the jitpack SDK dependency for your local instance of the project
2-
// Enabling here allows you to develop against a local android SDK for kotlin files in the library module
3-
if (file("local.properties").canRead()) {
4-
def localProperties = new Properties()
5-
localProperties.load(new FileInputStream(file("local.properties")))
6-
localCompositeBuild = localProperties["localCompositeBuild"] ?: localCompositeBuild
7-
localSdkPath = localProperties["localSdkPath"] ?: localSdkPath
8-
}
2+
// Enabling here allows you to build the example app against a local android SDK
3+
def localPropertiesFile = file("local.properties")
4+
5+
if (localPropertiesFile.exists() && localPropertiesFile.canRead()) {
6+
def localProperties = new Properties()
7+
localProperties.load(new FileInputStream(localPropertiesFile))
8+
def localCompositeBuild = localProperties["localCompositeBuild"] == "true"
9+
def localSdkPath = localProperties["localSdkPath"] as String
910

10-
if (localCompositeBuild == "true") {
11+
if (localCompositeBuild) {
1112
def sdkDirectory = new File(localSdkPath)
1213

1314
if (!sdkDirectory.exists()) {
14-
throw new GradleException("Local Klaviyo Android SDK directory does not exist: " + sdkDirectory.getAbsolutePath())
15+
throw new GradleException("Local Klaviyo Android SDK directory does not exist: " + sdkDirectory.getAbsolutePath())
1516
} else {
16-
print("Configuring composite project with local Klaviyo Android SDK for library.")
17+
print("Configuring composite project with local Klaviyo Android SDK for library.")
1718
}
1819

1920
includeBuild(sdkDirectory) {
20-
dependencySubstitution {
21-
// substitute remote dependency with local module
22-
substitute module("com.github.klaviyo.klaviyo-android-sdk:core") using project(":sdk:core")
23-
substitute module("com.github.klaviyo.klaviyo-android-sdk:analytics") using project(":sdk:analytics")
24-
substitute module("com.github.klaviyo.klaviyo-android-sdk:forms") using project(":sdk:forms")
25-
substitute module("com.github.klaviyo.klaviyo-android-sdk:push-fcm") using project(":sdk:push-fcm")
26-
}
21+
dependencySubstitution {
22+
// substitute remote dependency with local module
23+
substitute module("com.github.klaviyo.klaviyo-android-sdk:core") using project(":sdk:core")
24+
substitute module("com.github.klaviyo.klaviyo-android-sdk:analytics") using project(":sdk:analytics")
25+
substitute module("com.github.klaviyo.klaviyo-android-sdk:forms") using project(":sdk:forms")
26+
substitute module("com.github.klaviyo.klaviyo-android-sdk:push-fcm") using project(":sdk:push-fcm")
27+
}
2728
}
28-
}
29+
}
30+
}

android/src/main/java/com/klaviyoreactnativesdk/KlaviyoReactNativeSdkModule.kt

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,12 @@ import com.klaviyo.analytics.model.Profile
1515
import com.klaviyo.analytics.model.ProfileKey
1616
import com.klaviyo.core.Registry
1717
import com.klaviyo.core.utils.AdvancedAPI
18+
import com.klaviyo.forms.InAppFormsConfig
1819
import com.klaviyo.forms.registerForInAppForms
20+
import com.klaviyo.forms.unregisterFromInAppForms
1921
import java.io.Serializable
2022
import kotlin.reflect.KVisibility
23+
import kotlin.time.Duration.Companion.seconds
2124

2225
class KlaviyoReactNativeSdkModule(
2326
private val reactContext: ReactApplicationContext,
@@ -44,7 +47,7 @@ class KlaviyoReactNativeSdkModule(
4447
T::class
4548
.nestedClasses
4649
.filter {
47-
it.visibility == KVisibility.PUBLIC && it.objectInstance != null
50+
it.visibility == KVisibility.PUBLIC && it.objectInstance is T
4851
}.associate {
4952
it.simpleName.toString() to (it.objectInstance as T).name
5053
}
@@ -60,16 +63,28 @@ class KlaviyoReactNativeSdkModule(
6063
}
6164

6265
@ReactMethod
63-
fun registerForInAppForms() {
66+
fun registerForInAppForms(configuration: ReadableMap?) {
6467
UiThreadUtil.runOnUiThread {
6568
try {
66-
Klaviyo.registerForInAppForms()
69+
val timeout = configuration?.getDouble("sessionTimeoutDuration")?.seconds
70+
Klaviyo.registerForInAppForms(
71+
InAppFormsConfig(
72+
sessionTimeoutDuration = timeout ?: InAppFormsConfig.DEFAULT_SESSION_TIMEOUT,
73+
),
74+
)
6775
} catch (e: Exception) {
6876
Registry.log.error("Android unable to register for in app forms on main thread", e)
6977
}
7078
}
7179
}
7280

81+
@ReactMethod
82+
fun unregisterFromInAppForms() {
83+
UiThreadUtil.runOnUiThread {
84+
Klaviyo.unregisterFromInAppForms()
85+
}
86+
}
87+
7388
@ReactMethod
7489
fun setProfile(profile: ReadableMap) {
7590
val parsedProfile = Profile()
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
<resources>
2-
<string name="klaviyo_sdk_version_override">1.2.0</string>
2+
<string name="klaviyo_sdk_version_override">2.0.0</string>
33
<string name="klaviyo_sdk_name_override">react_native</string>
44
</resources>

bump-version.sh

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ read -rp "Enter the new React SDK version: " new_version
55

66
# Update SDK version in package.json
77
if [[ -f "package.json" ]]; then
8-
sed -i '' "0,/\"version\": \".*\"/s//\"version\": \"$new_version\"/" package.json
8+
jq --arg newVersion "$new_version" '.version = $newVersion' package.json > tmp.json && mv tmp.json package.json
99
echo "Updated SDK version in package.json."
1010
else
1111
echo "Error: package.json not found."
@@ -52,7 +52,8 @@ read -rp "Enter the Swift SDK version: " swift_version
5252
podspec_file="klaviyo-react-native-sdk.podspec"
5353
if [[ -f "$podspec_file" ]]; then
5454
sed -i '' "s/\"KlaviyoSwift\", \".*\"/\"KlaviyoSwift\", \"$swift_version\"/" "$podspec_file"
55-
echo "Updated KlaviyoSwift version in $podspec_file."
55+
sed -i '' "s/\"KlaviyoForms\", \".*\"/\"KlaviyoForms\", \"$swift_version\"/" "$podspec_file"
56+
echo "Updated KlaviyoSwift and KlaviyoForms version in $podspec_file."
5657
else
5758
echo "Error: $podspec_file not found."
5859
exit 1

0 commit comments

Comments
 (0)