Skip to content

chore!: publish 4.0 to Swift Package Manager only - #972

Merged
turnipdabeets merged 2 commits into
chore/v4-remove-deprecatedfrom
chore/v4-spm-only
Oct 27, 2026
Merged

turnipdabeets merged 2 commits into
chore/v4-remove-deprecatedfrom
chore/v4-spm-only

Conversation

@turnipdabeets

@turnipdabeets turnipdabeets commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

💡 Motivation and Context

Follow-up to #873, addressing ioannisj's review question there (#873) on whether 4.0 should still ship on CocoaPods. Decision: 4.0 ships on Swift Package Manager only. 3.x stays on CocoaPods until trunk locks.

  • CocoaPods trunk goes read-only on 2026-12-02 (CocoaPods blog), so a 4.x pod would stop getting updates within weeks of 4.0.
  • Pod builds always compile every package trait in, so pod users can't leave out session replay, crash reporting or surveys.

What changes

Item Decision Why
release.yml publish-cocoapods job Removed The point of the PR. notify-released now waits on create-github-release, which is the last publishing step (SPM resolves from the tag). Dropped the "tag before CocoaPods publish" comment.
build.yml podspec-lint job ("CocoaPods package lint") Removed It lints a package we no longer publish. Not a required check.
build.yml min-toolchain job: CocoaPods install + "Build SDK with Swift 5 language mode via CocoaPods" steps Removed (job kept) The job "Build SDK (minimum toolchain, Xcode 26.0.1)" keeps its SPM builds (buildSdkSpm, buildSdkSpmNoTraits). Swift 5 mode is still covered: Package.swift sets swiftLanguageModes: [.v5] and the Xcode project builds PostHog with SWIFT_VERSION = 5.0.
build-examples.yml build-pods matrix (static lib, static lib on Xcode 26.0.1, static framework, dynamic framework) Removed These only exercised CocoaPods linkage modes (e.g. the static-library module-interface workaround in the podspec). The minimum-Xcode coverage stays in the min-toolchain job. None of these are required checks.
PostHogExampleWithPods/ + its scheme + its reference in PostHog.xcodeproj Removed It installs the pod from PostHog.podspec, so it can't build without one.
PostHog.podspec Deleted Nothing publishes or lints it anymore, so it would silently rot while bump-version.sh kept stamping 4.x versions on it. Keeping it would also invite pod 'PostHog', :git => installs of 4.x, which compile every trait in, the thing we're moving away from. It doesn't help the wrappers either: their podspecs depend on PostHog through trunk, which can't serve 4.x. 3.x keeps its podspec at every 3.x tag, so a pre-lock 3.x fix can still be pushed from a branch cut from the last 3.x tag.
Makefile releaseCocoaPods, buildExamplePods*, gem install cocoapods in bootstrap Removed They only served the above. buildExamples no longer includes pods.
scripts/bump-version.sh podspec line, PostHogVersion.swift comment, .swiftlint.yml exclusion, upload-symbols.sh usage comments Updated Dropped podspec/PODS_ROOT references; the symbol-upload examples now use the SPM path.
RELEASING.md Updated 4.x is SPM-only and the tag is the release. New "CocoaPods" section: 3.x pods stay installable but are frozen after the lock; a pre-lock 3.x pod can only come from a 3.x branch (link to the pod-ownership/token docs at the 3.92.5 tag); delete COCOAPODS_TRUNK_TOKEN once no more 3.x pods are planned.
README.md, CONTRIBUTING.md, AGENTS.md Updated README: new Installation section with SPM instructions and a "Moving from CocoaPods" note; replaced the CocoaPods version/platform badges (platforms now from Swift Package Index). CONTRIBUTING/AGENTS: dropped CocoaPods validation rows and setup.
.changeset/v4-major-release.md Updated New top **Breaking:** entry. No new changeset file.

Not changed: no deprecated flag on the pod (it would warn every React Native and Flutter app that gets the pod through a wrapper). 3.x pods on trunk are untouched.

Open question for reviewers: the release workflow only runs on main, so once this lands there's no automated 3.x pod release. The RELEASING.md wording assumes a manual push from a 3.x branch before the lock is enough. Say if you'd rather have a workflow for it.

Cost for the wrapper SDKs

React Native plugin (posthog-js packages/react-native-plugin): its podspec resolves posthog-ios from CocoaPods trunk by default (s.dependency 'PostHog', "~> #{posthog_ios_version}", currently 3.88.2). SPM is opt-in through "posthog.useSpm": "true" in ios/Podfile.properties.json, which calls RN's spm_dependency helper. Since trunk can't serve 4.x, adopting posthog-ios 4.0 means:

posthog_flutter needs the same check. It ships both a podspec (s.dependency 'PostHog', '>= 3.84.0', '< 4.0.0') and a Package.swift ("3.84.0" ..< "4.0.0"). Apps that resolve plugins through CocoaPods can't get 4.x, so its major that adopts 4.0 has to require Flutter's Swift Package Manager integration and raise its iOS 13.0 / macOS 10.15 minimums. I didn't check which integration Flutter uses by default across posthog_flutter's supported range (flutter: >=3.27.0).

Both were read from the main branches of posthog-js and posthog-flutter; neither repo is changed here.

