From d3b3d1773ec860539ab91eb7cb96ea8c1ec05960 Mon Sep 17 00:00:00 2001 From: Jordan Morgan Date: Wed, 25 Feb 2026 13:42:16 -0600 Subject: [PATCH 1/4] Firebase docs: use setIntegrationAttributes Require the Firebase App Instance ID to be provided via setIntegrationAttributes (replacing prior setUserAttributes guidance). Adds a prominent warning, updates iOS/Android examples, adds a Flutter example, clarifies troubleshooting/best-practices around App Instance ID, and updates changelog metadata (lastUpdated and multiple entries). --- content/docs/integrations/firebase.mdx | 44 ++++-- src/lib/changelog-entries.json | 191 ++++++++++++++++++++++++- 2 files changed, 220 insertions(+), 15 deletions(-) diff --git a/content/docs/integrations/firebase.mdx b/content/docs/integrations/firebase.mdx index 08a133c8..f1559347 100644 --- a/content/docs/integrations/firebase.mdx +++ b/content/docs/integrations/firebase.mdx @@ -5,6 +5,10 @@ description: "The Firebase integration automatically sends Superwall subscriptio The Firebase integration automatically sends Superwall subscription and payment events to Firebase Analytics (Google Analytics 4) using the Measurement Protocol. Track subscription lifecycle events, analyze revenue metrics, and leverage Firebase's powerful analytics capabilities with automatic event mapping and ecommerce tracking. + +This integration requires you to set the Firebase App Instance ID using `setIntegrationAttributes` in your client app. Without it, events will be silently skipped and won't appear in Firebase Analytics. See [App Instance ID Requirement](#app-instance-id-requirement) for setup instructions. + + ## Features - **Standard Ecommerce Events**: Uses Firebase's standard `purchase` and `refund` events for revenue tracking @@ -76,11 +80,11 @@ Firebase requires separate credentials for iOS and Android apps since each platf ## App Instance ID Requirement -**Critical**: The Firebase integration requires `firebaseAppInstanceId` to be set in the user's `userAttributes` from your client app. This is the unique installation identifier from the Firebase Analytics SDK. +**Critical**: The Firebase integration requires the Firebase App Instance ID to be set from your client app using `setIntegrationAttributes`. This is the unique installation identifier from the Firebase Analytics SDK, and without it events will be silently skipped. ### How to Set It Up -1. In your app, retrieve the Firebase App Instance ID: +Retrieve the Firebase App Instance ID and pass it to Superwall using `setIntegrationAttributes`: **iOS (Swift):** ```swift @@ -88,8 +92,8 @@ import FirebaseAnalytics Analytics.appInstanceID { appInstanceId, error in if let appInstanceId = appInstanceId { - Superwall.shared.setUserAttributes([ - "firebaseAppInstanceId": appInstanceId + Superwall.shared.setIntegrationAttributes([ + .firebaseInstallationId: appInstanceId ]) } } @@ -100,15 +104,27 @@ Analytics.appInstanceID { appInstanceId, error in import com.google.firebase.analytics.FirebaseAnalytics FirebaseAnalytics.getInstance(context).appInstanceId.addOnSuccessListener { appInstanceId -> - Superwall.instance.setUserAttributes(mapOf( - "firebaseAppInstanceId" to appInstanceId + Superwall.instance.setIntegrationAttributes(mapOf( + IntegrationAttribute.FIREBASE_APP_INSTANCE_ID to appInstanceId )) } ``` +**Flutter (Dart):** +```dart +import 'package:firebase_analytics/firebase_analytics.dart'; + +final appInstanceId = await FirebaseAnalytics.instance.appInstanceId; +if (appInstanceId != null) { + await Superwall.shared.setIntegrationAttributes({ + IntegrationAttribute.firebaseAppInstanceId: appInstanceId, + }); +} +``` + ### What Happens Without It -If `firebaseAppInstanceId` is not found in `userAttributes`: +If the Firebase App Instance ID is not set via `setIntegrationAttributes`: - The event is **skipped** (not sent to Firebase) ### Revenue Events (Standard Ecommerce) @@ -320,7 +336,7 @@ After sending events, verify in Firebase: ## Best Practices -1. **Set App Instance ID Early**: Call `setUserAttributes` with `firebaseAppInstanceId` as soon as the app launches to ensure all subscription events are tracked. +1. **Set App Instance ID Early**: Call `setIntegrationAttributes` with the Firebase App Instance ID as soon as the app launches to ensure all subscription events are tracked. 2. **Separate Environments**: Use separate Firebase projects (or at minimum, separate measurement streams) for sandbox and production to keep analytics clean. @@ -363,7 +379,7 @@ Calculate: Sum of value per user ### Events Not Appearing in Firebase -1. **Check App Instance ID**: Ensure `firebaseAppInstanceId` is set in `userAttributes` +1. **Check App Instance ID**: Ensure the Firebase App Instance ID is set via `setIntegrationAttributes` 2. **Verify Platform Credentials**: Confirm the correct platform credentials are configured: - iOS events (App Store) require `ios_firebase_app_id` + `ios_api_secret` - Android events (Play Store) require `android_firebase_app_id` + `android_api_secret` @@ -381,14 +397,14 @@ Calculate: Sum of value per user **Single-Platform Apps**: If your app is iOS-only or Android-only, you only need to configure credentials for that platform. Events from unconfigured platforms will be skipped (this is expected behavior). -### Missing firebaseAppInstanceId +### Missing Firebase App Instance ID -**Problem**: Events are being skipped with warning about missing `firebaseAppInstanceId` +**Problem**: Events are being skipped with warning about missing Firebase App Instance ID **Solutions**: -1. Ensure your app calls `FirebaseAnalytics.getAppInstanceId()` and passes it to Superwall -2. Verify `setUserAttributes` is called before any purchases occur -3. Check that the attribute key is exactly `firebaseAppInstanceId` (case-sensitive) +1. Ensure your app calls `FirebaseAnalytics.getAppInstanceId()` (or platform equivalent) and passes it to Superwall via `setIntegrationAttributes` +2. Verify `setIntegrationAttributes` is called before any purchases occur +3. Use the correct integration attribute key for your platform (e.g., `.firebaseInstallationId` on iOS, `IntegrationAttribute.firebaseAppInstanceId` on Flutter) ### Revenue Not Tracking diff --git a/src/lib/changelog-entries.json b/src/lib/changelog-entries.json index a15b60e9..9f619737 100644 --- a/src/lib/changelog-entries.json +++ b/src/lib/changelog-entries.json @@ -1,6 +1,195 @@ { - "lastUpdated": "2026-02-19T17:46:40.862Z", + "lastUpdated": "2026-02-25T19:22:26.331Z", "entries": [ + { + "key": "content/docs/ios/guides/test-mode.mdx:2824ed71fb4bae46a9c080b9bf663e78bd4adc76", + "path": "ios/guides/test-mode.mdx", + "title": "Test Mode", + "description": "New guides for iOS SDK", + "category": "iOS SDK", + "subcategory": "Guides", + "url": "/docs/ios/guides/test-mode", + "date": "2026-02-24T15:55:53.000Z", + "changeType": "added" + }, + { + "key": "content/docs/dashboard/dashboard-creating-paywalls/paywall-editor-styling-elements.mdx:2824ed71fb4bae46a9c080b9bf663e78bd4adc76", + "path": "dashboard/dashboard-creating-paywalls/paywall-editor-styling-elements.mdx", + "title": "Styling Elements", + "description": "Updated creating paywalls for Dashboard", + "category": "Dashboard", + "subcategory": "Creating Paywalls", + "url": "/docs/dashboard/dashboard-creating-paywalls/paywall-editor-styling-elements", + "date": "2026-02-24T15:55:53.000Z", + "changeType": "modified" + }, + { + "key": "content/docs/flutter/changelog.mdx:8bb42154b95e4a3c3a91c023416e8149263c208a", + "path": "flutter/changelog.mdx", + "title": "Changelog", + "description": "Updated Flutter SDK documentation", + "category": "Flutter SDK", + "url": "/docs/flutter/changelog", + "date": "2026-02-23T17:39:35.000Z", + "changeType": "modified" + }, + { + "key": "content/docs/flutter/index.mdx:8bb42154b95e4a3c3a91c023416e8149263c208a", + "path": "flutter/index.mdx", + "title": "Welcome", + "description": "Updated Flutter SDK documentation", + "category": "Flutter SDK", + "url": "/docs/flutter", + "date": "2026-02-23T17:39:35.000Z", + "changeType": "modified" + }, + { + "key": "content/docs/flutter/sdk-reference/index.mdx:8bb42154b95e4a3c3a91c023416e8149263c208a", + "path": "flutter/sdk-reference/index.mdx", + "title": "Overview", + "description": "Updated sdk reference for Flutter SDK", + "category": "Flutter SDK", + "subcategory": "SDK Reference", + "url": "/docs/flutter/sdk-reference", + "date": "2026-02-23T17:39:35.000Z", + "changeType": "modified" + }, + { + "key": "content/docs/android/changelog.mdx:90c26e89d5b4eea20fc2483b063fac3359d3a666", + "path": "android/changelog.mdx", + "title": "Changelog", + "description": "Updated Android SDK documentation", + "category": "Android SDK", + "url": "/docs/android/changelog", + "date": "2026-02-21T00:00:02.000Z", + "changeType": "modified" + }, + { + "key": "content/docs/android/index.mdx:90c26e89d5b4eea20fc2483b063fac3359d3a666", + "path": "android/index.mdx", + "title": "Welcome", + "description": "Updated Android SDK documentation", + "category": "Android SDK", + "url": "/docs/android", + "date": "2026-02-21T00:00:02.000Z", + "changeType": "modified" + }, + { + "key": "content/docs/android/quickstart/install.mdx:90c26e89d5b4eea20fc2483b063fac3359d3a666", + "path": "android/quickstart/install.mdx", + "title": "Install the SDK", + "description": "Updated quickstart for Android SDK", + "category": "Android SDK", + "subcategory": "Quickstart", + "url": "/docs/android/quickstart/install", + "date": "2026-02-21T00:00:02.000Z", + "changeType": "modified" + }, + { + "key": "content/docs/android/sdk-reference/index.mdx:90c26e89d5b4eea20fc2483b063fac3359d3a666", + "path": "android/sdk-reference/index.mdx", + "title": "Overview", + "description": "Updated sdk reference for Android SDK", + "category": "Android SDK", + "subcategory": "SDK Reference", + "url": "/docs/android/sdk-reference", + "date": "2026-02-21T00:00:02.000Z", + "changeType": "modified" + }, + { + "key": "content/docs/dashboard/dashboard-settings/overview-settings.mdx:f67a22d4c7806bcff37fc771c678e444e6102c56", + "path": "dashboard/dashboard-settings/overview-settings.mdx", + "title": "General", + "description": "Updated settings for Dashboard", + "category": "Dashboard", + "subcategory": "Settings", + "url": "/docs/dashboard/dashboard-settings/overview-settings", + "date": "2026-02-19T23:32:36.000Z", + "changeType": "modified" + }, + { + "key": "content/docs/web-checkout/web-checkout-sdk-setup.mdx:2488c662531b501f7b4f160e969038e51aa2b0ba", + "path": "web-checkout/web-checkout-sdk-setup.mdx", + "title": "SDK Setup", + "description": "New Web Checkout documentation", + "category": "Web Checkout", + "url": "/docs/web-checkout/web-checkout-sdk-setup", + "date": "2026-02-19T19:09:31.000Z", + "changeType": "added" + }, + { + "key": "content/docs/ios/changelog.mdx:ef47cc98d95c72f4b99ccd71144eb35c5c25d12f", + "path": "ios/changelog.mdx", + "title": "Changelog", + "description": "Updated iOS SDK documentation", + "category": "iOS SDK", + "url": "/docs/ios/changelog", + "date": "2026-02-19T01:27:18.000Z", + "changeType": "modified" + }, + { + "key": "content/docs/ios/index.mdx:ef47cc98d95c72f4b99ccd71144eb35c5c25d12f", + "path": "ios/index.mdx", + "title": "Welcome", + "description": "Updated iOS SDK documentation", + "category": "iOS SDK", + "url": "/docs/ios", + "date": "2026-02-19T01:27:18.000Z", + "changeType": "modified" + }, + { + "key": "content/docs/ios/sdk-reference/index.mdx:ef47cc98d95c72f4b99ccd71144eb35c5c25d12f", + "path": "ios/sdk-reference/index.mdx", + "title": "Overview", + "description": "Updated sdk reference for iOS SDK", + "category": "iOS SDK", + "subcategory": "SDK Reference", + "url": "/docs/ios/sdk-reference", + "date": "2026-02-19T01:27:18.000Z", + "changeType": "modified" + }, + { + "key": "content/docs/expo/changelog.mdx:69b5351a6bb335ee8f92f9f12cfc17b02db9dc92", + "path": "expo/changelog.mdx", + "title": "Changelog", + "description": "Updated Expo SDK documentation", + "category": "Expo SDK", + "url": "/docs/expo/changelog", + "date": "2026-02-19T01:26:44.000Z", + "changeType": "modified" + }, + { + "key": "content/docs/expo/index.mdx:69b5351a6bb335ee8f92f9f12cfc17b02db9dc92", + "path": "expo/index.mdx", + "title": "Welcome", + "description": "Updated Expo SDK documentation", + "category": "Expo SDK", + "url": "/docs/expo", + "date": "2026-02-19T01:26:44.000Z", + "changeType": "modified" + }, + { + "key": "content/docs/expo/sdk-reference/index.mdx:69b5351a6bb335ee8f92f9f12cfc17b02db9dc92", + "path": "expo/sdk-reference/index.mdx", + "title": "Overview", + "description": "Updated sdk reference for Expo SDK", + "category": "Expo SDK", + "subcategory": "SDK Reference", + "url": "/docs/expo/sdk-reference", + "date": "2026-02-19T01:26:44.000Z", + "changeType": "modified" + }, + { + "key": "content/docs/flutter/sdk-reference/PaywallPresentationHandler.mdx:1cdac17265b4e8d6390a554fb62020c77c7989dd", + "path": "flutter/sdk-reference/PaywallPresentationHandler.mdx", + "title": "PaywallPresentationHandler", + "description": "Updated sdk reference for Flutter SDK", + "category": "Flutter SDK", + "subcategory": "SDK Reference", + "url": "/docs/flutter/sdk-reference/PaywallPresentationHandler", + "date": "2026-02-19T01:24:55.000Z", + "changeType": "modified" + }, { "key": "content/docs/ios/guides/test-mode.mdx:ab65878499ce2ae1e2bd0afc2be7b48c3b358e46", "path": "ios/guides/test-mode.mdx", From d1f01582075e487c45b47428b49c20ea9dba7710 Mon Sep 17 00:00:00 2001 From: Jordan Morgan Date: Wed, 25 Feb 2026 13:53:17 -0600 Subject: [PATCH 2/4] Comment out paywalls list/metrics sections Wrap several MDX blocks in JSX comments to disable rendering: the Note about the date toggle, the "Viewing paywalls as a table or list" section (including image and list note), and the "Viewing paywall metrics" section (and a closing Tip). This preserves the original content in source while removing it from the rendered docs, likely to hide outdated or duplicated documentation temporarily. --- content/docs/dashboard/paywalls.mdx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/content/docs/dashboard/paywalls.mdx b/content/docs/dashboard/paywalls.mdx index 0ca21500..3c5f15b8 100644 --- a/content/docs/dashboard/paywalls.mdx +++ b/content/docs/dashboard/paywalls.mdx @@ -22,10 +22,10 @@ You can toggle which paywalls are showing by using the **date toggle**, located Choose **Custom** to select any arbitrary date range to filter by. - +{/* The date toggle works when you are viewing your paywalls as a - [**list**](#viewing-paywall-metrics). - + **list**. + */} ### Viewing paywalls by status @@ -48,15 +48,15 @@ Here's what each status means: | Archived | Paywalls that have been archived. These can be restored if needed. | | Search | Perform a search across all of the app's paywalls, regardless of its status. | -### Viewing paywalls as a table or list +{/* ### Viewing paywalls as a table or list To toggle between viewing your paywalls by either a table or list, click the toggle buttons at the top: ![](/images/paywalls-overview-toggle-by-metric.png) -When viewing them as a **list**, Superwall also displays additional metrics. +When viewing them as a **list**, Superwall also displays additional metrics. */} -### Viewing paywall metrics +{/* ### Viewing paywall metrics Choose the **list** view to see high-level metrics about each paywall: @@ -81,7 +81,7 @@ Each metric displays the data in the time frame that's selected from the date to Click on any of these values at the top of the list to order the data by that metric, either ascending or descending. - + */} ### Creating a new paywall From e873f5da0de8561f358a35546f74d7a03aeb0029 Mon Sep 17 00:00:00 2001 From: Jordan Morgan Date: Wed, 25 Feb 2026 14:18:40 -0600 Subject: [PATCH 3/4] Deduplicate changelog entries by path Add deduplicateByPath which groups entries by path and collapses entries that occur within a 24-hour window (DEDUP_WINDOW_MS), keeping only the most recent per group. Integrate this into ChangelogTimeline so entries are first filtered to the recent months and then deduplicated to reduce noise from the same deploy/commit batch while preserving separate updates that are more than 24 hours apart and maintaining descending date order. --- src/components/ChangelogTimeline.tsx | 46 ++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/src/components/ChangelogTimeline.tsx b/src/components/ChangelogTimeline.tsx index 3d3a6558..0ae7970d 100644 --- a/src/components/ChangelogTimeline.tsx +++ b/src/components/ChangelogTimeline.tsx @@ -49,6 +49,46 @@ function filterToRecentMonths( }); } +/** + * Deduplicate entries by path when they fall within a short time window of each + * other (e.g. same deploy / commit batch). Genuine updates days or weeks apart + * are preserved as separate entries. + */ +const DEDUP_WINDOW_MS = 24 * 60 * 60 * 1000; // 24 hours + +function deduplicateByPath( + entries: ChangelogDataType['entries'] +): ChangelogDataType['entries'] { + const grouped = new Map(); + + for (const entry of entries) { + if (!grouped.has(entry.path)) { + grouped.set(entry.path, []); + } + grouped.get(entry.path)!.push(entry); + } + + const result: (typeof entries)[number][] = []; + + for (const group of grouped.values()) { + group.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()); + + let kept = group[0]; + result.push(kept); + + for (let i = 1; i < group.length; i++) { + const gap = new Date(kept.date).getTime() - new Date(group[i].date).getTime(); + if (gap > DEDUP_WINDOW_MS) { + kept = group[i]; + result.push(kept); + } + } + } + + result.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()); + return result; +} + /** * Get the month key for a date (e.g., "2026-01"). */ @@ -153,8 +193,10 @@ export function ChangelogTimeline() { return ; } - // Only show entries from the last 3 months - const recentEntries = filterToRecentMonths(data.entries, MONTHS_TO_SHOW); + // Only show entries from the last 3 months, deduplicated per page + const recentEntries = deduplicateByPath( + filterToRecentMonths(data.entries, MONTHS_TO_SHOW) + ); if (recentEntries.length === 0) { return ; From caae7a9237495ca00e331c5fa9f6dcc82d5283d4 Mon Sep 17 00:00:00 2001 From: Jordan Morgan Date: Wed, 25 Feb 2026 14:23:42 -0600 Subject: [PATCH 4/4] Update ChangelogTimeline.tsx --- src/components/ChangelogTimeline.tsx | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/components/ChangelogTimeline.tsx b/src/components/ChangelogTimeline.tsx index 0ae7970d..62704629 100644 --- a/src/components/ChangelogTimeline.tsx +++ b/src/components/ChangelogTimeline.tsx @@ -86,7 +86,20 @@ function deduplicateByPath( } result.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()); - return result; + + // Only the oldest entry per path can be "added" (New); later ones become "modified" (Update) + const firstSeen = new Map(); + for (let i = result.length - 1; i >= 0; i--) { + if (!firstSeen.has(result[i].path)) { + firstSeen.set(result[i].path, i); + } + } + return result.map((entry, i) => { + if (entry.changeType === 'added' && firstSeen.get(entry.path) !== i) { + return { ...entry, changeType: 'modified' as const }; + } + return entry; + }); } /**