Skip to content

docs(example): update READMEs for JS-first init and Firebase push setup#346

Open
evan-masseau wants to merge 3 commits intofeat/example-appfrom
ecm/example-app/5-docs
Open

docs(example): update READMEs for JS-first init and Firebase push setup#346
evan-masseau wants to merge 3 commits intofeat/example-appfrom
ecm/example-app/5-docs

Conversation

@evan-masseau
Copy link
Copy Markdown
Contributor

@evan-masseau evan-masseau commented Apr 17, 2026

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 Step annotations across the example's native + JS source to help integrators walk the integration surface.

Due Diligence

  • 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.
  • I am confident these changes are implemented with feature parity across iOS and Android (if applicable).

Release/Versioning Considerations

  • 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.
    • Example-app-only docs; merges to feat/example-app so docs updates land with the feature.
  • This is planned work for an upcoming release.

Changelog / Code Overview

File Change
README.md (root) Trim the example-app section to a 4-line pointer at example/README.md — no more duplicated setup steps
example/README.md (new) Standalone setup guide: integration step comment index, .env configuration (via .env.template), iOS Firebase setup (real GoogleService-Info.plist or documented stub for push-disabled builds), Android Firebase setup (google-services.json vs conditional gms plugin), native push alternative pointer
AppDelegate.mm Strip #if __has_include(<FirebaseCore/FirebaseCore.h>) guards and runtime if (plistPath) check now that the stub-plist path is the documented zero-config default — simpler code, same behavior
AppDelegate.{h,mm}, PushNotificationsHelper.swift, NotificationService.swift, MainActivity.kt, AndroidManifest.xml, build.gradle (android) Add `iOS
App.tsx Mark the RN Installation Step blocks for JS-first init
Podfile Comment reflow only (no behavior change)

Test Plan

  • Followed the root README pointer → example/README.md on a fresh clone: yarn example setup, copied .env.template to .env, set KLAVIYO_API_KEY, yarn example ios / yarn example android — both launched (iOS with stub plist, Android without google-services.json)
  • Followed example/README.md's Firebase-enable path: dropped real GoogleService-Info.plist + google-services.json into place, re-ran pod install / rebuilt Android, push section lit up
  • Grepped for each Installation Step prefix — all comment clusters resolve to the steps described in the index

Related Issues/Tickets

Part of MAGE-464

Chained PR series:

  1. theme + components (merged in feat(example): add theme system and reusable UI components #342)
  2. JS layer: permission helpers, hooks, app shell (merged in feat(example): rebuild JS layer with permission helpers, hooks, and app shell #343)
  3. native platform setup (iOS + Android Firebase push) — feat(example): native platform setup for Firebase push (iOS + Android) #345
  4. This PR — docs + integration-step annotations

Follow-up: MAGE-534 — convert AppDelegate.mm to pure Swift (removes the stripped-guards file entirely).

🤖 Generated with Claude Code

@evan-masseau evan-masseau force-pushed the ecm/example-app/4-native branch from 517a99f to 886bc3d Compare April 17, 2026 21:40
@evan-masseau evan-masseau force-pushed the ecm/example-app/5-docs branch from 1fd9a5b to 6332942 Compare April 17, 2026 21:41
@evan-masseau evan-masseau force-pushed the ecm/example-app/4-native branch from 886bc3d to d45deda Compare April 17, 2026 21:43
@evan-masseau evan-masseau force-pushed the ecm/example-app/5-docs branch from 6332942 to e418fc1 Compare April 17, 2026 21:43
@evan-masseau evan-masseau force-pushed the ecm/example-app/4-native branch from d45deda to 8a8d3ed Compare April 17, 2026 21:47
@evan-masseau evan-masseau force-pushed the ecm/example-app/5-docs branch from e418fc1 to c7fe2b8 Compare April 17, 2026 21:47
@evan-masseau evan-masseau force-pushed the ecm/example-app/4-native branch from 8a8d3ed to 170d522 Compare April 17, 2026 21:51
@evan-masseau evan-masseau force-pushed the ecm/example-app/5-docs branch from c7fe2b8 to 02bcc10 Compare April 17, 2026 21:51
@evan-masseau evan-masseau force-pushed the ecm/example-app/4-native branch from 170d522 to d820799 Compare April 17, 2026 22:00
@evan-masseau evan-masseau force-pushed the ecm/example-app/5-docs branch from 02bcc10 to 7743ad0 Compare April 17, 2026 22:00
@evan-masseau evan-masseau force-pushed the ecm/example-app/4-native branch from d820799 to c98ccb7 Compare April 17, 2026 22:07
@evan-masseau evan-masseau force-pushed the ecm/example-app/5-docs branch from 7743ad0 to 972de07 Compare April 17, 2026 22:07
@evan-masseau evan-masseau force-pushed the ecm/example-app/4-native branch from c98ccb7 to 0e2afef Compare April 17, 2026 22:22
@evan-masseau evan-masseau force-pushed the ecm/example-app/5-docs branch from 972de07 to 8680c94 Compare April 17, 2026 22:22
@evan-masseau evan-masseau force-pushed the ecm/example-app/4-native branch from 0e2afef to ef7e26a Compare April 18, 2026 00:57
@evan-masseau evan-masseau force-pushed the ecm/example-app/5-docs branch from 8680c94 to e71e650 Compare April 18, 2026 00:57
@evan-masseau evan-masseau force-pushed the ecm/example-app/4-native branch from ef7e26a to c3f4a6c Compare April 20, 2026 13:25
@evan-masseau evan-masseau force-pushed the ecm/example-app/5-docs branch from e71e650 to 0b13733 Compare April 20, 2026 13:25
@evan-masseau evan-masseau force-pushed the ecm/example-app/4-native branch from c3f4a6c to c21ee7a Compare April 20, 2026 15:33
@evan-masseau evan-masseau force-pushed the ecm/example-app/5-docs branch from 0b13733 to 9ff975d Compare April 20, 2026 15:33
@evan-masseau evan-masseau force-pushed the ecm/example-app/4-native branch from c21ee7a to 8049209 Compare April 20, 2026 16:04
@evan-masseau evan-masseau force-pushed the ecm/example-app/5-docs branch from 9ff975d to f54d106 Compare April 20, 2026 16:04
@evan-masseau evan-masseau force-pushed the ecm/example-app/4-native branch from 8049209 to 9b6ece0 Compare April 20, 2026 16:17
@evan-masseau evan-masseau force-pushed the ecm/example-app/5-docs branch from f54d106 to e924d84 Compare April 20, 2026 16:17
@evan-masseau evan-masseau force-pushed the ecm/example-app/4-native branch from 9b6ece0 to 6f7cf7b Compare April 20, 2026 16:25
@evan-masseau evan-masseau force-pushed the ecm/example-app/5-docs branch from e924d84 to 970fcf7 Compare April 20, 2026 16:25
@evan-masseau evan-masseau force-pushed the ecm/example-app/4-native branch from 6f7cf7b to 6f30213 Compare April 20, 2026 16:32
@evan-masseau evan-masseau force-pushed the ecm/example-app/5-docs branch from 970fcf7 to 22f3bcf Compare April 20, 2026 16:32
@evan-masseau evan-masseau force-pushed the ecm/example-app/5-docs branch from 182a164 to 88e9195 Compare April 20, 2026 21:33
@evan-masseau evan-masseau force-pushed the ecm/example-app/4-native branch from cd1dde7 to d126ffe Compare April 20, 2026 21:58
@evan-masseau evan-masseau force-pushed the ecm/example-app/5-docs branch from 88e9195 to 4dbe5b6 Compare April 20, 2026 21:59
@evan-masseau evan-masseau force-pushed the ecm/example-app/4-native branch from d126ffe to f4da17e Compare April 20, 2026 22:14
@evan-masseau evan-masseau force-pushed the ecm/example-app/5-docs branch from 4dbe5b6 to 5d4d25c Compare April 20, 2026 22:14
@evan-masseau evan-masseau force-pushed the ecm/example-app/4-native branch from f4da17e to e0b8e13 Compare April 21, 2026 00:12
@evan-masseau evan-masseau force-pushed the ecm/example-app/5-docs branch from 5d4d25c to 34740f6 Compare April 21, 2026 00:13
@evan-masseau evan-masseau force-pushed the ecm/example-app/4-native branch from e0b8e13 to a3d7d0a Compare April 21, 2026 00:20
@evan-masseau evan-masseau force-pushed the ecm/example-app/5-docs branch from 34740f6 to 38cf2b3 Compare April 21, 2026 00:20
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)
@evan-masseau evan-masseau force-pushed the ecm/example-app/4-native branch from a3d7d0a to ac8ae77 Compare April 21, 2026 01:43
@evan-masseau evan-masseau force-pushed the ecm/example-app/5-docs branch from 38cf2b3 to 24c4040 Compare April 21, 2026 01:43
@evan-masseau evan-masseau force-pushed the ecm/example-app/4-native branch from ac8ae77 to 92f82d4 Compare April 21, 2026 02:49
@evan-masseau evan-masseau force-pushed the ecm/example-app/5-docs branch from 24c4040 to c23fce4 Compare April 21, 2026 02:49
@evan-masseau evan-masseau force-pushed the ecm/example-app/4-native branch from 92f82d4 to ac8ae77 Compare April 21, 2026 02:56
@evan-masseau evan-masseau force-pushed the ecm/example-app/5-docs branch 3 times, most recently from 91ef4e0 to 58c04fc Compare April 21, 2026 15:36
@evan-masseau evan-masseau force-pushed the ecm/example-app/4-native branch from ac8ae77 to bf39103 Compare April 21, 2026 16:54
@evan-masseau evan-masseau force-pushed the ecm/example-app/5-docs branch 2 times, most recently from a944088 to a67643e Compare April 21, 2026 17:04
@ab1470 ab1470 force-pushed the ecm/example-app/4-native branch from bf39103 to cad277c Compare April 21, 2026 17:16
@evan-masseau evan-masseau force-pushed the ecm/example-app/5-docs branch 2 times, most recently from aff910d to 537311c Compare April 21, 2026 17:48
@evan-masseau evan-masseau force-pushed the ecm/example-app/4-native branch from cad277c to b2fb2b6 Compare April 21, 2026 17:54
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
evan-masseau and others added 2 commits April 21, 2026 16:56
…-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-inc-faae60d47d
Copy link
Copy Markdown

Wiz Scan Summary

Scanner Findings
Vulnerability Finding Vulnerabilities -
Data Finding Sensitive Data -
Secret Finding Secrets -
IaC Misconfiguration IaC Misconfigurations -
SAST Finding SAST Findings 5 Low
Software Management Finding Software Management Findings -
Total 5 Low

View scan details in Wiz

To detect these findings earlier in the dev lifecycle, try using Wiz Code VS Code Extension.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant