Repository navigation
feat: add Surveys package trait - #918
Merged
Merged
Conversation
turnipdabeets
marked this pull request as ready for review
October 7, 2026 01:26
Contributor
posthog-ios Compliance ReportDate: 2026-10-08 14:31:26 UTC
|
| Test | Status | Duration |
|---|---|---|
| Format Validation.Event Has Required Fields | ✅ | 2724ms |
| Format Validation.Event Has Uuid | ✅ | 2794ms |
| Format Validation.Event Has Lib Properties | ✅ | 3053ms |
| Format Validation.Distinct Id Is String | ✅ | 2812ms |
| Format Validation.Token Is Present | ✅ | 2750ms |
| Format Validation.Custom Properties Preserved | ✅ | 2711ms |
| Format Validation.Event Has Timestamp | ✅ | 2797ms |
| Retry Behavior.Retries On 503 | ✅ | 11895ms |
| Retry Behavior.Does Not Retry On 400 | ✅ | 2415ms |
| Retry Behavior.Does Not Retry On 401 | ✅ | 4695ms |
| Retry Behavior.Respects Retry After Header | ✅ | 7767ms |
| Retry Behavior.Implements Backoff | ✅ | 21731ms |
| Retry Behavior.Retries On 500 | ✅ | 9268ms |
| Retry Behavior.Retries On 502 | ✅ | 9278ms |
| Retry Behavior.Retries On 504 | ✅ | 8826ms |
| Retry Behavior.Max Retries Respected | ❌ | 21715ms |
| Deduplication.Generates Unique Uuids | ✅ | 2987ms |
| Deduplication.Preserves Uuid On Retry | ✅ | 5420ms |
| Deduplication.Preserves Uuid And Timestamp On Retry | ✅ | 16302ms |
| Deduplication.Preserves Uuid And Timestamp On Batch Retry | ✅ | 7838ms |
| Deduplication.No Duplicate Events In Batch | ✅ | 2891ms |
| Deduplication.Different Events Have Different Uuids | ✅ | 2836ms |
| Compression.Sends Gzip When Enabled | ✅ | 2731ms |
| Batch Format.Uses Proper Batch Structure | ✅ | 2746ms |
| Batch Format.Flush With No Events Sends Nothing | ✅ | 250ms |
| Batch Format.Multiple Events Batched Together | ✅ | 2977ms |
| Error Handling.Does Not Retry On 403 | ✅ | 2257ms |
| Error Handling.Does Not Retry On 413 | ✅ | 4778ms |
| Error Handling.Retries On 408 | ✅ | 9324ms |
Failures
retry_behavior.max_retries_respected
Expected 4 requests, got 6
Feature_Flags Tests
✅ 16/16 tests passed
View Details
| Test | Status | Duration |
|---|---|---|
| Request Payload.Request With Person Properties Device Id | ✅ | 2807ms |
| Request Payload.Flags Request Uses V2 Query Param | ✅ | 2699ms |
| Request Payload.Flags Request Hits Flags Path Not Decide | ✅ | 2825ms |
| Request Payload.Flags Request Omits Authorization Header | ✅ | 2709ms |
| Request Payload.Token In Flags Body Matches Init | ✅ | 2674ms |
| Request Payload.Groups Round Trip | ✅ | 2825ms |
| Request Payload.Groups Default To Empty Object | ✅ | 2813ms |
| Request Payload.Person Properties Distinct Id Auto Populated When Caller Omits It | ✅ | 2677ms |
| Request Payload.Disable Geoip False Propagates As Geoip Disable False | ✅ | 2700ms |
| Request Payload.Disable Geoip Omitted Defaults To False | ✅ | 2824ms |
| Request Payload.Flag Keys To Evaluate Contains Only Requested Key | ✅ | 2819ms |
| Request Lifecycle.No Flags Request On Init Alone | ✅ | 67ms |
| Request Lifecycle.No Flags Request On Normal Capture | ✅ | 2803ms |
| Request Lifecycle.Two Flag Calls Produce Two Remote Requests | ✅ | 5417ms |
| Request Lifecycle.Mock Response Value Is Returned To Caller | ✅ | 2814ms |
| Side Effect Events.Get Feature Flag Captures Feature Flag Called Event | ✅ | 3045ms |
turnipdabeets
force-pushed
the
feat/spm-package-traits
branch
from
October 7, 2026 02:05
6823d45 to
8641221
Compare
turnipdabeets
force-pushed
the
feat/spm-surveys-trait
branch
from
October 7, 2026 02:14
acf063e to
5a7b47d
Compare
5 tasks done
SwiftPM apps that don't use surveys can now leave out the survey engine and UI by disabling the `Surveys` trait. It is on by default, so existing setups are unchanged; CocoaPods and xcodeproj builds always include surveys. The public survey API (`config.surveys`, `surveysConfig`, the delegate protocol and display models) stays compiled. With the trait off, the survey integration is a stub that is skipped with `disabledByPackageTrait`. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A custom surveysDelegate is never called when the trait is off, so say so where apps set it. Also drop the unused clearInstalls() from the stub. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
turnipdabeets
force-pushed
the
feat/spm-surveys-trait
branch
from
October 7, 2026 02:26
5a7b47d to
098b5f4
Compare
3 of 23 tasks
dustinbyrne
approved these changes
Oct 7, 2026
dustinbyrne
left a comment
Contributor
There was a problem hiding this comment.
The Surveys trait preserves existing public declarations and default behavior, and the disabled integration follows the SDK’s existing skip semantics. No issues found.
AI-assisted review.
This was referenced Oct 7, 2026
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…at/spm-surveys-trait-local
turnipdabeets
added a commit
that referenced
this pull request
Oct 9, 2026
* feat: add SessionReplayWebP and CrashReporting package traits SwiftPM apps can now leave out the vendored libwebp and PLCrashReporter by disabling the traits. Both are on by default, so existing setups are unchanged; CocoaPods and xcodeproj builds always include both. Also moves the ignoredExceptionTypes matcher out of the crash-reporting gate so it applies to manual captures everywhere, which fixes it on watchOS and visionOS. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test: gate WebP suites on the SessionReplayWebP trait The WebP test suites called `UIImage.webpData`, which SwiftPM compiles out on iOS when the trait is off, so the test target failed to build. Gate them like the encoder and add a separate encoding test that expects WebP or the JPEG fallback depending on the trait. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * ci: compile the iOS test target with package traits off `make test` runs on macOS, where the iOS-only trait gates compile out, so a test calling trait-gated SDK code on iOS went unnoticed. Build the iOS test target for the simulator with traits off and with each trait on its own. The concurrent flag reload suite's `.timeLimit` needs iOS 16, so limit it to macOS SwiftPM builds rather than every SwiftPM build. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat!: replace the SessionReplayWebP trait with SessionReplay SessionReplay now gates the whole replay engine along with libwebp, so an app that doesn't use replay can leave all of it out. The public replay API (config, masking modifiers, URLSession helpers, start/stop recording) stays compiled; with the trait off the replay integration is a stub that is skipped with `disabledByPackageTrait`. A finer split (replay without WebP) can come later in a minor release by keeping SessionReplay as an umbrella trait that enables the new ones. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test: gate the remaining replay tests on the SessionReplay trait The session replay suite and the two SDK tests that assert replay debug properties need the replay engine, so skip them in SPM builds with the SessionReplay trait off. Also list buildTestsSpmTraitsIOS for trait-gated changes in the contributor guide. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test: limit the Swift 6 callback suite's timeLimit to macOS `.timeLimit` needs iOS 16, above the iOS 15 minimum, so the iOS SwiftPM test build failed after merging the 4.0 base. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat: add Surveys package trait (#918) * feat: add Surveys package trait SwiftPM apps that don't use surveys can now leave out the survey engine and UI by disabling the `Surveys` trait. It is on by default, so existing setups are unchanged; CocoaPods and xcodeproj builds always include surveys. The public survey API (`config.surveys`, `surveysConfig`, the delegate protocol and display models) stays compiled. With the trait off, the survey integration is a stub that is skipped with `disabledByPackageTrait`. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * docs: note the Surveys trait on surveysConfig and surveysDelegate A custom surveysDelegate is never called when the trait is off, so say so where apps set it. Also drop the unused clearInstalls() from the stub. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
turnipdabeets
added a commit
that referenced
this pull request
Oct 9, 2026
* feat: add SessionReplayWebP and CrashReporting package traits SwiftPM apps can now leave out the vendored libwebp and PLCrashReporter by disabling the traits. Both are on by default, so existing setups are unchanged; CocoaPods and xcodeproj builds always include both. Also moves the ignoredExceptionTypes matcher out of the crash-reporting gate so it applies to manual captures everywhere, which fixes it on watchOS and visionOS. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test: gate WebP suites on the SessionReplayWebP trait The WebP test suites called `UIImage.webpData`, which SwiftPM compiles out on iOS when the trait is off, so the test target failed to build. Gate them like the encoder and add a separate encoding test that expects WebP or the JPEG fallback depending on the trait. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * ci: compile the iOS test target with package traits off `make test` runs on macOS, where the iOS-only trait gates compile out, so a test calling trait-gated SDK code on iOS went unnoticed. Build the iOS test target for the simulator with traits off and with each trait on its own. The concurrent flag reload suite's `.timeLimit` needs iOS 16, so limit it to macOS SwiftPM builds rather than every SwiftPM build. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat!: replace the SessionReplayWebP trait with SessionReplay SessionReplay now gates the whole replay engine along with libwebp, so an app that doesn't use replay can leave all of it out. The public replay API (config, masking modifiers, URLSession helpers, start/stop recording) stays compiled; with the trait off the replay integration is a stub that is skipped with `disabledByPackageTrait`. A finer split (replay without WebP) can come later in a minor release by keeping SessionReplay as an umbrella trait that enables the new ones. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test: gate the remaining replay tests on the SessionReplay trait The session replay suite and the two SDK tests that assert replay debug properties need the replay engine, so skip them in SPM builds with the SessionReplay trait off. Also list buildTestsSpmTraitsIOS for trait-gated changes in the contributor guide. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test: limit the Swift 6 callback suite's timeLimit to macOS `.timeLimit` needs iOS 16, above the iOS 15 minimum, so the iOS SwiftPM test build failed after merging the 4.0 base. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat: add Surveys package trait (#918) * feat: add Surveys package trait SwiftPM apps that don't use surveys can now leave out the survey engine and UI by disabling the `Surveys` trait. It is on by default, so existing setups are unchanged; CocoaPods and xcodeproj builds always include surveys. The public survey API (`config.surveys`, `surveysConfig`, the delegate protocol and display models) stays compiled. With the trait off, the survey integration is a stub that is skipped with `disabledByPackageTrait`. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * docs: note the Surveys trait on surveysConfig and surveysDelegate A custom surveysDelegate is never called when the trait is off, so say so where apps set it. Also drop the unused clearInstalls() from the stub. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
turnipdabeets
added a commit
that referenced
this pull request
Oct 20, 2026
* feat: add SessionReplayWebP and CrashReporting package traits SwiftPM apps can now leave out the vendored libwebp and PLCrashReporter by disabling the traits. Both are on by default, so existing setups are unchanged; CocoaPods and xcodeproj builds always include both. Also moves the ignoredExceptionTypes matcher out of the crash-reporting gate so it applies to manual captures everywhere, which fixes it on watchOS and visionOS. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test: gate WebP suites on the SessionReplayWebP trait The WebP test suites called `UIImage.webpData`, which SwiftPM compiles out on iOS when the trait is off, so the test target failed to build. Gate them like the encoder and add a separate encoding test that expects WebP or the JPEG fallback depending on the trait. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * ci: compile the iOS test target with package traits off `make test` runs on macOS, where the iOS-only trait gates compile out, so a test calling trait-gated SDK code on iOS went unnoticed. Build the iOS test target for the simulator with traits off and with each trait on its own. The concurrent flag reload suite's `.timeLimit` needs iOS 16, so limit it to macOS SwiftPM builds rather than every SwiftPM build. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat!: replace the SessionReplayWebP trait with SessionReplay SessionReplay now gates the whole replay engine along with libwebp, so an app that doesn't use replay can leave all of it out. The public replay API (config, masking modifiers, URLSession helpers, start/stop recording) stays compiled; with the trait off the replay integration is a stub that is skipped with `disabledByPackageTrait`. A finer split (replay without WebP) can come later in a minor release by keeping SessionReplay as an umbrella trait that enables the new ones. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test: gate the remaining replay tests on the SessionReplay trait The session replay suite and the two SDK tests that assert replay debug properties need the replay engine, so skip them in SPM builds with the SessionReplay trait off. Also list buildTestsSpmTraitsIOS for trait-gated changes in the contributor guide. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test: limit the Swift 6 callback suite's timeLimit to macOS `.timeLimit` needs iOS 16, above the iOS 15 minimum, so the iOS SwiftPM test build failed after merging the 4.0 base. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * feat: add Surveys package trait (#918) * feat: add Surveys package trait SwiftPM apps that don't use surveys can now leave out the survey engine and UI by disabling the `Surveys` trait. It is on by default, so existing setups are unchanged; CocoaPods and xcodeproj builds always include surveys. The public survey API (`config.surveys`, `surveysConfig`, the delegate protocol and display models) stays compiled. With the trait off, the survey integration is a stub that is skipped with `disabledByPackageTrait`. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * docs: note the Surveys trait on surveysConfig and surveysDelegate A custom surveysDelegate is never called when the trait is off, so say so where apps set it. Also drop the unused clearInstalls() from the stub. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
💡 Motivation and Context
Surveys are the largest feature in the SDK, and every iOS app that adds PostHog through Swift Package Manager compiles and links the survey engine and its SwiftUI UI, even when it never turns surveys on. This adds a
Surveyspackage trait, on by default, so an app can leave surveys out:Surveysconfig.surveyshas no effectOn an iOS arm64 Release build of a small app that links the SDK dynamically, the stripped binary (
strip -x) is 2.87 MB with every trait on and 2.23 MB with onlySurveysoff, about 636 KB less. With every trait off it is 1.35 MB.This builds on the trait setup from #916: the same
#if !SWIFT_PACKAGE || <Trait>gates, so CocoaPods and xcodeproj builds always include surveys, and the same.skipped(.disabledByPackageTrait)result for an integration whose trait is off.What changes:
PostHog/Surveysare gated on the trait. The public API stays compiled with the trait off:config.surveys,config.surveysConfig,PostHogSurveysDelegateand the display models. Code that sets these keeps compiling when an app turns the trait off.PostHogSurveyIntegrationis a stub that is skipped withdisabledByPackageTrait(logged whenconfig.debugis on), andPostHogSurveysDefaultDelegaterenders nothing. The docs onconfig.surveys,config.surveysConfigandsurveysDelegatesay they need the trait.PostHogSurveysTest.swift,PostHogSurveyEventsTest.swift,PostHogSurveySharedStorageTest.swiftandStoredSurveyResponseTest.swiftis re-indentation from the new file-level#if, which--ifdef indentrequires. Hide whitespace to review them; each one changes by two lines.make buildTestsSpmTraitsIOSnow also compiles the iOS test target with onlySurveyson.One trait for all of surveys, rather than a separate trait for the built-in UI only, keeps it simple. The trade-off is that an app rendering surveys through its own
surveysDelegatehas to keep the trait on.Things to know:
Part of #557. Stacked on #916.
💚 How did you test it?
make lintpasses, with the same warnings as the base branch.make apiCheckpasses, and the public API snapshot is unchanged.make buildTestsSpmTraitsIOScompiles the iOS test target with traits off, and withCrashReporting,SessionReplayorSurveyson its own. Its first run caught three iOS-only test files that still referenced gated survey code.swift build --target PostHogpasses on macOS with traits off, and on iOS with traits off, defaults, andSurveysonly. A clean iOS release build with traits off compiles no survey UI symbols.make test filter=Surveypasses: 168 tests.make testNoTraits filter=IntegrationInstallationpasses: 8 tests. On macOS with traits off the survey tests are compiled out.Surveysoff (--traits SessionReplay,CrashReporting), the library and tests build for macOS and the iOS simulator.traits: ["SessionReplay", "CrashReporting"]builds for iOS and links no survey engine or UI symbols (SurveysWindow,SurveySheet,SurveyDisplayController, matching). The public survey API and models are still linked.traits: []launches, logsIntegration PostHogSurveyIntegration skipped: Disabled by a package trait, and still sends captured events. Its binary has no survey UI symbols.make testandmake testNoTraitssuites were not run.📝 Checklist
If releasing new changes
pnpm changesetto generate a changeset file🤖 Agent context
Autonomy: Human-driven (agent-assisted)
surveysspec says disabled surveys no-op. It led to the doc notes onsurveysConfig/surveysDelegateand to removing an unused stub method. Deferred: survey model decoding tests are also compiled out with the trait off. They still run with the default traits.🤖 Generated with Claude Code