docs(example): update READMEs for JS-first init and Firebase push setup#346
Open
evan-masseau wants to merge 3 commits intofeat/example-appfrom
Open
docs(example): update READMEs for JS-first init and Firebase push setup#346evan-masseau wants to merge 3 commits intofeat/example-appfrom
evan-masseau wants to merge 3 commits intofeat/example-appfrom
Conversation
517a99f to
886bc3d
Compare
1fd9a5b to
6332942
Compare
886bc3d to
d45deda
Compare
6332942 to
e418fc1
Compare
d45deda to
8a8d3ed
Compare
e418fc1 to
c7fe2b8
Compare
8a8d3ed to
170d522
Compare
c7fe2b8 to
02bcc10
Compare
170d522 to
d820799
Compare
02bcc10 to
7743ad0
Compare
d820799 to
c98ccb7
Compare
7743ad0 to
972de07
Compare
c98ccb7 to
0e2afef
Compare
972de07 to
8680c94
Compare
0e2afef to
ef7e26a
Compare
8680c94 to
e71e650
Compare
ef7e26a to
c3f4a6c
Compare
e71e650 to
0b13733
Compare
c3f4a6c to
c21ee7a
Compare
0b13733 to
9ff975d
Compare
c21ee7a to
8049209
Compare
9ff975d to
f54d106
Compare
8049209 to
9b6ece0
Compare
f54d106 to
e924d84
Compare
9b6ece0 to
6f7cf7b
Compare
e924d84 to
970fcf7
Compare
6f7cf7b to
6f30213
Compare
970fcf7 to
22f3bcf
Compare
This was referenced Apr 20, 2026
182a164 to
88e9195
Compare
cd1dde7 to
d126ffe
Compare
88e9195 to
4dbe5b6
Compare
d126ffe to
f4da17e
Compare
4dbe5b6 to
5d4d25c
Compare
f4da17e to
e0b8e13
Compare
5d4d25c to
34740f6
Compare
e0b8e13 to
a3d7d0a
Compare
34740f6 to
38cf2b3
Compare
evan-masseau
added a commit
that referenced
this pull request
Apr 21, 2026
…pp shell (#343) # Description Part 2 of 4 in the RN example-app overhaul chain for [MAGE-464](https://linear.app/klaviyo/issue/MAGE-464). Rebuilds the entire JS-layer of the example app: - **`PermissionHelper`** — location + push permission flows wrapping `react-native-permissions` and `@react-native-firebase/messaging` - **Four domain hooks** — `useAnalytics`, `useForms`, `useLocation`, `usePush`, each owning the state and SDK calls for one Klaviyo feature area - **App shell (`App.tsx`)** — sectioned `SectionList` UI that consumes the hooks, wires UI buttons to handlers, loads the API key from env, and fails fast at module load if the key is missing - **Cleanup** — removes the old `KlaviyoReactWrapper.ts` / `AppViewInterface.ts` / `RandomGenerators.ts` approach ## Due Diligence - [x] I have tested this on a simulator/emulator or a physical device, on iOS and Android (if applicable). - [ ] I have added sufficient unit/integration tests of my changes. - [ ] I have adjusted or added new test cases to team test docs, if applicable. - [x] I am confident these changes are implemented with feature parity across iOS and Android (if applicable). ## Release/Versioning Considerations - [x] `Patch` Contains internal changes or backwards-compatible bug fixes. - [ ] `Minor` Contains changes to the public API. - [ ] `Major` Contains **breaking** changes. - [ ] Contains readme or migration guide changes. - [ ] This is planned work for an upcoming release. ## Changelog / Code Overview | Area | Change | |------|--------| | `example/src/PermissionHelper.ts` (new) | Location + push permission request / status helpers; memoized Firebase availability probe | | `example/src/hooks/useAnalytics.ts` (new) | Profile identity + attribute setters, structured Location support, events (test + Viewed Product) | | `example/src/hooks/useForms.ts` (new) | In-app forms register/unregister | | `example/src/hooks/useLocation.ts` (new) | Geofencing register/unregister, Get Current Geofences with modal display | | `example/src/hooks/usePush.ts` (new) | Push permission + token flow; platform-aware `onTokenRefresh` (Android: FCM token; iOS: re-fetch APNs) | | `example/src/App.tsx` (rewritten) | Sectioned UI wiring hooks to buttons; JS-first SDK init; fail-fast on missing `KLAVIYO_API_KEY` | | `example/src/Styles.ts` | Expanded styles for the new sectioned layout | | `example/src/env.d.ts` + `.env.example` | Env-var typing + example key placeholder | ## Key decisions - **Fail fast on missing API key** — module-load throw with a clear setup message beats silently broken SDK calls or threading `isApiKeyConfigured` through every hook. Matches the Android example app pattern. - **iOS location** — `WhenInUse → Always` upgrades are driven by separate user taps, never auto-chained, because iOS silently blocks the upgrade if requested in the same interaction as the initial grant. - **iOS push** — `messaging().requestPermission()` triggers APNs registration; we use `getAPNSToken` (not `getToken`) because Klaviyo iOS expects raw APNs tokens. - **Firebase availability probe** uses `messaging().app` — not just module resolvability — so we correctly detect when `[FIRApp configure]` was skipped due to missing plist. ## Test Plan - [x] Hooks typecheck and lint clean - [x] End-to-end behavior verified in the simulator on both iOS and Android ## Related Issues/Tickets Part of [MAGE-464](https://linear.app/klaviyo/issue/MAGE-464) **Chained PR series:** 1. theme + components (merged in #342) 2. **This PR** — permission helpers, hooks, app shell, JS-first init 3. native platform setup (iOS + Android Firebase push) — #345 4. docs — #346 🤖 Generated with [Claude Code](https://claude.com/claude-code)
a3d7d0a to
ac8ae77
Compare
38cf2b3 to
24c4040
Compare
ac8ae77 to
92f82d4
Compare
24c4040 to
c23fce4
Compare
92f82d4 to
ac8ae77
Compare
91ef4e0 to
58c04fc
Compare
ac8ae77 to
bf39103
Compare
a944088 to
a67643e
Compare
bf39103 to
cad277c
Compare
aff910d to
537311c
Compare
cad277c to
b2fb2b6
Compare
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]> Manual readme review
16 tasks
…-534] - Rewrite AppDelegate in Swift using the @main attribute and the RCTReactNativeFactory + RCTDefaultReactNativeFactoryDelegate pattern (resolves the RCTAppDelegate deprecation warning introduced in RN 0.78+). - Inline PushNotificationsHelper's static methods as a nested `enum Push` inside AppDelegate — they only existed as a Swift-island shim for the Objective-C AppDelegate to call into. Note: `setPushToken` signature changed from `setPushToken(token:)` to `setPushToken(_:)`, and `handleSilentPush` parameter type changed from NSDictionary to [AnyHashable: Any]. No external callers (all references were ObjC or commented-out examples). - Drop main.m and the bridging header — the app target is now pure Swift. - Port KlaviyoReactNativeSdkExampleTests.m to Swift; wrap the RCTSetLogFunction capture box in an NSLock since RN invokes the log callback from arbitrary threads. - Update example/README.md references from AppDelegate.mm / PushNotificationsHelper.swift to AppDelegate.swift / Push.handleReceivingPush. - project.pbxproj: prune all refs to deleted ObjC files, remove SWIFT_OBJC_BRIDGING_HEADER build settings, add SWIFT_VERSION=5.0 to the test target for Swift compilation support. Out of scope (separate tickets): - ios/KlaviyoReactNativeSdk.mm + .h (the SDK's public RN bridge) — deserves its own review. - The existing example test still searches for the stale "Welcome to React" label from the RN template; the port preserves behavior verbatim. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
# Description Rewrites the React Native example app's `AppDelegate` from Objective-C++ to Swift. Makes example app code much more approachable. Also resolves a `RCTAppDelegate` deprecation warning introduced in RN 0.78+ and first step toward modernizing (we could move SDK code to swift next, this was a good little PoC of asking Claude to convert stuff) > [!NOTE] > This is a **stacked PR** targeting `ecm/example-app/5-docs`, not `master`. It is the 6th in the `ecm/example-app/*` chain. ## Due Diligence - [x] I have tested this on a simulator/emulator or a physical device, on iOS and Android (if applicable). _(iOS only — this PR only touches the iOS example app.)_ - [x] I have added sufficient unit/integration tests of my changes. _(Ported the existing test target to Swift; no new behavior to cover.)_ - [ ] I have adjusted or added new test cases to team test docs, if applicable. - [x] I am confident these changes are implemented with feature parity across iOS and Android (if applicable). _(iOS-only change; Android example app unaffected.)_ ## Release/Versioning Considerations - [x] `Patch` Contains internal changes or backwards-compatible bug fixes. _(Example-app-only; no SDK surface changes.)_ - [ ] `Minor` Contains changes to the public API. - [ ] `Major` Contains **breaking** changes. - [x] Contains readme or migration guide changes. _(Updated `example/README.md` file references only — no migration guide impact.)_ - [x] This is planned work for an upcoming release. ## Changelog / Code Overview **Rewrite `AppDelegate` in Swift** - Uses the `@main` attribute and the `RCTReactNativeFactory` + `RCTDefaultReactNativeFactoryDelegate` pattern (resolves the `RCTAppDelegate` deprecation warning introduced in RN 0.78+). - Inlines `PushNotificationsHelper`'s static methods as a nested `enum Push` inside `AppDelegate` — those methods only existed as a Swift-island shim for the Objective-C `AppDelegate` to call into. - `setPushToken(token:)` -> `setPushToken(_:)` - `handleSilentPush` parameter type: `NSDictionary` -> `[AnyHashable: Any]` - No external callers (all references were ObjC or commented-out examples). **Drop Objective-C scaffolding** - Delete `AppDelegate.h`, `AppDelegate.mm`, `main.m`, `PushNotificationsHelper.swift`, and the bridging header. The app target is now pure Swift. **Port the test target to Swift** - `KlaviyoReactNativeSdkExampleTests.m` -> `KlaviyoReactNativeSdkExampleTests.swift`. - Wrap the `RCTSetLogFunction` capture box in an `NSLock` since RN invokes the log callback from arbitrary threads. **Docs** - Update `example/README.md` references from `AppDelegate.mm` / `PushNotificationsHelper.swift` to `AppDelegate.swift` / `Push.handleReceivingPush`. **Xcode project** - `project.pbxproj`: prune all refs to deleted ObjC files, remove `SWIFT_OBJC_BRIDGING_HEADER` build settings, add `SWIFT_VERSION=5.0` to the test target for Swift compilation support. ### Out of scope (separate tickets) - `ios/KlaviyoReactNativeSdk.mm` + `.h` (the SDK's public RN bridge) — deserves its own review. - The existing example test still searches for the stale "Welcome to React" label from the RN template; the port preserves behavior verbatim. ## Test Plan - [x] `xcodebuild build` green on iPhone 16e sim - [x] `xcodebuild build-for-testing` green on iPhone 16e sim - [x] App launches on iPhone 16e sim - [x] APNs token received - [x] Firebase configures cleanly at startup - [ ] Manual push / deep link / badge flows — _not exercised end-to-end in this PR_ ## Related Issues/Tickets Closes MAGE-534
Wiz Scan Summary
To detect these findings earlier in the dev lifecycle, try using Wiz Code VS Code Extension. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Part 4 of 4 in the RN example-app overhaul chain for MAGE-464. Final piece: documentation updates reflecting the JS-first initialization flow and Firebase-optional push setup from the preceding PRs, plus
Installation Stepannotations across the example's native + JS source to help integrators walk the integration surface.Due Diligence
Release/Versioning Considerations
PatchContains internal changes or backwards-compatible bug fixes.MinorContains changes to the public API.MajorContains breaking changes.feat/example-appso docs updates land with the feature.Changelog / Code Overview
README.md(root)example/README.md— no more duplicated setup stepsexample/README.md(new).envconfiguration (via.env.template), iOS Firebase setup (realGoogleService-Info.plistor documented stub for push-disabled builds), Android Firebase setup (google-services.jsonvs conditional gms plugin), native push alternative pointerAppDelegate.mm#if __has_include(<FirebaseCore/FirebaseCore.h>)guards and runtimeif (plistPath)check now that the stub-plist path is the documented zero-config default — simpler code, same behaviorAppDelegate.{h,mm},PushNotificationsHelper.swift,NotificationService.swift,MainActivity.kt,AndroidManifest.xml,build.gradle(android)App.tsxRN Installation Stepblocks for JS-first initPodfileTest Plan
example/README.mdon a fresh clone:yarn example setup, copied.env.templateto.env, setKLAVIYO_API_KEY,yarn example ios/yarn example android— both launched (iOS with stub plist, Android withoutgoogle-services.json)example/README.md's Firebase-enable path: dropped realGoogleService-Info.plist+google-services.jsoninto place, re-ranpod install/ rebuilt Android, push section lit upInstallation Stepprefix — all comment clusters resolve to the steps described in the indexRelated Issues/Tickets
Part of MAGE-464
Chained PR series:
Follow-up: MAGE-534 — convert
AppDelegate.mmto pure Swift (removes the stripped-guards file entirely).🤖 Generated with Claude Code