Skip to content

Commit 58c04fc

Browse files
evan-masseauclaude
andcommitted
docs(example): update READMEs for JS-first init and Firebase push setup
Root README: rewrite the example-app section to reflect the JS-first initialization flow (API key via .env, Firebase push optional) instead of the removed initializeKlaviyoFromNative toggle. example/README.md: new standalone setup guide covering .env configuration, iOS Firebase setup (GoogleService-Info.plist + pod install), Android Firebase setup (google-services.json vs the shipped .template), and the push-disabled path (app runs cleanly without Firebase configured). Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
1 parent ac8ae77 commit 58c04fc

13 files changed

Lines changed: 254 additions & 82 deletions

File tree

README.md

Lines changed: 3 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -103,29 +103,9 @@ yarn add klaviyo-react-native-sdk
103103

104104
### Example App
105105

106-
We have included an example app in this repository for reference of how to integrate with our SDK.
107-
It is primarily intended to give code samples such as how and where to `initialize`, implement notification
108-
delegate methods on iOS, and handle an opened notification intent on Android. We've commented the sample app
109-
code to call out key setup steps, search for `iOS Installation Step` and `Android Installation Step`.
110-
111-
To run the example app:
112-
113-
- Clone this repository
114-
- From the root directory, run `yarn example setup`. This is an alias that will do the following:
115-
- Run `yarn install --immutable` from the root directory
116-
- Navigate to the `example` directory and run `bundle install`
117-
- Navigate to the `example/ios` directory and run `bundle exec pod install`
118-
- Android configuration:
119-
- To initialize Klaviyo from the native layer, open `example/android/gradle.properties` and follow the
120-
instructions to set your `publicApiKey` and verify `initializeKlaviyoFromNative` is enabled.
121-
- If you wish to run the Android example app with push/firebase, you'll need to copy a `google-services.json`
122-
file into `example/android/app/src` and update the `applicationId` in `app/build.gradle` to match your application ID.
123-
Then, open `example/android/gradle.properties` and follow the instructions to enable `useNativeFirebase`.
124-
This is disabled by default because the app will crash on launch without a `google-services.json` file.
125-
- From the project's root directory, use the following commands start a metro server and run the example app.
126-
- `yarn example start` - to start the metro server
127-
- `yarn example android` - to run the example app on an Android emulator or device
128-
- `yarn example ios` - to run the example app on an iOS simulator
106+
The [example app](./example) is a reference integration demonstrating SDK setup and
107+
exercising the public API surface. See [`example/README.md`](./example/README.md) for
108+
setup instructions, Firebase configuration, and a walkthrough of the demonstrated features.
129109

130110
### Android
131111

