From 97317a48f099044d177fe96d4f606e17d7f97675 Mon Sep 17 00:00:00 2001 From: Anna Garcia Date: Wed, 30 Sep 2026 11:56:02 -0400 Subject: [PATCH 1/3] chore!: remove the v2 legacy queue migration for v4 Co-Authored-By: Claude Opus 5.5 (1M context) --- .../v4-remove-legacy-queue-migration.md | 7 + PostHog.xcodeproj/project.pbxproj | 8 -- PostHog/PostHogFileBackedQueue.swift | 2 +- PostHog/PostHogLegacyQueue.swift | 44 ------ .../PostHogFileBackedQueueAlignmentTest.swift | 16 +++ PostHogTests/PostHogLegacyQueueTest.swift | 133 ------------------ PostHogTests/PostHogStorageTest.swift | 22 +++ 7 files changed, 46 insertions(+), 186 deletions(-) create mode 100644 .changeset/v4-remove-legacy-queue-migration.md delete mode 100644 PostHog/PostHogLegacyQueue.swift delete mode 100644 PostHogTests/PostHogLegacyQueueTest.swift diff --git a/.changeset/v4-remove-legacy-queue-migration.md b/.changeset/v4-remove-legacy-queue-migration.md new file mode 100644 index 0000000000..be49671f2a --- /dev/null +++ b/.changeset/v4-remove-legacy-queue-migration.md @@ -0,0 +1,7 @@ +--- +'posthog-ios': major +--- + +**Breaking:** remove the migration of the event queue written by SDK 2.x (`posthog.queue.plist`). + +Events still queued on disk by a 2.x SDK are deleted instead of sent when the app updates straight from 2.x to 4.0. Distinct ID, anonymous ID, groups and cached feature flags are still carried over. Apps on any 3.x release are not affected. diff --git a/PostHog.xcodeproj/project.pbxproj b/PostHog.xcodeproj/project.pbxproj index 105f568175..9a51994d44 100644 --- a/PostHog.xcodeproj/project.pbxproj +++ b/PostHog.xcodeproj/project.pbxproj @@ -95,8 +95,6 @@ 6A11C0DE2026092800000011 /* PostHogSurveySheetDetentsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A11C0DE2026092800000012 /* PostHogSurveySheetDetentsTest.swift */; }; 6A11C0DE2026092800000003 /* PostHogHingeStatusTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A11C0DE2026092800000004 /* PostHogHingeStatusTest.swift */; }; 690FF0BF2AEFA97F00A0B06B /* FileUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 690FF0BE2AEFA97F00A0B06B /* FileUtils.swift */; }; - 690FF0C52AEFAE8200A0B06B /* PostHogLegacyQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 690FF0C42AEFAE8200A0B06B /* PostHogLegacyQueue.swift */; }; - 690FF0DF2AEFBC5700A0B06B /* PostHogLegacyQueueTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 690FF0DE2AEFBC5700A0B06B /* PostHogLegacyQueueTest.swift */; }; 690FF0E12AEFC59100A0B06B /* PostHogFileBackedQueueTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 690FF0E02AEFC59100A0B06B /* PostHogFileBackedQueueTest.swift */; }; 690FF0E32AEFD12900A0B06B /* PostHogConfigTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 690FF0E22AEFD12900A0B06B /* PostHogConfigTest.swift */; }; 6A0C1F2E3B4D5A6B7C8D9E02 /* PostHogOptOutPersistenceTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A0C1F2E3B4D5A6B7C8D9E01 /* PostHogOptOutPersistenceTest.swift */; }; @@ -958,8 +956,6 @@ 6A11C0DE2026092800000012 /* PostHogSurveySheetDetentsTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostHogSurveySheetDetentsTest.swift; sourceTree = ""; }; 6A11C0DE2026092800000004 /* PostHogHingeStatusTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostHogHingeStatusTest.swift; sourceTree = ""; }; 690FF0BE2AEFA97F00A0B06B /* FileUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileUtils.swift; sourceTree = ""; }; - 690FF0C42AEFAE8200A0B06B /* PostHogLegacyQueue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostHogLegacyQueue.swift; sourceTree = ""; }; - 690FF0DE2AEFBC5700A0B06B /* PostHogLegacyQueueTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostHogLegacyQueueTest.swift; sourceTree = ""; }; 690FF0E02AEFC59100A0B06B /* PostHogFileBackedQueueTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostHogFileBackedQueueTest.swift; sourceTree = ""; }; 690FF0E22AEFD12900A0B06B /* PostHogConfigTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostHogConfigTest.swift; sourceTree = ""; }; 6A0C1F2E3B4D5A6B7C8D9E01 /* PostHogOptOutPersistenceTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostHogOptOutPersistenceTest.swift; sourceTree = ""; }; @@ -1808,7 +1804,6 @@ 6926DA8D2ADD2876005760D2 /* PostHogContext.swift */, 6A11C0DE2026092800000002 /* PostHogHingeStatusObserver.swift */, 69779BEB2AE68E6900D7A48E /* UIViewController.swift */, - 690FF0C42AEFAE8200A0B06B /* PostHogLegacyQueue.swift */, 69F518372BB2BA0100F52C14 /* PostHogSwizzler.swift */, 69ED1A5B2C7F15F300FE7A91 /* PostHogSessionManager.swift */, B5EB989F28A201A9F44026EE /* PostHogCompression.swift */, @@ -1879,7 +1874,6 @@ 6A11C0DE2026092300000002 /* PostHogContextScreenSizeTest.swift */, 6A11C0DE2026092800000012 /* PostHogSurveySheetDetentsTest.swift */, 6A11C0DE2026092800000004 /* PostHogHingeStatusTest.swift */, - 690FF0DE2AEFBC5700A0B06B /* PostHogLegacyQueueTest.swift */, DA4FFB142DA93C78006BAEEA /* PostHogSessionReplayTest.swift */, DA4FFB182DA93C78006BAEEA /* PostHogReplayScreenshotDedupTest.swift */, EA5700022DA93C78006BAEEA /* PostHogReplayCaptureTouchesTest.swift */, @@ -3623,7 +3617,6 @@ DA3BB5052EDF15320097A97A /* PostHogStackFrame.swift in Sources */, DACC08BB2D6E3C6D00115E46 /* PostHogIntegration.swift in Sources */, DAD5DD0C2CB6DEF30087387B /* PostHogMaskViewModifier.swift in Sources */, - 690FF0C52AEFAE8200A0B06B /* PostHogLegacyQueue.swift in Sources */, 3AE3FB332991388500AFFC18 /* PostHogQueue.swift in Sources */, DA1044882D0B1D2D00C4ACF3 /* View+PostHogLabel.swift in Sources */, DAA1B2F12F8C100000112233 /* View+PostHogNoRageClick.swift in Sources */, @@ -3809,7 +3802,6 @@ DADE7DFE2F7EA90D00C4650C /* PostHogDeepLinkIntegrationTests.swift in Sources */, DA52CAEF2F24319800305F3D /* PostHogSessionReplayPluginTest.swift in Sources */, DA52CAEF2F2431980030AA01 /* PostHogConsoleLogInterceptorTest.swift in Sources */, - 690FF0DF2AEFBC5700A0B06B /* PostHogLegacyQueueTest.swift in Sources */, DA6F24822D4A6CA100CA2777 /* PostHogApiTest.swift in Sources */, AE3C6A5B6DAB01EF554A0270 /* PostHogPushNotificationTest.swift in Sources */, D36F92424A13D7CE61B6E348 /* PostHogSwizzlerTest.swift in Sources */, diff --git a/PostHog/PostHogFileBackedQueue.swift b/PostHog/PostHogFileBackedQueue.swift index 5ea44b3afc..9206ca0a00 100644 --- a/PostHog/PostHogFileBackedQueue.swift +++ b/PostHog/PostHogFileBackedQueue.swift @@ -43,7 +43,7 @@ class PostHogFileBackedQueue { migrateOldQueueFolder(queue: queue, oldQueueFolder: oldQueue) } else { // old plist file - migrateOldQueue(queue: queue, oldQueue: oldQueue) + deleteSafely(oldQueue) } } } diff --git a/PostHog/PostHogLegacyQueue.swift b/PostHog/PostHogLegacyQueue.swift deleted file mode 100644 index 8b7285259c..0000000000 --- a/PostHog/PostHogLegacyQueue.swift +++ /dev/null @@ -1,44 +0,0 @@ -// -// PostHogLegacyQueue.swift -// PostHog -// -// Created by Manoel Aranda Neto on 30.10.23. -// - -import Foundation - -// Migrates the Old Queue (v2) to the new Queue (v3) -func migrateOldQueue(queue: URL, oldQueue: URL) { - if !FileManager.default.fileExists(atPath: oldQueue.path) { - return - } - - defer { - deleteSafely(oldQueue) - } - - do { - let data = try Data(contentsOf: oldQueue) - guard let array = try JSONSerialization.jsonObject(with: data) as? [Any] else { - hedgeLog("Failed to migrate queue: invalid data format") - return - } - - for item in array { - guard let event = item as? [String: Any] else { - continue - } - let timestamp = event["timestamp"] as? String ?? toISO8601String(Date()) - - let timestampDate = toISO8601Date(timestamp) ?? Date() - - let filename = "\(timestampDate.timeIntervalSince1970)" - - let contents = try JSONSerialization.data(withJSONObject: event) - - try contents.write(to: queue.appendingPathComponent(filename)) - } - } catch { - hedgeLog("Failed to migrate queue \(error)") - } -} diff --git a/PostHogTests/PostHogFileBackedQueueAlignmentTest.swift b/PostHogTests/PostHogFileBackedQueueAlignmentTest.swift index 8ad9e6726b..a6b2629576 100644 --- a/PostHogTests/PostHogFileBackedQueueAlignmentTest.swift +++ b/PostHogTests/PostHogFileBackedQueueAlignmentTest.swift @@ -31,6 +31,22 @@ struct PostHogFileBackedQueueAlignmentTest { data.map { String(data: $0, encoding: .utf8)! } } + @Test("deletes a v2 queue file without importing its events") + func deletesLegacyQueueFile() throws { + let dir = FileManager.default.temporaryDirectory + .appendingPathComponent("ph-queue-align-\(UUID().uuidString)") + let oldQueue = FileManager.default.temporaryDirectory + .appendingPathComponent("ph-queue-plist-\(UUID().uuidString)") + defer { try? FileManager.default.removeItem(at: dir) } + try Data(#"[{"event":"v2 event","distinct_id":"user","timestamp":"2023-10-25T14:14:04.407Z"}]"#.utf8) + .write(to: oldQueue) + + let queue = PostHogFileBackedQueue(queue: dir, oldQueues: [oldQueue]) + + #expect(!FileManager.default.fileExists(atPath: oldQueue.path)) + #expect(queue.depth == 0) + } + @Test("a failed write to a full queue preserves existing entries", .enabled(if: geteuid() != 0)) func failedWritePreservesFullQueue() throws { let (queue, dir) = makeQueue() diff --git a/PostHogTests/PostHogLegacyQueueTest.swift b/PostHogTests/PostHogLegacyQueueTest.swift deleted file mode 100644 index 9a91ce6a66..0000000000 --- a/PostHogTests/PostHogLegacyQueueTest.swift +++ /dev/null @@ -1,133 +0,0 @@ -// -// PostHogLegacyQueueTest.swift -// PostHogTests -// -// Created by Manoel Aranda Neto on 30.10.23. -// - -import Foundation -import Nimble -@testable import PostHog -import Quick - -class PostHogLegacyQueueTest: QuickSpec { - override func spec() { - it("migrate old queue to new queue") { - let baseUrl = applicationSupportDirectoryURL() - try FileManager.default.createDirectory(atPath: baseUrl.path, withIntermediateDirectories: true) - - let newURL = baseUrl.appendingPathComponent("queue") - try FileManager.default.createDirectory(atPath: newURL.path, withIntermediateDirectories: true) - - let oldURL = baseUrl.appendingPathComponent("oldQueue") - - let eventsArray = - """ - [ - { - "properties": { - "$network_cellular": false, - "$groups": { - "some-group": "id:4" - }, - "$app_build": 1, - "$os_name": "iOS", - "$feature/multivariant": "payload", - "$screen_width": 852, - "$app_version": "1.0", - "$device_type": "Mobile", - "$active_feature_flags__0": "multivariant", - "$feature/4535-funnel-bar-viz": true, - "$network_wifi": true, - "$timezone": "Europe/Vienna", - "$device_id": "48DA3429-495A-4903-B347-F096FC31C3AB", - "$active_feature_flags": [ - "$feature/multivariant", - "$feature/testJson", - "$feature/4535-funnel-bar-viz", - "$feature/disabledFlag" - ], - "$active_feature_flags__3": "disabledFlag", - "$device_name": "iPhone", - "$app_name": "", - "$app_namespace": "com.posthog.CocoapodsExample", - "$locale": "en-US", - "$feature/disabledFlag": true, - "$active_feature_flags__1": "testJson", - "$screen_height": 393, - "$device_model": "arm64", - "$device_manufacturer": "Apple", - "$feature/testJson": "theInteger", - "$lib_version": "2.1.0", - "$os_version": "17.0.1", - "$lib": "posthog-ios", - "$active_feature_flags__2": "4535-funnel-bar-viz" - }, - "timestamp": "2023-10-25T14:14:04.407Z", - "message_id": "5CE069F8-E967-4B47-9D89-207EF7519453", - "event": "Cocoapods Example Button", - "distinct_id": "Prateek" - } - ] - """ - - let eventsData = eventsArray.data(using: .utf8)! - try eventsData.write(to: oldURL) - - expect(FileManager.default.fileExists(atPath: oldURL.path)) == true - - migrateOldQueue(queue: newURL, oldQueue: oldURL) - - expect(FileManager.default.fileExists(atPath: oldURL.path)) == false - - let items = try FileManager.default.contentsOfDirectory(atPath: newURL.path) - - let eventURL = newURL.appendingPathComponent(items[0]) - expect(FileManager.default.fileExists(atPath: eventURL.path)) == true - - let eventData = try Data(contentsOf: eventURL) - let eventObject = try JSONSerialization.jsonObject(with: eventData, options: .allowFragments) as? [String: Any] - - expect(eventObject!["distinct_id"] as? String) == "Prateek" - expect(eventObject!["event"] as? String) == "Cocoapods Example Button" - expect(eventObject!["message_id"] as? String) == "5CE069F8-E967-4B47-9D89-207EF7519453" - expect(eventObject!["timestamp"] as? String) == "2023-10-25T14:14:04.407Z" - expect(eventObject!["properties"] as? [String: Any]) != nil - - deleteSafely(oldURL) - deleteSafely(newURL) - } - - it("ignore and delete corrupted file") { - let baseUrl = applicationSupportDirectoryURL() - try FileManager.default.createDirectory(atPath: baseUrl.path, withIntermediateDirectories: true) - - let newURL = baseUrl.appendingPathComponent("queue") - try FileManager.default.createDirectory(atPath: newURL.path, withIntermediateDirectories: true) - - let oldURL = baseUrl.appendingPathComponent("oldQueue") - - let eventsArray = - """ - [ - i am broken - ] - """ - - let eventsData = eventsArray.data(using: .utf8)! - try eventsData.write(to: oldURL) - - expect(FileManager.default.fileExists(atPath: oldURL.path)) == true - - migrateOldQueue(queue: newURL, oldQueue: oldURL) - - expect(FileManager.default.fileExists(atPath: oldURL.path)) == false - - let items = try FileManager.default.contentsOfDirectory(atPath: newURL.path) - expect(items.isEmpty) == true - - deleteSafely(oldURL) - deleteSafely(newURL) - } - } -} diff --git a/PostHogTests/PostHogStorageTest.swift b/PostHogTests/PostHogStorageTest.swift index 9bbed3cb3b..80cf1e943a 100644 --- a/PostHogTests/PostHogStorageTest.swift +++ b/PostHogTests/PostHogStorageTest.swift @@ -124,6 +124,28 @@ class PostHogStorageTest { sut.reset() } + @Test("deletes a v2 queue file left in the legacy base folder without importing it") + func deletesLegacyV2QueueFileFromBaseFolder() throws { + let fileManager = FileManager.default + let baseUrl = applicationSupportDirectoryURL() + try fileManager.createDirectory(at: baseUrl, withIntermediateDirectories: true) + let legacyPlist = baseUrl.appendingPathComponent(PostHogStorage.StorageKey.oldQueuePlist.rawValue) + try Data(#"[{"event":"v2 event","distinct_id":"user","timestamp":"2023-10-25T14:14:04.407Z"}]"#.utf8) + .write(to: legacyPlist) + + let storage = PostHogStorage(PostHogConfig(projectToken: "v2_queue_\(UUID().uuidString)")) + defer { try? fileManager.removeItem(at: storage.appFolderUrl) } + // Same keys as the `/batch` endpoint. + let queue = PostHogFileBackedQueue( + queue: storage.url(forKey: .queue), + oldQueues: [.oldQueueFolder, .oldQueuePlist].map { storage.url(forKey: $0) } + ) + + #expect(!fileManager.fileExists(atPath: legacyPlist.path)) + #expect(!fileManager.fileExists(atPath: storage.url(forKey: .oldQueuePlist).path)) + #expect(queue.depth == 0) + } + @Test("writes to disk in a project token folder under application support directory") func writesToDiskInAProjectTokenFolderUnderApplicationSupportDirectory() { let config = PostHogConfig(projectToken: "test_project_token") From f585973c754de1db9a4a621294d24ef56b71cfc2 Mon Sep 17 00:00:00 2001 From: Anna Garcia Date: Thu, 1 Oct 2026 14:46:43 -0400 Subject: [PATCH 2/3] chore!: stop reading the 2.x event format in queued events Drop the top-level $set and message_id fallbacks in PostHogEvent.fromJSON. They only served events copied unchanged from the 2.x queue, whose import was removed in the previous commit. Co-Authored-By: Claude Opus 5.5 (1M context) --- .../v4-remove-legacy-queue-migration.md | 2 + PostHog.xcodeproj/project.pbxproj | 4 ++ PostHog/Models/PostHogEvent.swift | 11 +----- PostHogTests/PostHogEventTest.swift | 38 +++++++++++++++++++ 4 files changed, 46 insertions(+), 9 deletions(-) create mode 100644 PostHogTests/PostHogEventTest.swift diff --git a/.changeset/v4-remove-legacy-queue-migration.md b/.changeset/v4-remove-legacy-queue-migration.md index be49671f2a..4b89aa58a2 100644 --- a/.changeset/v4-remove-legacy-queue-migration.md +++ b/.changeset/v4-remove-legacy-queue-migration.md @@ -5,3 +5,5 @@ **Breaking:** remove the migration of the event queue written by SDK 2.x (`posthog.queue.plist`). Events still queued on disk by a 2.x SDK are deleted instead of sent when the app updates straight from 2.x to 4.0. Distinct ID, anonymous ID, groups and cached feature flags are still carried over. Apps on any 3.x release are not affected. + +Queued events are no longer read in the 2.x format: a top-level `$set` and `message_id` are ignored. diff --git a/PostHog.xcodeproj/project.pbxproj b/PostHog.xcodeproj/project.pbxproj index 9a51994d44..c8ac8ab704 100644 --- a/PostHog.xcodeproj/project.pbxproj +++ b/PostHog.xcodeproj/project.pbxproj @@ -23,6 +23,7 @@ BF5E00000000000000000002 /* PHObjCExceptionCatcher.h in Headers */ = {isa = PBXBuildFile; fileRef = BF5E00000000000000000006 /* PHObjCExceptionCatcher.h */; settings = {ATTRIBUTES = (Private, ); }; }; BF5E00000000000000000003 /* PHObjCExceptionCatcher.m in Sources */ = {isa = PBXBuildFile; fileRef = BF5E00000000000000000007 /* PHObjCExceptionCatcher.m */; }; D0E500012FA0000100000002 /* PostHogEventSnapshotTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E500012FA0000100000001 /* PostHogEventSnapshotTests.swift */; }; + D0E500022FA0000100000002 /* PostHogEventTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E500022FA0000100000001 /* PostHogEventTest.swift */; }; D0E500012FA0000100000021 /* event-shapes-batch.json in Resources */ = {isa = PBXBuildFile; fileRef = D0E500012FA0000100000011 /* event-shapes-batch.json */; }; D0E500012FA0000100000022 /* feature-flags-request.json in Resources */ = {isa = PBXBuildFile; fileRef = D0E500012FA0000100000012 /* feature-flags-request.json */; }; D0E500012FA0000100000023 /* session-replay-request.json in Resources */ = {isa = PBXBuildFile; fileRef = D0E500012FA0000100000013 /* session-replay-request.json */; }; @@ -862,6 +863,7 @@ BF5E00000000000000000006 /* PHObjCExceptionCatcher.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PHObjCExceptionCatcher.h; sourceTree = ""; }; BF5E00000000000000000007 /* PHObjCExceptionCatcher.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = PHObjCExceptionCatcher.m; sourceTree = ""; }; D0E500012FA0000100000001 /* PostHogEventSnapshotTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostHogEventSnapshotTests.swift; sourceTree = ""; }; + D0E500022FA0000100000001 /* PostHogEventTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostHogEventTest.swift; sourceTree = ""; }; D0E500012FA0000100000011 /* event-shapes-batch.json */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.json; path = "event-shapes-batch.json"; sourceTree = ""; }; D0E500012FA0000100000012 /* feature-flags-request.json */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.json; path = "feature-flags-request.json"; sourceTree = ""; }; D0E500012FA0000100000013 /* session-replay-request.json */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.json; path = "session-replay-request.json"; sourceTree = ""; }; @@ -1842,6 +1844,7 @@ B74800000000000000000004 /* PostHogTests-Bridging-Header.h */, B74800000000000000000003 /* PostHogPushNotificationSwizzlingTest.swift */, D0E500012FA0000100000001 /* PostHogEventSnapshotTests.swift */, + D0E500022FA0000100000001 /* PostHogEventTest.swift */, DA27AEC82F56B3F3002A59CE /* PostHogSamplingTest.swift */, DA27AEF12F56B3F3002A59CE /* PostHogGeoIpTest.swift */, DB7100052F80000100000001 /* PostHogTracingHeadersTest.swift */, @@ -3744,6 +3747,7 @@ BF5E00000000000000000001 /* PHBeforeSendExceptionTestFixture.m in Sources */, B74800000000000000000002 /* PHNotificationDelegateTestFixture.m in Sources */, D0E500012FA0000100000002 /* PostHogEventSnapshotTests.swift in Sources */, + D0E500022FA0000100000002 /* PostHogEventTest.swift in Sources */, DA979D7B2CD370B700F56BAE /* PostHogAutocaptureEventTrackerSpec.swift in Sources */, DA0C944B2D54CDD000BFD9FB /* PostHogStorageMigrationTest.swift in Sources */, 690FF0F52AF0F06100A0B06B /* PostHogSDKTest.swift in Sources */, diff --git a/PostHog/Models/PostHogEvent.swift b/PostHog/Models/PostHogEvent.swift index 6418b3c60e..2f47b3a882 100644 --- a/PostHog/Models/PostHogEvent.swift +++ b/PostHog/Models/PostHogEvent.swift @@ -62,18 +62,11 @@ import Foundation let timestampDate = toISO8601Date(timestamp) ?? Date() - var properties = (json["properties"] as? [String: Any]) ?? [:] - - // back compatibility with v2 - let setProps = json["$set"] as? [String: Any] - if setProps != nil { - properties["$set"] = setProps - } + let properties = (json["properties"] as? [String: Any]) ?? [:] guard let distinctId = (json["distinct_id"] as? String) ?? (properties["distinct_id"] as? String) else { return nil } - // `message_id` is deprecated and only accepted for backwards compatibility with legacy persisted events. - let uuid = ((json["uuid"] as? String) ?? (json["message_id"] as? String)) ?? UUID.v7String() + let uuid = (json["uuid"] as? String) ?? UUID.v7String() let uuidObj = UUID(uuidString: uuid) ?? UUID.v7() // Wire field name remains api_key, but it carries the PostHog project token. diff --git a/PostHogTests/PostHogEventTest.swift b/PostHogTests/PostHogEventTest.swift new file mode 100644 index 0000000000..18ef37a43b --- /dev/null +++ b/PostHogTests/PostHogEventTest.swift @@ -0,0 +1,38 @@ +import Foundation +@testable import PostHog +import Testing + +@Suite("PostHogEvent JSON decoding") +struct PostHogEventTest { + @Test("ignores the 2.x top-level $set and message_id") + func ignoresV2Fields() throws { + let messageId = "5CE069F8-E967-4B47-9D89-207EF7519453" + let json: [String: Any] = [ + "event": "test", + "distinct_id": "user", + "properties": ["key": "value"], + "$set": ["plan": "pro"], + "message_id": messageId, + ] + + let event = try #require(PostHogEvent.fromJSON(json)) + + #expect(event.properties["$set"] == nil) + #expect(event.properties["key"] as? String == "value") + #expect(event.uuid.uuidString != messageId) + } + + @Test("reads uuid") + func readsUuid() throws { + let uuid = "019A0000-0000-7000-8000-000000000001" + let json: [String: Any] = [ + "event": "test", + "distinct_id": "user", + "uuid": uuid, + ] + + let event = try #require(PostHogEvent.fromJSON(json)) + + #expect(event.uuid.uuidString == uuid) + } +} From 62f33bb844b6c5f951fe890ae46b9852af9eb154 Mon Sep 17 00:00:00 2001 From: Anna Garcia Date: Tue, 6 Oct 2026 18:02:27 -0400 Subject: [PATCH 3/3] docs: narrow the changeset to events created by 3.x Co-Authored-By: Claude Opus 5.5 (1M context) --- .changeset/v4-remove-legacy-queue-migration.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.changeset/v4-remove-legacy-queue-migration.md b/.changeset/v4-remove-legacy-queue-migration.md index 4b89aa58a2..b517e957e6 100644 --- a/.changeset/v4-remove-legacy-queue-migration.md +++ b/.changeset/v4-remove-legacy-queue-migration.md @@ -4,6 +4,6 @@ **Breaking:** remove the migration of the event queue written by SDK 2.x (`posthog.queue.plist`). -Events still queued on disk by a 2.x SDK are deleted instead of sent when the app updates straight from 2.x to 4.0. Distinct ID, anonymous ID, groups and cached feature flags are still carried over. Apps on any 3.x release are not affected. +Events still queued on disk by a 2.x SDK are deleted instead of sent when the app updates straight from 2.x to 4.0. Distinct ID, anonymous ID, groups and cached feature flags are still carried over. Events created by a 3.x SDK are not affected. -Queued events are no longer read in the 2.x format: a top-level `$set` and `message_id` are ignored. +Queued events are no longer read in the 2.x format: a top-level `$set` and `message_id` are ignored. This includes 2.x events that 3.x imported and had not sent yet when the app updated to 4.0.