💚 How did you test it?

  • actionlint 1.7.12 on release.yml, build.yml, build-examples.yml: no new findings. The two shellcheck notes it prints (SC2010, SC2143 in the check-changesets step) are already on the base branch.
  • make buildSdk: passes (Xcode project builds for iOS, macOS, Mac Catalyst, tvOS, watchOS and visionOS, plus buildSdkSpm and buildSdkSpmNoTraits). This covers the PostHog.xcodeproj edit.
  • make lint: passes (32 existing warnings, 0 serious).
  • plutil -lint PostHog.xcodeproj/project.pbxproj: OK. No PostHogExampleWithPods references left in the repo besides CHANGELOG.md.
  • git diff --check: clean.
  • Links: the CocoaPods blog post, swift.org package manager docs and Swift Package Index page return 200; RELEASING.md at tag 3.92.5 has the # CocoaPods Management heading the new link points to. The Swift Package Index shields badges (the existing Swift versions one and the new platforms one) both rendered "pending" when I checked.
  • Not run: the release workflow itself (it only runs on main), and the CI jobs I removed.

📝 Checklist

  • All commits are signed (see Commit signing)
  • 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

Edited the existing .changeset/v4-major-release.md instead of adding a new changeset.

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

  • Claude Code (Opus 5.5), run as a subagent from Anna's session. The maintainer made the SPM-only decision; the agent made the per-item CI/podspec calls above.
  • Tools: git, gh (read-only on chore!: iOS Major Release 4.0 #873, posthog-js and posthog-flutter), actionlint, make, plutil, curl for link checks.
  • make buildSdk couldn't run in the agent's git worktree because the example projects reference the package as ../../posthog-ios (the directory has to be named posthog-ios). It ran on a copy of this branch's files in a directory named posthog-ios.

🤖 Generated with Claude Code

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>
@turnipdabeets turnipdabeets self-assigned this Oct 27, 2026
@turnipdabeets turnipdabeets mentioned this pull request Oct 27, 2026
2 of 15 tasks
@github-actions

github-actions Bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

posthog-ios Compliance Report

Date: 2026-10-27T18:51:27.630402+00:00
Duration: 223888ms

⚠️ Some Tests Failed

46/47 tests passed, 1 failed


Capture Tests

⚠️ 29/30 tests passed, 1 failed

View Details
Test Status Duration
Format Validation.Event Has Required Fields ✅ 2935ms
Format Validation.Event Has Uuid ✅ 2966ms
Format Validation.Event Has Lib Properties ✅ 2882ms
Format Validation.Distinct Id Is String ✅ 2870ms
Format Validation.Token Is Present ✅ 2783ms
Format Validation.Custom Properties Preserved ✅ 2860ms
Format Validation.Event Has Timestamp ✅ 2731ms
Format Validation.Non Utc Event Timestamp Is Converted To Utc ✅ 2792ms
Retry Behavior.Retries On 503 ✅ 11217ms
Retry Behavior.Does Not Retry On 400 ✅ 4778ms
Retry Behavior.Does Not Retry On 401 ✅ 4672ms
Retry Behavior.Respects Retry After Header ✅ 5408ms
Retry Behavior.Implements Backoff ✅ 21222ms
Retry Behavior.Retries On 500 ✅ 9336ms
Retry Behavior.Retries On 502 ✅ 9339ms
Retry Behavior.Retries On 504 ✅ 5364ms
Retry Behavior.Max Retries Respected ❌ 21885ms
Deduplication.Generates Unique Uuids ✅ 2975ms
Deduplication.Preserves Uuid On Retry ✅ 5428ms
Deduplication.Preserves Uuid And Timestamp On Retry ✅ 10368ms
Deduplication.Preserves Uuid And Timestamp On Batch Retry ✅ 7734ms
Deduplication.No Duplicate Events In Batch ✅ 2912ms
Deduplication.Different Events Have Different Uuids ✅ 2842ms
Compression.Sends Gzip When Enabled ✅ 376ms
Batch Format.Uses Proper Batch Structure ✅ 2785ms
Batch Format.Flush With No Events Sends Nothing ✅ 314ms
Batch Format.Multiple Events Batched Together ✅ 2875ms
Error Handling.Does Not Retry On 403 ✅ 4660ms
Error Handling.Does Not Retry On 413 ✅ 4800ms
Error Handling.Retries On 408 ✅ 9212ms

Failures

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 ✅ 2830ms
Request Payload.Flags Request Uses V2 Query Param ✅ 2806ms
Request Payload.Flags Request Hits Flags Path Not Decide ✅ 2690ms
Request Payload.Flags Request Omits Authorization Header ✅ 2800ms
Request Payload.Token In Flags Body Matches Init ✅ 2749ms
Request Payload.Groups Round Trip ✅ 2827ms
Request Payload.Groups Default To Empty Object ✅ 2948ms
Request Payload.Disable Geoip False Propagates As Geoip Disable False ✅ 2809ms
Request Payload.Disable Geoip Omitted Defaults To False ✅ 2735ms
Request Payload.Flag Keys To Evaluate Contains Only Requested Key ✅ 2700ms
Request Lifecycle.No Flags Request On Init Alone ✅ 129ms
Request Lifecycle.No Flags Request On Normal Capture ✅ 2874ms
Request Lifecycle.Two Flag Calls Produce Two Remote Requests ✅ 5532ms
Request Lifecycle.Mock Response Value Is Returned To Caller ✅ 2700ms
Retry Behavior.Retries Flags On 502 ✅ 2757ms
Retry Behavior.Retries Flags On 504 ✅ 2885ms
Side Effect Events.Get Feature Flag Captures Feature Flag Called Event ✅ 3284ms

… tmp-972

# Conflicts:
#	PostHogExampleWithPods/PostHogExampleWithPods/AppDelegate.swift

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

Re-reviewed the updated snapshot. The CocoaPods withdrawal remains consistent across packaging, retained integrations, release automation and migration documentation, with the intentional break covered by the major changeset. No actionable findings.

AI-assisted review.

@turnipdabeets
turnipdabeets merged commit 54a392b into chore/v4-remove-deprecated Oct 27, 2026
43 checks passed
@turnipdabeets
turnipdabeets deleted the chore/v4-spm-only branch October 27, 2026 22:11
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