Skip to content

fix: omit null object properties when serializing events - #810

Draft
marandaneto wants to merge 2 commits into
mainfrom
fix/null-property-serialization-sqq3ojkm
Draft

fix: omit null object properties when serializing events#810
marandaneto wants to merge 2 commits into
mainfrom
fix/null-property-serialization-sqq3ojkm

Conversation

@marandaneto

@marandaneto marandaneto commented Sep 9, 2026

Copy link
Copy Markdown
Member

💡 Motivation and Context

Custom event properties containing NSNull() currently retain null object members in serialized events. This implements the recursive serialization rule in sdk-specs PR #60 at the shared native event serializer, without requiring callers or wrappers to filter inputs.

PostHogEvent.toJSON() now omits null-valued custom object members, including nested objects inside arrays. Null array entries keep their positions. Empty objects, other scalar values, caller-owned data and existing public input types remain unchanged. The same serializer serves event queues and final batch/snapshot delivery, including restored events. Legacy queue rewrites also clean custom properties and the old envelope-level $set field.

Typed replay, exception and feature-flag metadata retain their existing event-specific null semantics. Generic storage and OTLP serialization are unchanged. This change belongs in the native SDK rather than an additional Flutter serialization pass.

💚 How did you test it?

  • Eight Swift Testing tests with 15 parameterized invocations passed after merging current main. They exercise actual event serialization, intercepted gzip batch/snapshot bytes, physical queue persistence/restoration, legacy migration, Foundation bridging, and the before-send chain.
  • These local tests compile a dependency-free macOS source subset with configuration, logging, constants and UUID support stand-ins. They do not establish full Apple SDK or device conformance. The retained baseline run failed with 21 assertion issues before this fix.
  • Focused SwiftFormat and SwiftLint checks passed. Xcode project plist validation and git diff --check passed.
  • An isolated exact-commit autoreview passed with no actionable findings.

Remaining validation gates: Full SDK/example builds, the full test suite, Objective-C callers, public capture-to-hook integration, replay-buffer integration and the Apple simulator/platform matrix were not run locally. Existing broad tests use shared application-support storage, so they were not rerun in this environment. Public signatures were source-checked, not validated through the full compiled API check. Hosted CI at commit c391c7e941633a1f920d5205a4d23c30ee424e9e had 38 passing checks, one neutral check and six pending checks at 2026-09-09 08:59 UTC. SDK platform builds, example builds, downgrade compatibility, lint and the public API snapshot passed. The main test job, iOS simulator tests, replay masking snapshots, CocoaPods lint, Swift CodeQL and compliance tests were still running after a bounded watch. CI is not yet fully green. This is a draft for human review, not merge or release approval.

📝 Checklist

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

If releasing new changes

  • Ran pnpm changeset to generate a changeset file.

A patch changeset for posthog-ios was added by hand. No package manager command, release or manual version bump was performed.

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

Pi assisted with the implementation and publication using Git, GitHub CLI, make, SwiftFormat, SwiftLint and the isolated autoreview helper. The human-directed scope keeps null cleanup in the native event serializer and preserves typed metadata semantics. No public session transcript is available. Human review and the remaining platform validation are required before merging.

@marandaneto marandaneto self-assigned this Sep 9, 2026
@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

posthog-ios Compliance Report

Date: 2026-09-09 09:05:55 UTC
Duration: 223304ms

✅ All Tests Passed!

45/45 tests passed


Capture Tests

29/29 tests passed

View Details
Test Status Duration
Format Validation.Event Has Required Fields 2995ms
Format Validation.Event Has Uuid 2698ms
Format Validation.Event Has Lib Properties 2658ms
Format Validation.Distinct Id Is String 2676ms
Format Validation.Token Is Present 2745ms
Format Validation.Custom Properties Preserved 2749ms
Format Validation.Event Has Timestamp 2774ms
Retry Behavior.Retries On 503 11315ms
Retry Behavior.Does Not Retry On 400 2245ms
Retry Behavior.Does Not Retry On 401 2361ms
Retry Behavior.Respects Retry After Header 7719ms
Retry Behavior.Implements Backoff 21308ms
Retry Behavior.Retries On 500 9248ms
Retry Behavior.Retries On 502 8787ms
Retry Behavior.Retries On 504 9272ms
Retry Behavior.Max Retries Respected 21761ms
Deduplication.Generates Unique Uuids 2835ms
Deduplication.Preserves Uuid On Retry 9314ms
Deduplication.Preserves Uuid And Timestamp On Retry 16808ms
Deduplication.Preserves Uuid And Timestamp On Batch Retry 7799ms
Deduplication.No Duplicate Events In Batch 2829ms
Deduplication.Different Events Have Different Uuids 2850ms
Compression.Sends Gzip When Enabled 2781ms
Batch Format.Uses Proper Batch Structure 2750ms
Batch Format.Flush With No Events Sends Nothing 355ms
Batch Format.Multiple Events Batched Together 2863ms
Error Handling.Does Not Retry On 403 4788ms
Error Handling.Does Not Retry On 413 2355ms
Error Handling.Retries On 408 8791ms

Feature_Flags Tests

16/16 tests passed

View Details
Test Status Duration
Request Payload.Request With Person Properties Device Id 2696ms
Request Payload.Flags Request Uses V2 Query Param 2696ms
Request Payload.Flags Request Hits Flags Path Not Decide 2699ms
Request Payload.Flags Request Omits Authorization Header 2774ms
Request Payload.Token In Flags Body Matches Init 2801ms
Request Payload.Groups Round Trip 2801ms
Request Payload.Groups Default To Empty Object 2765ms
Request Payload.Person Properties Distinct Id Auto Populated When Caller Omits It 2780ms
Request Payload.Disable Geoip False Propagates As Geoip Disable False 2633ms
Request Payload.Disable Geoip Omitted Defaults To False 2717ms
Request Payload.Flag Keys To Evaluate Contains Only Requested Key 2768ms
Request Lifecycle.No Flags Request On Init Alone 58ms
Request Lifecycle.No Flags Request On Normal Capture 410ms
Request Lifecycle.Two Flag Calls Produce Two Remote Requests 5522ms
Request Lifecycle.Mock Response Value Is Returned To Caller 2855ms
Side Effect Events.Get Feature Flag Captures Feature Flag Called Event 2955ms

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.

1 participant