Repository navigation
chore!: replace vendored Reachability with NWPathMonitor - #909
Conversation
114c733 to
1449415
Compare
posthog-ios Compliance ReportDate: 2026-10-07 20:51:22 UTC
|
| Test | Status | Duration |
|---|---|---|
| Format Validation.Event Has Required Fields | ✅ | 3018ms |
| Format Validation.Event Has Uuid | ✅ | 3282ms |
| Format Validation.Event Has Lib Properties | ✅ | 2784ms |
| Format Validation.Distinct Id Is String | ✅ | 3078ms |
| Format Validation.Token Is Present | ✅ | 659ms |
| Format Validation.Custom Properties Preserved | ✅ | 449ms |
| Format Validation.Event Has Timestamp | ✅ | 2829ms |
| Retry Behavior.Retries On 503 | ✅ | 11899ms |
| Retry Behavior.Does Not Retry On 400 | ✅ | 4977ms |
| Retry Behavior.Does Not Retry On 401 | ✅ | 2758ms |
| Retry Behavior.Respects Retry After Header | ✅ | 7887ms |
| Retry Behavior.Implements Backoff | ✅ | 21322ms |
| Retry Behavior.Retries On 500 | ✅ | 5640ms |
| Retry Behavior.Retries On 502 | ✅ | 9357ms |
| Retry Behavior.Retries On 504 | ✅ | 9377ms |
| Retry Behavior.Max Retries Respected | ❌ | 22055ms |
| Deduplication.Generates Unique Uuids | ✅ | 3261ms |
| Deduplication.Preserves Uuid On Retry | ✅ | 9220ms |
| Deduplication.Preserves Uuid And Timestamp On Retry | ✅ | 16317ms |
| Deduplication.Preserves Uuid And Timestamp On Batch Retry | ✅ | 7849ms |
| Deduplication.No Duplicate Events In Batch | ✅ | 2904ms |
| Deduplication.Different Events Have Different Uuids | ✅ | 3097ms |
| Compression.Sends Gzip When Enabled | ✅ | 2902ms |
| Batch Format.Uses Proper Batch Structure | ✅ | 479ms |
| Batch Format.Flush With No Events Sends Nothing | ✅ | 291ms |
| Batch Format.Multiple Events Batched Together | ✅ | 3100ms |
| Error Handling.Does Not Retry On 403 | ✅ | 4911ms |
| Error Handling.Does Not Retry On 413 | ✅ | 4883ms |
| Error Handling.Retries On 408 | ✅ | 9212ms |
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 | ✅ | 3060ms |
| Request Payload.Flags Request Uses V2 Query Param | ✅ | 3028ms |
| Request Payload.Flags Request Hits Flags Path Not Decide | ✅ | 2837ms |
| Request Payload.Flags Request Omits Authorization Header | ✅ | 3092ms |
| Request Payload.Token In Flags Body Matches Init | ✅ | 2974ms |
| Request Payload.Groups Round Trip | ✅ | 2938ms |
| Request Payload.Groups Default To Empty Object | ✅ | 3088ms |
| Request Payload.Person Properties Distinct Id Auto Populated When Caller Omits It | ✅ | 2960ms |
| Request Payload.Disable Geoip False Propagates As Geoip Disable False | ✅ | 3013ms |
| Request Payload.Disable Geoip Omitted Defaults To False | ✅ | 2984ms |
| Request Payload.Flag Keys To Evaluate Contains Only Requested Key | ✅ | 3339ms |
| Request Lifecycle.No Flags Request On Init Alone | ✅ | 168ms |
| Request Lifecycle.No Flags Request On Normal Capture | ✅ | 3005ms |
| Request Lifecycle.Two Flag Calls Produce Two Remote Requests | ✅ | 5610ms |
| Request Lifecycle.Mock Response Value Is Returned To Caller | ✅ | 2963ms |
| Side Effect Events.Get Feature Flag Captures Feature Flag Called Event | ✅ | 3141ms |
dustinbyrne
left a comment
There was a problem hiding this comment.
AI-assisted static review at 144941560cb468c1eb9992a0092b28db8a2245ab. Regression scenarios were not executed.
e1cc8cf to
5b6c50e
Compare
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Notify on every path update and report the current connection when the notifier starts, as the SCNetworkReachability version did. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Wait for the first path at most 100 ms after init in total, not 100 ms per read. Add tests for the start/update/stop notification rules. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A start notification queued before a newer path could arrive last and pause the queues while online. Also narrow the changeset to what is actually preserved and list the edge-state changes. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
ec83128 to
8fd604d
Compare
dustinbyrne
left a comment
There was a problem hiding this comment.
Both earlier review comments are addressed. No demonstrated implementation regression found at 8fd604deca89cb595f659badf9a6fceedc06b28b; the inline test-coverage note is nonblocking, and current tests pass.
Reviewed statically with existing CI, without executing new scenarios. Compliance still reports 43/45 despite its green check: the retry-count failure also appears in base-associated CI, and the additional zero-request case has unresolved harness/PR causality.
AI-assisted review.
Seed the context's reachability instead of relying on a live NWPathMonitor delivering its first path within 100 ms, and assert that network properties are omitted until a path arrives. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…odes The logs and replay queue reachability tests used a live NWPathMonitor, so a real path update could race the simulated offline event. Drive them through update(_:) instead, and add tests for .wifi waiting for a Wi-Fi path and .any sending before the first path. Mark the changeset major like the rest of v4. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* chore: replace vendored Reachability with NWPathMonitor Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: match the old reachability notifications Notify on every path update and report the current connection when the notifier starts, as the SCNetworkReachability version did. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: cap the first-path wait and test the reachability notifier Wait for the first path at most 100 ms after init in total, not 100 ms per read. Add tests for the start/update/stop notification rules. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: report reachability from the connection at delivery A start notification queued before a newer path could arrive last and pause the queues while online. Also narrow the changeset to what is actually preserved and list the edge-state changes. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test: cover unknown and known network state in the context test Seed the context's reachability instead of relying on a live NWPathMonitor delivering its first path within 100 ms, and assert that network properties are omitted until a path arrives. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test: drive queue reachability tests with update(_:) and cover data modes The logs and replay queue reachability tests used a live NWPathMonitor, so a real path update could race the simulated offline event. Drive them through update(_:) instead, and add tests for .wifi waiting for a Wi-Fi path and .any sending before the first path. Mark the changeset major like the rest of v4. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* chore: replace vendored Reachability with NWPathMonitor Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: match the old reachability notifications Notify on every path update and report the current connection when the notifier starts, as the SCNetworkReachability version did. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: cap the first-path wait and test the reachability notifier Wait for the first path at most 100 ms after init in total, not 100 ms per read. Add tests for the start/update/stop notification rules. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: report reachability from the connection at delivery A start notification queued before a newer path could arrive last and pause the queues while online. Also narrow the changeset to what is actually preserved and list the edge-state changes. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test: cover unknown and known network state in the context test Seed the context's reachability instead of relying on a live NWPathMonitor delivering its first path within 100 ms, and assert that network properties are omitted until a path arrives. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test: drive queue reachability tests with update(_:) and cover data modes The logs and replay queue reachability tests used a live NWPathMonitor, so a real path update could race the simulated offline event. Drive them through update(_:) instead, and add tests for .wifi waiting for a Wi-Fi path and .any sending before the first path. Mark the changeset major like the rest of v4. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* chore: replace vendored Reachability with NWPathMonitor Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: match the old reachability notifications Notify on every path update and report the current connection when the notifier starts, as the SCNetworkReachability version did. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: cap the first-path wait and test the reachability notifier Wait for the first path at most 100 ms after init in total, not 100 ms per read. Add tests for the start/update/stop notification rules. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: report reachability from the connection at delivery A start notification queued before a newer path could arrive last and pause the queues while online. Also narrow the changeset to what is actually preserved and list the edge-state changes. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test: cover unknown and known network state in the context test Seed the context's reachability instead of relying on a live NWPathMonitor delivering its first path within 100 ms, and assert that network properties are omitted until a path arrives. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test: drive queue reachability tests with update(_:) and cover data modes The logs and replay queue reachability tests used a live NWPathMonitor, so a real path update could race the simulated offline event. Drive them through update(_:) instead, and add tests for .wifi waiting for a Wi-Fi path and .any sending before the first path. Mark the changeset major like the rest of v4. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* chore: replace vendored Reachability with NWPathMonitor Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: match the old reachability notifications Notify on every path update and report the current connection when the notifier starts, as the SCNetworkReachability version did. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: cap the first-path wait and test the reachability notifier Wait for the first path at most 100 ms after init in total, not 100 ms per read. Add tests for the start/update/stop notification rules. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * fix: report reachability from the connection at delivery A start notification queued before a newer path could arrive last and pause the queues while online. Also narrow the changeset to what is actually preserved and list the edge-state changes. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test: cover unknown and known network state in the context test Seed the context's reachability instead of relying on a live NWPathMonitor delivering its first path within 100 ms, and assert that network properties are omitted until a path arrives. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * test: drive queue reachability tests with update(_:) and cover data modes The logs and replay queue reachability tests used a live NWPathMonitor, so a real path update could race the simulated offline event. Drive them through update(_:) instead, and add tests for .wifi waiting for a Wi-Fi path and .any sending before the first path. Mark the changeset major like the rest of v4. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
💡 Motivation and Context
Part of the 4.0 stack, on top of #873. It doesn't depend on the macOS/tvOS/watchOS bump in #906. This replaces the vendored Reachability (Ashley Mills, about 400 lines built on
SCNetworkReachability) with a small wrapper aroundNWPathMonitor. Apple deprecated theSCNetworkReachabilityAPIs in the iOS 17.4 / macOS 14.4 SDKs.NWPathMonitoris available on every platform we support.The wrapper keeps the same internal interface (
connection,onReachable/onUnreachable,startNotifier/stopNotifier), so the queues andPostHogContextbarely change.Behavior
dataMode = .anypath.status == .unsatisfieddataMode = .wifi!path.usesInterfaceType(.cellular).requiresConnection).requiresConnection).unsatisfied, reason.cellularDenied), queues pause$network_wifi/$network_cellularNWPathMonitordelivers its first path asynchronously. The old API read flags synchronously, so reads ofconnectionwait for that first path, up to 100 ms after init in total; locally it arrives in about 1 ms. If no path has arrived,connectionisnil. In that state.anydoesn't pause,.wifistays paused until a path arrives, and events leave out$network_*rather than reportingfalse.startNotifier()no longer throws.NWPathMonitorhas no failure path, so the queue's "notifier failed to start" fallback is gone.NWPathMonitoralso reports changes that keep the same connection (e.g. DNS), which now trigger a flush that does nothing. The firststartNotifier()reports the current connection, as the old initial check did, so every queue flushes leftover events at launch. Each notification reads the connection when it's delivered, as the old code did.NWPathdoesn't separate a connect-on-demand path from one that needs user action, so both count as reachable; when a request then fails, the normal retry backoff applies. With cellular data turned off for the app, the queues now pause instead of sending requests that fail.Public API: none. #874 already made
ReachabilityErrorinternal. The only public symbol removed is theReachabilityChangedNotificationglobal, which was already@available(*, unavailable)and isn't in the API snapshot.Objective-C: none of this was ever exposed to Objective-C.
Networkautolinks likeSystemConfigurationdid, so there are no podspec or SPM linker changes.💚 How did you test it?
Reachability notifiertests cover the start/update/stop rules: the first start reports the current connection once, paths after start are reported, paths after stop aren't, and with no path the wait is bounded. A notification queued before a newer path reports the connection at delivery, so a stale "unavailable" can't pause the queues while online.make test(macOS): all 169 XCTest tests pass. Swift Testing ran 964 tests; 13 issues inPostHogStorageapp-group tests fail the same way on the base branch (macOS blocks access to the app-group container on my machine) and are unrelated.SCNetworkReachabilitycode and the new code in one process on macOS (Wi-Fi). Both reportedWiFiwith no mismatches, and both fired one initialonReachable. I didn't test Wi-Fi/cellular switching or going offline.xcodebuild buildsucceeds for iOS, tvOS, watchOS, visionOS and Mac Catalyst.make buildSdkwasn't run as-is becausexcprettyisn't installed locally.📝 Checklist
If releasing new changes
pnpm changesetto generate a changeset file🤖 Agent context
Autonomy: Human-driven (agent-assisted)
.wifito "not cellular" to keep today's behavior. I rejected!path.isExpensive, which would also pause on Wi-Fi tethered to an iPhone hotspot. That's a real behavior change and would need its own changelog entry.🤖 Generated with Claude Code