example/README.md

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
# Klaviyo React Native SDK Example App
2+
3+
The example app is a reference integration of the Klaviyo React Native SDK. It
4+
demonstrates where and how to `initialize`, implement notification delegate
5+
methods on iOS, handle opened notification intents on Android, and exercise the
6+
SDK's public API surface (profile, events, in-app forms, geofencing, push).
7+
Sample code is annotated — search for `JS Installation Step`, `iOS Installation Step`,
8+
and `Android Installation Step` in the source to find the integration callouts.
9+
10+
## Getting Started
11+
12+
### 1. Configure Your API Key
13+
14+
The app loads your Klaviyo public API key from a gitignored `.env` file via
15+
[`react-native-dotenv`](https://github.com/goatandsheep/react-native-dotenv).
16+
17+
1. Copy the template: `cp .env.template .env`
18+
2. Edit `.env` and set `KLAVIYO_API_KEY=` to your actual public API key
19+
3. Restart Metro with `yarn start --reset-cache` so the babel plugin picks up the new value
20+
21+
> **If you hit "Klaviyo API key not configured" after setting up `.env`:** Metro inlines
22+
> `.env` values at bundle time via `react-native-dotenv`. If Metro was already running when
23+
> you created or edited `.env`, it will serve a cached bundle with `KLAVIYO_API_KEY` still
24+
> undefined. Stop Metro and restart it with `yarn start --reset-cache` (from `example/`).
25+
26+
Notes:
27+
28+
- `.env` is gitignored — only `.env.template` is checked in.
29+
- If `KLAVIYO_API_KEY` is missing or still set to the placeholder, the app fails
30+
fast at module load with a clear error — no silent misbehavior. Copy
31+
`.env.template` to `.env` and set a real key before running.
32+
33+
### 2. Install Dependencies
34+
35+
The one-liner, run from the repo root:
36+
37+
```bash
38+
# From the repo root
39+
yarn example setup
40+
41+
# This alias runs:
42+
# yarn install --immutable (repo root)
43+
# bundle install (example/)
44+
# bundle exec pod install (example/ios/)
45+
```
46+
47+
You can run those steps manually if you prefer, but `yarn example setup` is the
48+
preferred entry point.
49+
50+
### 3. Run the App
51+
52+
Preferred — from the repo root (yarn workspaces entry point):
53+
54+
```bash
55+
# From the repo root
56+
yarn example start
57+
yarn example android
58+
yarn example ios
59+
```
60+
61+
Alternatively, from the `example/` directory:
62+
63+
```bash
64+
# Start Metro bundler
65+
yarn start
66+
67+
# Run iOS
68+
yarn ios
69+
70+
# Run Android
71+
yarn android
72+
```
73+
74+
## Push Notifications
75+
76+
Push notifications are handled via `@react-native-firebase/messaging` in the
77+
JavaScript layer — the Klaviyo SDK is initialized from JS, and Firebase manages
78+
push tokens cross-platform.
79+
80+
### Platform-specific Firebase setup
81+
82+
**Android** builds and runs out of the box without Firebase configured. The
83+
`com.google.gms.google-services` plugin is conditionally applied only when
84+
`google-services.json` is present. Without the file, push features are dormant
85+
but everything else (profile, events, in-app forms, geofencing) works normally.
86+
87+
- To enable push: drop a real `google-services.json` from your Firebase project
88+
into `example/android/app/`. The file is gitignored. Ensure the
89+
`applicationId` in `example/android/app/build.gradle`
90+
(`com.klaviyoreactnativesdkexample`) is registered as an Android app in your
91+
Firebase project.
92+
93+
**iOS** requires a `GoogleService-Info.plist` at `example/ios/` to build at all
94+
— the Xcode project's Resources build phase references it unconditionally. Two
95+
options:
96+
97+
- **To enable push:** drop a real `GoogleService-Info.plist` from your Firebase
98+
project at `example/ios/GoogleService-Info.plist` (gitignored). Ensure your
99+
iOS bundle identifier is registered in the same Firebase project. Then run
100+
`bundle exec pod install` from `example/ios/`.
101+
- **To build without push:** write a stub plist at the same path. The app's
102+
AppDelegate guards `[FIRApp configure]` on the real plist's presence of valid
103+
values — a stub passes the Xcode build-input check, Firebase logs an
104+
invalid-config warning at runtime, and the rest of the app works. Stub
105+
content you can copy:
106+
<details>
107+
<summary>Stub <code>GoogleService-Info.plist</code></summary>
108+
109+
```xml
110+
<?xml version="1.0" encoding="UTF-8"?>
111+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
112+
<plist version="1.0">
113+
<dict>
114+
<key>BUNDLE_ID</key><string>com.klaviyoreactnativesdkexample</string>
115+
<key>GOOGLE_APP_ID</key><string>1:000000000000:ios:0000000000000000</string>
116+
<key>API_KEY</key><string>stub-for-ci</string>
117+
<key>GCM_SENDER_ID</key><string>000000000000</string>
118+
<key>PROJECT_ID</key><string>stub-project</string>
119+
<key>STORAGE_BUCKET</key><string>stub.appspot.com</string>
120+
<key>IS_GCM_ENABLED</key><true/>
121+
<key>IS_ADS_ENABLED</key><false/>
122+
<key>IS_ANALYTICS_ENABLED</key><false/>
123+
<key>IS_APPINVITE_ENABLED</key><false/>
124+
<key>IS_SIGNIN_ENABLED</key><false/>
125+
</dict>
126+
</plist>
127+
```
128+
</details>
129+
130+
### Native push (alternative)
131+
132+
For apps that prefer to handle push tokens natively (e.g., brownfield apps), the
133+
Klaviyo iOS and Android SDKs both have their own native push APIs. See the
134+
`AppDelegate.mm` comments on iOS and `MainApplication.kt` comments on Android for
135+
pointers, plus the platform SDKs' READMEs:
136+
137+
- iOS: https://github.com/klaviyo/klaviyo-swift-sdk#push-notifications
138+
- Android: https://github.com/klaviyo/klaviyo-android-sdk#push-notifications
139+
140+
Note: `Klaviyo.handlePush(intent)` in `MainActivity.kt` is always required on Android — push open tracking depends on native intent handling regardless of how tokens are managed.
141+
142+
## Integration Step Comments
143+
144+
The example source is annotated with `JS Installation Step`, `iOS Installation Step`,
145+
and `Android Installation Step` comments that mark the code an integrator needs to
146+
replicate in their own app. Grep for any of those prefixes across `example/` to walk
147+
the integration surface:
148+
149+
- `JS Installation Step``example/src/App.tsx` and `example/index.js` (SDK init,
150+
API key sourcing, optional `@react-native-firebase/messaging` background handler)
151+
- `iOS Installation Step``example/ios/KlaviyoReactNativeSdkExample/AppDelegate.{h,mm}`,
152+
`PushNotificationsHelper.swift`, `NotificationService.swift`, and the Podfile
153+
- `Android Installation Step``example/android/build.gradle`, `app/build.gradle`,
154+
`AndroidManifest.xml`, and `MainActivity.kt`
155+
156+
Each comment is self-describing — the prefix is just a grep handle, not part of a
157+
strictly ordered checklist. Some steps are labelled by concern rather than being
158+
numbered (e.g., "Firebase, part 1 of 2") where that reads more clearly than an
159+
ordinal would.
160+
161+
## Features Demonstrated
162+
163+
- **Profile Management** — Set email, phone, external ID individually or together; reset profile
164+
- **Event Tracking** — Create test events and Viewed Product events
165+
- **In-App Forms** — Register/unregister with configurable session timeout
166+
- **Geofencing** — Location permission flow, register/unregister, list current geofences
167+
- **Push Notifications** — Firebase-based permission request, token display, badge count (iOS)
168+
- **Deep Linking** — Universal tracking link handling via `Linking` API

example/android/app/build.gradle

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ dependencies {
114114
// The version of react-native is set by the React Native Gradle Plugin
115115
implementation("com.facebook.react:react-android")
116116

117-
// Android Installation Step 1b - add firebase dependency if using push
117+
// Android Installation Step - add firebase dependency if using push
118118
implementation 'com.google.firebase:firebase-messaging-ktx:24.0.0'
119119

120120
if (hermesEnabled.toBoolean()) {
@@ -124,11 +124,13 @@ dependencies {
124124
}
125125
}
126126

127-
// Apply the google-services plugin only when google-services.json is present.
128-
// This lets the example app build out of the box without a Firebase project.
129-
// To enable push: drop your Firebase project's google-services.json into
130-
// example/android/app/, and make sure its package_name matches applicationId
131-
// above. See example/README.md for full setup details.
127+
// Optional Android Installation Step: Apply the google-services plugin only
128+
// when google-services.json is present. This lets the example app build out of
129+
// the box without a Firebase project. To enable push: drop your Firebase
130+
// project's google-services.json into example/android/app/, and make sure its
131+
// package_name matches applicationId above. See example/README.md for full
132+
// setup details. The equivalent on iOS is the `#if __has_include(...)` +
133+
// `if (plistPath)` guard in example/ios/.../AppDelegate.mm.
132134
if (file('google-services.json').exists()) {
133135
apply plugin: "com.google.gms.google-services"
134136
}

example/android/app/src/main/AndroidManifest.xml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<!-- Android Installation Step 2: Configure manifest.xml ... -->
1+
<!-- Android Installation Step: Configure manifest.xml ... -->
22
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
33

44
<uses-permission android:name="android.permission.INTERNET" />
@@ -23,7 +23,7 @@
2323
<category android:name="android.intent.category.LAUNCHER" />
2424
</intent-filter>
2525

26-
<!-- Android Installation Step 2a: Configure deep links, if your push notifications will contain them -->
26+
<!-- Android Installation Step: Configure deep links, if your push notifications will contain them -->
2727
<intent-filter android:label="deep_link_filter">
2828
<action android:name="android.intent.action.VIEW"/>
2929

@@ -34,7 +34,7 @@
3434
<data android:scheme="klaviyosample"/>
3535
</intent-filter>
3636

37-
<!-- Android Installation Step 2b: Configure verified app links for Universal Link Click Tracking -->
37+
<!-- Android Installation Step: Configure verified app links for Universal Link Click Tracking -->
3838
<intent-filter android:label="app_link_handler" android:autoVerify="true">
3939
<action android:name="android.intent.action.VIEW" />
4040
<category android:name="android.intent.category.DEFAULT" />
@@ -46,7 +46,7 @@
4646

4747
</activity>
4848

49-
<!-- Android Installation Step 2b: Register KlaviyoPushService to receive MESSAGING_EVENT intents. -->
49+
<!-- Android Installation Step: Register KlaviyoPushService to receive MESSAGING_EVENT intents. -->
5050
<service
5151
android:name="com.klaviyo.pushFcm.KlaviyoPushService"
5252
android:exported="false">
@@ -55,14 +55,14 @@
5555
</intent-filter>
5656
</service>
5757

58-
<!-- Optional Android Installation Step 2c: Specify an icon for Klaviyo notifications -->
58+
<!-- Optional Android Installation Step: Specify an icon for Klaviyo notifications -->
5959
<!-- Absent this key, Klaviyo SDK will look for com.google.firebase.messaging.default_notification_icon -->
6060
<!-- and absent that, use the default launcher icon for your app -->
6161
<meta-data
6262
android:name="com.klaviyo.push.default_notification_icon"
6363
android:resource="@drawable/ic_notification" />
6464

65-
<!-- Optional Android Installation Step 2d: Specify a notification color for Klaviyo notifications -->
65+
<!-- Optional Android Installation Step: Specify a notification color for Klaviyo notifications -->
6666
<!-- Absent this key, Klaviyo SDK will look for com.google.firebase.messaging.default_notification_color -->
6767
<!-- and absent that, omit specifying a color -->
6868
<meta-data android:name="com.klaviyo.push.default_notification_color"

example/android/app/src/main/java/com/klaviyoreactnativesdkexample/MainActivity.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class MainActivity : ReactActivity() {
2727
Log.v("KlaviyoSampleApp", "MainActivity.onCreate()")
2828
super.onCreate(savedInstanceState)
2929

30-
// Android Installation Step 5a: Depending on the state of your application when the notification is tapped,
30+
// Android Installation Step: Depending on the state of your application when the notification is tapped,
3131
// the intent have started this activity, or it might be received via onNewIntent if the app was already running.
3232
// We recommend passing all intents through Klaviyo.handlePush to make sure you don't miss a use case.
3333
onNewIntent(intent)
@@ -38,13 +38,13 @@ class MainActivity : ReactActivity() {
3838
Log.v("KlaviyoSampleApp", "Launch Intent: " + intent.toString())
3939
super.onNewIntent(intent)
4040

41-
// Android Installation Step 5: Call handlePush when a push notification is tapped
41+
// Android Installation Step: Call handlePush when a push notification is tapped
4242
// Note: due to platform differences, this step must be implemented in native code.
4343
// Tapping on a notification broadcasts an intent to your app. This method detects if the
4444
// intent originated from a Klaviyo push notification and registers a special Opened Push event
4545
Klaviyo.handlePush(intent)
4646

47-
// Android Installation Step 6: Deep linking from native layer (uncommon)
47+
// Android Installation Step: Deep linking from native layer (uncommon)
4848
// Read deep link data from intent, open the appropriate page
4949
val action: String? = intent?.action // e.g. ACTION_VIEW
5050
val deepLink: Uri? = intent?.data // e.g. klaviyoreactnativesdkexample://link

example/android/build.gradle

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
buildscript {
22
ext {
3-
// Android Installation Step 1a: Set compatible minSdkVersion and compileSdkVersion
3+
// Android Installation Step: Set compatible minSdkVersion and compileSdkVersion
44
minSdkVersion = 24
55
targetSdkVersion = 36 // Typically minSdkVersion <= targetSdkVersion <= compileSdkVersion
66
compileSdkVersion = 36
@@ -17,7 +17,10 @@ buildscript {
1717
classpath("com.facebook.react:react-native-gradle-plugin")
1818
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin")
1919

20-
// Android Installation Step - Required dependency to initialize firebase via google-services.json file
20+
// Optional Android Installation Step: google-services Gradle plugin,
21+
// required to process google-services.json if you're enabling push.
22+
// The plugin is applied conditionally in app/build.gradle; see the
23+
// `apply plugin: "com.google.gms.google-services"` guard there.
2124
classpath("com.google.gms:google-services:4.4.2")
2225
}
2326
}

example/index.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ import { AppRegistry } from 'react-native';
22
import App from './src/App';
33
import { name as appName } from './app.json';
44

5-
// @react-native-firebase/messaging requires a background message handler to be
6-
// registered at module load — before AppRegistry.registerComponent — or it will
7-
// warn on every cold start ("No task registered for key ReactNativeFirebaseMessagingHeadlessTask").
5+
// JS Installation Step (only if using @react-native-firebase/messaging):
6+
// Register a background message handler at module load — before
7+
// AppRegistry.registerComponent — or RNFB will warn on every cold start
8+
// ("No task registered for key ReactNativeFirebaseMessagingHeadlessTask").
89
// See https://rnfirebase.io/reference/messaging#setBackgroundMessageHandler
910
//
1011
// For Klaviyo push, this is a no-op: Klaviyo delivers notifications through

example/ios/KlaviyoReactNativeSdkExample/AppDelegate.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
#import "klaviyo_react_native_sdk-Swift.h"
99
#endif
1010

11-
// iOS Installation Step 1: Conform AppDelegate to UNUserNotificationCenterDelegate
11+
// iOS Installation Step: Conform AppDelegate to UNUserNotificationCenterDelegate
1212
@interface AppDelegate: RCTAppDelegate <UNUserNotificationCenterDelegate>
1313

1414
@end

0 commit comments

Comments
 (0)