Skip to content

chore!: iOS Major Release 4.0 - #873

Draft
turnipdabeets wants to merge 31 commits into
mainfrom
chore/v4-remove-deprecated
Draft

turnipdabeets wants to merge 31 commits into
mainfrom
chore/v4-remove-deprecated

Conversation

@turnipdabeets

@turnipdabeets turnipdabeets commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

💡 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 main without changesets pre mode, so merging it publishes 4.0.0. It stays open until everything under "Before merging" is in, which keeps main free for 3.x fixes in the meantime.

Removed Replacement
PostHogConfig.apiKey, init(apiKey:), init(apiKey:host:) projectToken, init(projectToken:), init(projectToken:host:)
PostHogConfig.propertiesSanitizer, PostHogPropertiesSanitizer PostHogConfig.setBeforeSend(_:)
PostHogConfig.evaluationEnvironments evaluationContexts
PostHogConfig.remoteConfig none, it was a no-op
PostHogSDK.getFeatureFlagPayload(_:) getFeatureFlagResult(_:sendFeatureFlagEvent:)?.payload
PostHogSessionReplayConfig.debouncerDelay throttleDelay
PostHogSessionReplayConfig.maskPhotoLibraryImages none, it was a no-op

Public API change: this is a breaking removal, see the api/posthog-ios.public-api.txt diff.

Left in place on purpose:

  • The deprecated members in PostHog/Utils/Reachability.swift, which is internal vendored code.
  • PostHogConfig(apiKey:) in scripts/downgrade-compatibility-smoke, which compiles against 3.48.0.
  • The internal PostHogEvent.apiKey and PostHogRemoteConfig.getFeatureFlagPayload.
  • The storage migrations migrateLegacyStorage (added in 3.20) and mergeLegacyContainerIfNeeded (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 set appGroupIdentifier, 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.

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):

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.

Wrapper Release PR Adopts posthog-ios 4.0 Docs
posthog_flutter 6.0 PostHog/posthog-flutter#637 PostHog/posthog-flutter#649 PostHog/posthog.com#20924
@posthog/react-native-plugin 3.0 PostHog/posthog-js#5194 PostHog/posthog-js#5237, plus PostHog/posthog-js#5236 if #907 ships PostHog/posthog.com#20922

Before merging

Follow-up PRs target this branch; each folds its line into the combined changeset.

💚 How did you test it?

  • main is merged in through 3.92.5.
  • make test passes locally (909 tests, macOS).
  • The iOS SDK builds locally, via make apiUpdate.
  • Not run locally: make build for the other platforms and example apps, and the iOS simulator test job.
  • Tests that only covered the removed aliases are deleted.

📝 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

The changeset is major and was written by hand in the changeset format. The release workflow runs on main, so merging this PR publishes 4.0.0.

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

  • Written with Claude Code (Opus 5.5).
  • Scope is limited to declarations already carrying @available(*, deprecated), plus the code, tests and comments that only existed to support them.
  • Docs on posthog.com that mention the removed options are not updated here.

🤖 Generated with Claude Code

@turnipdabeets turnipdabeets self-assigned this Sep 30, 2026

@greptile-apps greptile-apps 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.

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.

@github-actions

github-actions Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

posthog-ios Compliance Report

Date: 2026-10-29T15:28:24.928520+00:00
Duration: 241947ms

⚠️ Some Tests Failed

44/47 tests passed, 3 failed


Capture Tests

⚠️ 27/30 tests passed, 3 failed

View Details
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

@greptile-apps

greptile-apps Bot commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Retrigger

[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..."

@turnipdabeets
turnipdabeets force-pushed the chore/v4-remove-deprecated branch from ce9e3ac to 1fb0c40 Compare October 26, 2026 16:28

@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.

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.

Comment thread PostHogTests/PostHogMaskScenarioTest.swift
…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>
@turnipdabeets

turnipdabeets commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor Author

Thanks ioannisj! On the top-level points:

  1. CocoaPods: going SPM-only in chore!: publish 4.0 to Swift Package Manager only #972. It updates RELEASING.md with what happens after the trunk lock, and the description spells out the React Native plugin cost (RN ≥ 0.75 and SPM by default, in chore(react-native-plugin)!: resolve posthog-ios through Swift Package Manager posthog-js#5449).
  2. Merge timing: keeping the base on main, but chore!: iOS Major Release 4.0 #873 stays open until feat!: send events through capture v1 and add captureAi #938, feat: add experimental GPU screenshot capture for session replay #911 and the follow-ups land. The checklist is under "Before merging" in the description.
  3. Last 3.x: we're skipping it. chore: 4.0 fix-it stubs for removed APIs and review nits #977 adds unavailable renamed/message fix-its for the removals apps are likely to hit, and docs: add a 3.x to 4.0 migration guide #978 covers the rest.
  4. Migration guide: docs: add a 3.x to 4.0 migration guide #978 adds MIGRATION.md, starting with the changes the compiler won't flag.

turnipdabeets and others added 6 commits October 27, 2026 13:53
* 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>

@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.

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.

Comment thread api/posthog-ios.public-api.no-traits.txt Outdated
turnipdabeets and others added 2 commits October 27, 2026 18:25
* 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>
… guide (#980)

#970 masks a full-window ph-no-capture view and its descendants, so the
exception no longer applies.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@marandaneto

Copy link
Copy Markdown
Member

keep PRs like this in draft until its ready to go so its out of our review list

turnipdabeets and others added 2 commits October 28, 2026 16:02
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>

This branch has not been deployed

No deployments
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.

Next iOS major version plan

4 participants