Skip to content

feat: add push notification support - #511

Merged
ioannisj merged 21 commits into
mainfrom
feat/push-notifications
Aug 5, 2026
Merged

feat: add push notification support#511
ioannisj merged 21 commits into
mainfrom
feat/push-notifications

Conversation

@ioannisj

@ioannisj ioannisj commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

💡 Motivation and Context

PostHog Workflows can send push notifications, and both native SDKs already ship the full feature (posthog-ios 3.69.0 via PostHog/posthog-ios#729, #735 and #743, posthog-android 3.58.0 via PostHog/posthog-android#642 and #667). Flutter apps can't reach any of it: the plugin never forwarded the push config flags and the manual APIs weren't bridged.

Ports it as a config + API bridge. The native SDKs own the whole subscription state machine (retry with backoff, durable pending records, 401 re-mint, re-registration on identify/reset, opt-out gating); nothing is reimplemented in Dart.

What's new:

  • PostHogConfig gains capturePushNotificationSubscriptions and capturePushNotificationOpened (both default true, matching the natives) plus a pushIdentityProvider hook for projects that require identity verification.
  • Three manual APIs: registerPushNotificationToken, unregisterPushNotificationToken and capturePushNotificationOpened. These are more load-bearing on Flutter than on native: firebase_messaging apps handle push in Dart, so live token refresh and warm-start taps are only reachable through them.
  • The identity mint is the plugin's first native to Dart call that expects a reply. A closure can't cross the channel, so toMap() sends pushIdentityProviderEnabled and native calls pushIdentityProvider back over the channel when it needs a token. With multiple Flutter engines, the engine that installed a provider owns the mint route; when it detaches, a surviving provider-enabled engine gets promoted.
  • AUTO_INIT apps opt out with the com.posthog.posthog.CAPTURE_PUSH_NOTIFICATION_SUBSCRIPTIONS / com.posthog.posthog.CAPTURE_PUSH_NOTIFICATION_OPENED keys (Info.plist on iOS, AndroidManifest <meta-data> on Android). The provider hook isn't available there, since the native SDK is set up before any Dart runs.
  • Native floors move to the first releases carrying push: podspec and Package.swift to 3.69.0, gradle to [3.58.0,4.0.0). Web is a no-op (posthog-js has no push feature).

Platform divergences are kept exactly as the natives define them: subtitle is iOS-only (the Android bridge drops it), a null appId falls back to the bundle id on iOS and to the Firebase projectId (via reflection, no Firebase dependency added) on Android, and register/unregister no-op on macOS while opened capture works there.

One trap for the docs: iOS registers the raw APNs token, Android the FCM token. FirebaseMessaging.instance.onTokenRefresh yields FCM tokens on both platforms, so wiring it must be guarded with Platform.isAndroid or it overwrites the correct APNs token on iOS and silently breaks iOS delivery. The full firebase_messaging wiring guide is a follow-up to posthog.com docs.

Out of scope: notification rendering, channels and permission prompting stay with the host app, and no Firebase/APNs credentials were added to example/.

💚 How did you test it?

  • flutter test (265 tests): the argument maps for the three APIs (including appId omitted when null), the pushIdentityProvider round-trip driven by an inbound MethodCall, a throwing provider returning null, and config serialization.
  • ./gradlew :posthog_flutter:testDebugUnitTest: config-flag plumbing, the Firebase projectId fallback, subtitle being dropped, and the multi-engine mint-route ownership cases (anchoring, no steal on secondary setup, promotion on detach).
  • make checkFormatDart analyzeDart formatKotlin formatSwift; api/posthog_flutter.api.json regenerated and gated by checkApiDart in CI.
  • There's no Swift test target under darwin/, so the iOS bridge itself is covered by review only. The wire contract (POST /api/push_subscriptions/ body, retry, 401 re-mint) lives in the native SDKs and is covered by their suites; I didn't re-verify it end to end from Flutter.

📝 Checklist

  • I reviewed the submitted code.
  • I added tests to verify the changes.
  • I updated the docs if needed.
  • No breaking change or entry added to the changelog.

If releasing new changes

  • Ran pnpm changeset to generate a changeset file

🤖 Agent context

DRI: @ioannisj
Autonomy: Human-driven (agent-assisted)

ioannisj added 18 commits July 31, 2026 17:00
@ioannisj ioannisj self-assigned this Aug 4, 2026
@posthog

posthog Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

🦔 ReviewHog reviewed this pull request

Found 0 must fix, 1 should fix, 0 consider.

Published 1 finding (view the review).

@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

posthog-flutter Compliance Report

Date: 2026-08-04 13:01:48 UTC
Duration: 96837ms

✅ All Tests Passed!

45/45 tests passed


Capture Tests

29/29 tests passed

View Details
Test Status Duration
Format Validation.Event Has Required Fields 141ms
Format Validation.Event Has Uuid 120ms
Format Validation.Event Has Lib Properties 116ms
Format Validation.Distinct Id Is String 115ms
Format Validation.Token Is Present 114ms
Format Validation.Custom Properties Preserved 117ms
Format Validation.Event Has Timestamp 117ms
Retry Behavior.Retries On 503 5331ms
Retry Behavior.Does Not Retry On 400 2118ms
Retry Behavior.Does Not Retry On 401 2117ms
Retry Behavior.Respects Retry After Header 8126ms
Retry Behavior.Implements Backoff 15447ms
Retry Behavior.Retries On 500 5226ms
Retry Behavior.Retries On 502 5226ms
Retry Behavior.Retries On 504 5225ms
Retry Behavior.Max Retries Respected 15445ms
Deduplication.Generates Unique Uuids 125ms
Deduplication.Preserves Uuid On Retry 5223ms
Deduplication.Preserves Uuid And Timestamp On Retry 10335ms
Deduplication.Preserves Uuid And Timestamp On Batch Retry 5231ms
Deduplication.No Duplicate Events In Batch 124ms
Deduplication.Different Events Have Different Uuids 117ms
Compression.Sends Gzip When Enabled 116ms
Batch Format.Uses Proper Batch Structure 113ms
Batch Format.Flush With No Events Sends Nothing 108ms
Batch Format.Multiple Events Batched Together 124ms
Error Handling.Does Not Retry On 403 2116ms
Error Handling.Does Not Retry On 413 2117ms
Error Handling.Retries On 408 5225ms

Feature_Flags Tests

16/16 tests passed

View Details
Test Status Duration
Request Payload.Request With Person Properties Device Id 13ms
Request Payload.Flags Request Uses V2 Query Param 10ms
Request Payload.Flags Request Hits Flags Path Not Decide 9ms
Request Payload.Flags Request Omits Authorization Header 10ms
Request Payload.Token In Flags Body Matches Init 10ms
Request Payload.Groups Round Trip 10ms
Request Payload.Groups Default To Empty Object 11ms
Request Payload.Person Properties Distinct Id Auto Populated When Caller Omits It 10ms
Request Payload.Disable Geoip False Propagates As Geoip Disable False 9ms
Request Payload.Disable Geoip Omitted Defaults To False 10ms
Request Payload.Flag Keys To Evaluate Contains Only Requested Key 9ms
Request Lifecycle.No Flags Request On Init Alone 5ms
Request Lifecycle.No Flags Request On Normal Capture 113ms
Request Lifecycle.Two Flag Calls Produce Two Remote Requests 17ms
Request Lifecycle.Mock Response Value Is Returned To Caller 11ms
Side Effect Events.Get Feature Flag Captures Feature Flag Called Event 118ms

@posthog

posthog Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

ReviewHog Alpha 🦔 If you find any issues helpful - please reply "valid", "invalid", etc., for evaluation purposes 🙏

@posthog posthog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ReviewHog Report

Feature

Issues: 1 issue

Files (10)
  • posthog_flutter/darwin/posthog_flutter/Sources/posthog_flutter/PosthogFlutterPlugin.swift
  • posthog_flutter/darwin/posthog_flutter.podspec
  • posthog_flutter/darwin/posthog_flutter/Package.swift
  • example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
  • example/ios/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved
  • example/macos/Runner.xcodeproj/project.pbxproj
  • example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
  • example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
  • example/macos/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved
  • .changeset/fancy-owls-eat.md
What were the main changes
  • Adds registerPushNotificationToken (iOS-only), unregisterPushNotificationToken and capturePushNotificationOpened (subtitle iOS-only) bridging to posthog-ios
  • Implements the same multi-engine pushIdentityProvider mint-route pattern as Android: candidate list, setup-time anchor, promotion on detach, fast decline when unowned
  • Reads CAPTURE_PUSH_NOTIFICATION_SUBSCRIPTIONS / CAPTURE_PUSH_NOTIFICATION_OPENED Info.plist keys for the AUTO_INIT opt-out path
  • Bumps native posthog-ios floor to 3.69.0 in podspec, Package.swift, and example Package.resolved pins
  • macOS example project: adds FlutterGeneratedPluginSwiftPackage SPM dependency and a pre-build 'prepare Flutter framework' script action needed to consume the bumped SDK
  • Adds the changeset documenting the minor release and its default-on behavior/opt-out keys

@ioannisj
ioannisj marked this pull request as ready for review August 4, 2026 11:31
@ioannisj
ioannisj requested a review from a team as a code owner August 4, 2026 11:31
@ioannisj
ioannisj requested a review from a team August 4, 2026 11:31
@greptile-apps

greptile-apps Bot commented Aug 4, 2026

Copy link
Copy Markdown
Prompt To Fix All With AI
### Issue 1
example/macos/Runner.xcodeproj/project.pbxproj:257-259
**CocoaPods build requires SPM package**

When the macOS CocoaPods variant runs, the Runner target still resolves `FlutterGeneratedPluginSwiftPackage`, although Flutter only generates that package when Swift Package Manager integration is enabled, causing package resolution to fail before compilation.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Reviews (1): Last reviewed commit: "refactor: type the push method-call maps..." | Re-trigger Greptile

Comment thread example/macos/Runner.xcodeproj/project.pbxproj
Comment thread posthog_flutter/lib/src/posthog_config.dart Outdated
@ioannisj
ioannisj merged commit 2087837 into main Aug 5, 2026
27 checks passed
@ioannisj
ioannisj deleted the feat/push-notifications branch August 5, 2026 07:25
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.

2 participants