Repository navigation
chore!: iOS Major Release 4.0 - #873
turnipdabeets wants to merge 31 commits into
Conversation
There was a problem hiding this comment.
This review was skipped because it would exceed your organization's monthly flex usage limit. Raise the limit in billing settings or wait until the next billing period resets limits.
posthog-ios Compliance ReportDate: 2026-10-29T15:28:24.928520+00:00
|
| Test | Status | Duration |
|---|---|---|
| Format Validation.Event Has Required Fields | ❌ | 882ms |
| Format Validation.Event Has Uuid | ❌ | 635ms |
| Format Validation.Event Has Lib Properties | ✅ | 3876ms |
| Format Validation.Distinct Id Is String | ✅ | 2848ms |
| Format Validation.Token Is Present | ✅ | 3016ms |
| Format Validation.Custom Properties Preserved | ✅ | 632ms |
| Format Validation.Event Has Timestamp | ✅ | 2999ms |
| Format Validation.Non Utc Event Timestamp Is Converted To Utc | ✅ | 2732ms |
| Retry Behavior.Retries On 503 | ✅ | 11805ms |
| Retry Behavior.Does Not Retry On 400 | ✅ | 4930ms |
| Retry Behavior.Does Not Retry On 401 | ✅ | 4879ms |
| Retry Behavior.Respects Retry After Header | ✅ | 7780ms |
| Retry Behavior.Implements Backoff | ✅ | 21358ms |
| Retry Behavior.Retries On 500 | ✅ | 9350ms |
| Retry Behavior.Retries On 502 | ✅ | 9273ms |
| Retry Behavior.Retries On 504 | ✅ | 9384ms |
| Retry Behavior.Max Retries Respected | ❌ | 21765ms |
| Deduplication.Generates Unique Uuids | ✅ | 3169ms |
| Deduplication.Preserves Uuid On Retry | ✅ | 9230ms |
| Deduplication.Preserves Uuid And Timestamp On Retry | ✅ | 16265ms |
| Deduplication.Preserves Uuid And Timestamp On Batch Retry | ✅ | 7848ms |
| Deduplication.No Duplicate Events In Batch | ✅ | 3030ms |
| Deduplication.Different Events Have Different Uuids | ✅ | 3291ms |
| Compression.Sends Gzip When Enabled | ✅ | 2956ms |
| Batch Format.Uses Proper Batch Structure | ✅ | 2844ms |
| Batch Format.Flush With No Events Sends Nothing | ✅ | 373ms |
| Batch Format.Multiple Events Batched Together | ✅ | 2965ms |
| Error Handling.Does Not Retry On 403 | ✅ | 4900ms |
| Error Handling.Does Not Retry On 413 | ✅ | 4727ms |
| Error Handling.Retries On 408 | ✅ | 8845ms |
Failures
format_validation.event_has_required_fields
Expected 1 requests, got 0
format_validation.event_has_uuid
No requests recorded
retry_behavior.max_retries_respected
Expected 4 requests, got 6
Feature_Flags Tests
✅ 17/17 tests passed
View Details
| Test | Status | Duration |
|---|---|---|
| Request Payload.Request With Person Properties Device Id | ✅ | 2881ms |
| Request Payload.Flags Request Uses V2 Query Param | ✅ | 2885ms |
| Request Payload.Flags Request Hits Flags Path Not Decide | ✅ | 2767ms |
| Request Payload.Flags Request Omits Authorization Header | ✅ | 2893ms |
| Request Payload.Token In Flags Body Matches Init | ✅ | 2906ms |
| Request Payload.Groups Round Trip | ✅ | 2742ms |
| Request Payload.Groups Default To Empty Object | ✅ | 2843ms |
| Request Payload.Disable Geoip False Propagates As Geoip Disable False | ✅ | 2682ms |
| Request Payload.Disable Geoip Omitted Defaults To False | ✅ | 2820ms |
| Request Payload.Flag Keys To Evaluate Contains Only Requested Key | ✅ | 2802ms |
| Request Lifecycle.No Flags Request On Init Alone | ✅ | 110ms |
| Request Lifecycle.No Flags Request On Normal Capture | ✅ | 2923ms |
| Request Lifecycle.Two Flag Calls Produce Two Remote Requests | ✅ | 5434ms |
| Request Lifecycle.Mock Response Value Is Returned To Caller | ✅ | 2777ms |
| Retry Behavior.Retries Flags On 502 | ✅ | 2887ms |
| Retry Behavior.Retries Flags On 504 | ✅ | 2766ms |
| Side Effect Events.Get Feature Flag Captures Feature Flag Called Event | ✅ | 2973ms |
6db44c0 to
9393686
Compare
|
[High risk] Removes public API surface for iOS SDK major version. The PR appears safe to merge; no actionable regression was identified. Reviews (1) · Last reviewed commit: "chore!: remove deprecated public APIs fo..." |
e1cc8cf to
5b6c50e
Compare
ce9e3ac to
1fb0c40
Compare
There was a problem hiding this comment.
PostHog Review · Standard · reviewed 1fb0c40: 1 Must fix. This pull request is large, so the review ran in 4 parts with less depth than usual.
This is review round 9 on this pull request. When findings keep coming back in the same code, the approach is usually the problem. Before the next fix, check whether a different design would remove the whole class of findings.
…lay trait (#968) The suite uses replay-only test helpers, so the no-traits iOS test build failed. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…from the 4.0 changeset Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Thanks ioannisj! On the top-level points:
|
* chore!: remove the didReceiveFeatureFlags notification onFeatureFlags(_:) covers it: it reports the loaded flags and whether loading failed, is scoped to one SDK instance, and can be unsubscribed. The notification was posted with a nil object for every instance and carried no data. * chore: unsubscribe the example's flags listener when its model is released
… entry Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Session replay always records masked screenshots, so the deprecated property only read true and dropped writes. An app that set it to false to keep pixels out of recordings would compile against 4.0 with just a warning. Deleting it makes the compiler flag the assignment, matching the screenshotModeBackgroundCapture removal. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…rap (#974) Passing a [(PostHogEvent) -> PostHogEvent?] (or the logs equivalent) to setBeforeSend(_:) force-cast the array to the @sendable element type and trapped at runtime in Swift 5 mode. Add disfavored overloads for the plain function types that box each element and forward, so no cast happens. Literals, variadic calls and [BeforeSendBlock] arrays still resolve to the @sendable overloads. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
* chore!: remove the public API of disabled package traits Gate trait-specific public API with the same `!SWIFT_PACKAGE || <Trait>` condition as its implementation, so an SPM app that turns a trait off gets a compile error instead of an option that silently does nothing. - SessionReplay: PostHogConfig.sessionReplay/sessionReplayConfig, PostHogSessionReplayConfig, PostHogSessionReplayConsoleLogConfig, PostHogConsoleLogEntry/Level, start/stopSessionRecording(), isSessionReplayActive(), View.postHogNoMask() and the replay SPI. - CrashReporting: PostHogErrorTrackingConfig.autoCapture. - Surveys: PostHogConfig.surveys/surveysConfig, PostHogSurveysConfig, PostHogSurveysDelegate and its callback typealiases, the display, response and next-question types. Add api/posthog-ios.public-api.no-traits.txt (SPM build with --disable-default-traits), checked by `make apiCheck` in CI and regenerated by `make apiUpdate`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: regenerate the traits-off API snapshot with CI's Xcode 26.3 Xcode 26.4.1 prints @sendable on five closure types in the SPM symbol graph; CI's Xcode 26.3 doesn't, so apiCheck failed. --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Stop publishing to CocoaPods: CocoaPods trunk goes read-only on 2026-12-02, and pod builds always compile every package trait in. Remove the release workflow's CocoaPods publish job, the podspec lint and pod example CI jobs, PostHogExampleWithPods, PostHog.podspec and the pod Makefile targets. Document SPM-only distribution in README, RELEASING and the 4.0 changeset; 3.x stays on CocoaPods. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
PostHog Review · Standard · reviewed 732dc7d: 1 Should fix. This pull request is large, so the review ran in 4 parts with less depth than usual.
This is review round 10 on this pull request. When findings keep coming back in the same code, the approach is usually the problem. Before the next fix, check whether a different design would remove the whole class of findings.
* docs: add a 3.x to 4.0 migration guide Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: say which capture v1 errors are retried in the migration guide * docs: note the full-window container masking exception in the migration guide Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: note the UIKit image-mask exemptions in the migration guide Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: say a plain UIViewController reports no replay screen name Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: scope the capture status rules to capture v1 and cover 413 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
|
keep PRs like this in draft until its ready to go so its out of our review list |
Drops the removed didReceiveFeatureFlags and adds the plain-closure setBeforeSend overloads, matching the Xcode 26.3 output in CI. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
💡 Motivation and Context
Closes #557
The 4.0 major release branch. Its first change removes every public API that is already marked deprecated; the other 4.0 changes are listed under "Also on this branch".
Important
Don't merge yet. This PR targets
mainwithout changesets pre mode, so merging it publishes 4.0.0. It stays open until everything under "Before merging" is in, which keepsmainfree for 3.x fixes in the meantime.PostHogConfig.apiKey,init(apiKey:),init(apiKey:host:)projectToken,init(projectToken:),init(projectToken:host:)PostHogConfig.propertiesSanitizer,PostHogPropertiesSanitizerPostHogConfig.setBeforeSend(_:)PostHogConfig.evaluationEnvironmentsevaluationContextsPostHogConfig.remoteConfigPostHogSDK.getFeatureFlagPayload(_:)getFeatureFlagResult(_:sendFeatureFlagEvent:)?.payloadPostHogSessionReplayConfig.debouncerDelaythrottleDelayPostHogSessionReplayConfig.maskPhotoLibraryImagesPublic API change: this is a breaking removal, see the
api/posthog-ios.public-api.txtdiff.Left in place on purpose:
PostHog/Utils/Reachability.swift, which is internal vendored code.PostHogConfig(apiKey:)inscripts/downgrade-compatibility-smoke, which compiles against 3.48.0.PostHogEvent.apiKeyandPostHogRemoteConfig.getFeatureFlagPayload.migrateLegacyStorage(added in 3.20) andmergeLegacyContainerIfNeeded(the app-group merge, added in 3.27). Both run on every init, but removing them would cost data. If someone updates an app straight from an older version to 4.x, their users lose their distinct ID and any queued events. In the last 7 days (US), about 8% of iOS SDK end-users were on an app build older than 3.27, and about 6% of apps had not shipped 3.27 or later. The app-group merge only affects apps that setappGroupIdentifier, so its share is smaller. We'll revisit in a later major.Wrapper SDKs: posthog-flutter called
getFeatureFlagPayload; PostHog/posthog-flutter#630 moves it off. The React Native plugin calls none of the removed APIs. Both pin posthog-ios below 4.0.Also on this branch
This branch is the 4.0 integration branch. Each item below was reviewed and merged as its own PR. Their changesets are combined into one release entry,
.changeset/v4-major-release.md.swift-tools-version: 6.2, withPackage@swift-5.9.swiftremoved (chore!: require Xcode 26 (swift-tools-version 6.2) #898).SessionReplay,CrashReportingandSurveysSwiftPM traits, on by default (feat: add SessionReplay and CrashReporting package traits #916).PostHogDataMode.cellular(chore!: remove PostHogDataMode.cellular for v4 #890)PostHogConsoleLog*(chore!: rename console log types to PostHogConsoleLog* for v4 #912)reloadFeatureFlagscallback receives the reload result (chore!: pass the reload result to reloadFeatureFlags' callback #913)nil(chore!: return nil for malformed feature flag payloads #903)screenshotModeBackgroundCapture(chore!: remove experimental screenshotModeBackgroundCapture #937)PostHogSDK.didReceiveFeatureFlagsnotification: useonFeatureFlags(_:), which reports the flags and load errors per SDK instance. posthog-flutter moves to the listener in chore!: adopt posthog-ios 4.0 and posthog-android 4.0 posthog-flutter#649. This one lands through chore!: remove the didReceiveFeatureFlags notification #965.$setare dropped; 3.x migrations are kept (chore!: remove the v2 legacy queue migration for v4 #875).NWPathMonitor(chore!: replace vendored Reachability with NWPathMonitor #909).macos-15with Xcode 26.0.1.buildSdkSpmNoTraits,testNoTraits).Distribution: Swift Package Manager only
4.0 is published to Swift Package Manager only, not CocoaPods. CocoaPods trunk goes read-only on 2026-12-02, so any 4.x pod would stop getting updates within weeks. Pod builds also always compile every trait in, so CocoaPods users can't leave out replay or crash reporting. 3.x stays on CocoaPods until the lock, and the CocoaPods-to-SPM notice goes in the README, the changelog and the migration guide.
Cost for the React Native plugin, which resolves posthog-ios through CocoaPods by default and supports SPM only as an opt-in (
posthog.useSpm):spm_dependencyhelper.expo-build-propertieschange (verified in chore(react-native-plugin)!: resolve posthog-ios through Swift Package Manager posthog-js#5449).posthog_flutter needs the same check, since it ships both CocoaPods and SPM.
Wrapper SDK releases
posthog-ios 4.0 ships with a major release of each wrapper that embeds it. Each adoption PR waits until 4.0 is published.
Before merging
Follow-up PRs target this branch; each folds its line into the combined changeset.
setBeforeSend(_:)instead of crashing at runtime.screenshotModeinstead of ignoring it.didReceiveFeatureFlagsnotification.RELEASING.mdand the CocoaPods notice.unavailablefix-its for renamed and removed types, plus review nits.MIGRATION.mdcovering all of 4.0, starting with the changes the compiler doesn't flag.💚 How did you test it?
mainis merged in through 3.92.5.make testpasses locally (909 tests, macOS).make apiUpdate.make buildfor the other platforms and example apps, and the iOS simulator test job.📝 Checklist
If releasing new changes
pnpm changesetto generate a changeset fileThe changeset is
majorand was written by hand in the changeset format. The release workflow runs onmain, so merging this PR publishes 4.0.0.🤖 Agent context
Autonomy: Human-driven (agent-assisted)
@available(*, deprecated), plus the code, tests and comments that only existed to support them.🤖 Generated with Claude Code