From 2030cc52bb1fc1cb4a1a8f14321033ae0ccf3e7f Mon Sep 17 00:00:00 2001 From: Brandon Sneed Date: Wed, 19 Mar 2025 11:53:26 -0700 Subject: [PATCH 1/5] Traits can no longer be nil. Pass empty object if no traits desired. --- Package.resolved | 18 ------------------ Sources/Segment/Analytics.swift | 4 ++-- Sources/Segment/State.swift | 10 +++++----- Tests/Segment-Tests/Analytics_Tests.swift | 15 ++++++++++++++- Tests/Segment-Tests/JSON_Tests.swift | 2 +- 5 files changed, 22 insertions(+), 27 deletions(-) diff --git a/Package.resolved b/Package.resolved index 319773ef..6cf82b7f 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,14 +1,5 @@ { "pins" : [ - { - "identity" : "analytics-swift-mixpanel", - "kind" : "remoteSourceControl", - "location" : "https://github.com/segment-integrations/analytics-swift-mixpanel", - "state" : { - "revision" : "bc6a9628af225e679a581cc9ac2316eaf42f23a8", - "version" : "1.1.7" - } - }, { "identity" : "jsonsafeencoding-swift", "kind" : "remoteSourceControl", @@ -18,15 +9,6 @@ "version" : "2.0.0" } }, - { - "identity" : "mixpanel-swift", - "kind" : "remoteSourceControl", - "location" : "https://github.com/mixpanel/mixpanel-swift", - "state" : { - "revision" : "48d6668ceaaefc338f94e2b084c3cf77b90182f8", - "version" : "4.3.0" - } - }, { "identity" : "sovran-swift", "kind" : "remoteSourceControl", diff --git a/Sources/Segment/Analytics.swift b/Sources/Segment/Analytics.swift index bcaf6967..aec6c59c 100644 --- a/Sources/Segment/Analytics.swift +++ b/Sources/Segment/Analytics.swift @@ -241,7 +241,7 @@ extension Analytics { /// Returns the traits that were specified in the last identify call. public func traits() -> T? { if let userInfo: UserInfo = store.currentState() { - return userInfo.traits?.codableValue() + return userInfo.traits.codableValue() } return nil } @@ -249,7 +249,7 @@ extension Analytics { /// Returns the traits that were specified in the last identify call, as a dictionary. public func traits() -> [String: Any]? { if let userInfo: UserInfo = store.currentState() { - return userInfo.traits?.dictionaryValue + return userInfo.traits.dictionaryValue } return nil } diff --git a/Sources/Segment/State.swift b/Sources/Segment/State.swift index 0c8e798a..1772572f 100644 --- a/Sources/Segment/State.swift +++ b/Sources/Segment/State.swift @@ -108,7 +108,7 @@ struct System: State { struct UserInfo: Codable, State { let anonymousId: String let userId: String? - let traits: JSON? + let traits: JSON let referrer: URL? @Noncodable var anonIdGenerator: AnonymousIdGenerator? @@ -121,7 +121,7 @@ struct UserInfo: Codable, State { } else { anonId = UUID().uuidString } - return UserInfo(anonymousId: anonId, userId: nil, traits: nil, referrer: nil, anonIdGenerator: state.anonIdGenerator) + return UserInfo(anonymousId: anonId, userId: nil, traits: .object([:]), referrer: nil, anonIdGenerator: state.anonIdGenerator) } } @@ -137,7 +137,7 @@ struct UserInfo: Codable, State { let traits: JSON? func reduce(state: UserInfo) -> UserInfo { - return UserInfo(anonymousId: state.anonymousId, userId: state.userId, traits: traits, referrer: state.referrer, anonIdGenerator: state.anonIdGenerator) + return UserInfo(anonymousId: state.anonymousId, userId: state.userId, traits: traits ?? .object([:]), referrer: state.referrer, anonIdGenerator: state.anonIdGenerator) } } @@ -146,7 +146,7 @@ struct UserInfo: Codable, State { let traits: JSON? func reduce(state: UserInfo) -> UserInfo { - return UserInfo(anonymousId: state.anonymousId, userId: userId, traits: traits, referrer: state.referrer, anonIdGenerator: state.anonIdGenerator) + return UserInfo(anonymousId: state.anonymousId, userId: userId, traits: traits ?? .object([:]), referrer: state.referrer, anonIdGenerator: state.anonIdGenerator) } } @@ -178,7 +178,7 @@ extension System { extension UserInfo { static func defaultState(from storage: Storage, anonIdGenerator: AnonymousIdGenerator) -> UserInfo { let userId: String? = storage.read(.userId) - let traits: JSON? = storage.read(.traits) + let traits: JSON = storage.read(.traits) ?? .object([:]) var anonymousId: String if let existingId: String = storage.read(.anonymousId) { anonymousId = existingId diff --git a/Tests/Segment-Tests/Analytics_Tests.swift b/Tests/Segment-Tests/Analytics_Tests.swift index 4585bac9..1eb9cadf 100644 --- a/Tests/Segment-Tests/Analytics_Tests.swift +++ b/Tests/Segment-Tests/Analytics_Tests.swift @@ -347,11 +347,17 @@ final class Analytics_Tests: XCTestCase { } func testIdentify() { + Storage.hardSettingsReset(writeKey: "test") let analytics = Analytics(configuration: Configuration(writeKey: "test")) let outputReader = OutputReaderPlugin() analytics.add(plugin: outputReader) waitUntilStarted(analytics: analytics) + + // traits should be an empty object. + let currentTraits = analytics.traits() + XCTAssertNotNil(currentTraits) + XCTAssertTrue(currentTraits!.isEmpty == true) analytics.identify(userId: "brandon", traits: MyTraits(email: "blah@blah.com")) @@ -359,9 +365,16 @@ final class Analytics_Tests: XCTestCase { XCTAssertTrue(identifyEvent?.userId == "brandon") let traits = identifyEvent?.traits?.dictionaryValue XCTAssertTrue(traits?["email"] as? String == "blah@blah.com") + + analytics.reset() + + let emptyTraits = analytics.traits() + XCTAssertNotNil(emptyTraits) + XCTAssertTrue(emptyTraits!.isEmpty == true) } func testUserIdAndTraitsPersistCorrectly() { + Storage.hardSettingsReset(writeKey: "test") let analytics = Analytics(configuration: Configuration(writeKey: "test")) let outputReader = OutputReaderPlugin() analytics.add(plugin: outputReader) @@ -693,7 +706,7 @@ final class Analytics_Tests: XCTestCase { return request }.errorHandler { error in switch error { - case AnalyticsError.networkServerRejected(_): + case AnalyticsError.networkServerRejected(_, _): // we expect this one; it's a bogus writekey break; default: diff --git a/Tests/Segment-Tests/JSON_Tests.swift b/Tests/Segment-Tests/JSON_Tests.swift index 01479e5f..b445365d 100644 --- a/Tests/Segment-Tests/JSON_Tests.swift +++ b/Tests/Segment-Tests/JSON_Tests.swift @@ -38,7 +38,7 @@ class JSONTests: XCTestCase { } func testJSONBasic() throws { - let traits = try? JSON(["email": "blah@blah.com"]) + let traits = try! JSON(["email": "blah@blah.com"]) let userInfo = UserInfo(anonymousId: "1234", userId: "brandon", traits: traits, referrer: nil) let encoder = JSONSafeEncoder.default From a66d06996551f2b370601a6d020443852fab7087 Mon Sep 17 00:00:00 2001 From: Brandon Sneed Date: Wed, 19 Mar 2025 11:54:36 -0700 Subject: [PATCH 2/5] Remove linux/windows tests. --- .github/workflows/swift.yml | 34 ---------------------------------- 1 file changed, 34 deletions(-) diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index a8a43bdf..5d3e751f 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -49,40 +49,6 @@ jobs: - name: Build & Run tests run: swift test - build_and_test_spm_linux: - needs: cancel_previous - runs-on: ubuntu-latest - steps: - - uses: sersoft-gmbh/swifty-linux-action@v3 - with: - release-version: "5.9.2" - github-token: ${{secrets.GITHUB_TOKEN}} - - uses: actions/checkout@v2 - - uses: webfactory/ssh-agent@v0.8.0 - with: - ssh-private-key: ${{ secrets.SOVRAN_SSH_KEY }} - - name: Build & Run tests - run: swift test --enable-test-discovery - - build_and_test_spm_windows: - needs: cancel_previous - runs-on: windows-latest - steps: - - uses: SwiftyLab/setup-swift@latest - with: - swift-version: "5.10" - - uses: actions/checkout@v2 - - name: Build - run: swift build - # - # Disable tests right now. There's an SPM issue where link errors generate - # a bad exit code even though the tests run/work properly. - # - # See: https://forums.swift.org/t/linker-warnings-on-windows-with-swift-argument-parser/71443/2 - # - # - name: Run tests - # run: swift test --enable-test-discovery - build_and_test_ios: needs: cancel_previous runs-on: macos-14 From 0eaa4949431e9a72f4babaa462dd7b2fac3f82df Mon Sep 17 00:00:00 2001 From: Brandon Sneed Date: Wed, 19 Mar 2025 12:05:57 -0700 Subject: [PATCH 3/5] Attempt to fix Apple TV test runner. --- .github/workflows/swift.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index 5d3e751f..a3d362c4 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -78,7 +78,7 @@ jobs: - uses: webfactory/ssh-agent@v0.8.0 with: ssh-private-key: ${{ secrets.SOVRAN_SSH_KEY }} - - run: xcodebuild -scheme Segment test -sdk appletvsimulator -destination 'platform=tvOS Simulator,name=Apple TV' + - run: xcodebuild -scheme Segment test -sdk appletvsimulator -destination 'platform=tvOS Simulator,name=Apple TV 4K' build_and_test_watchos: needs: cancel_previous From acc44037834350010d5a01301d85bb1d017d9be4 Mon Sep 17 00:00:00 2001 From: Brandon Sneed Date: Wed, 19 Mar 2025 12:18:30 -0700 Subject: [PATCH 4/5] Update test runners. --- .github/workflows/swift.yml | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index a3d362c4..dde7df2b 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -16,11 +16,11 @@ jobs: generate_code_coverage: needs: cancel_previous - runs-on: macos-14 + runs-on: macos-15 steps: - uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: "15.2" + xcode-version: "16.2" - uses: actions/checkout@v2 - uses: webfactory/ssh-agent@v0.8.0 with: @@ -37,11 +37,11 @@ jobs: build_and_test_spm_mac: needs: cancel_previous - runs-on: macos-14 + runs-on: macos-15 steps: - uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: "15.2" + xcode-version: "16.2" - uses: actions/checkout@v2 - uses: webfactory/ssh-agent@v0.8.0 with: @@ -51,7 +51,7 @@ jobs: build_and_test_ios: needs: cancel_previous - runs-on: macos-14 + runs-on: macos-15 steps: - name: Install yeetd run: | @@ -60,33 +60,33 @@ jobs: yeetd & - uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: "15.2" + xcode-version: "16.2" - uses: actions/checkout@v2 - uses: webfactory/ssh-agent@v0.8.0 with: ssh-private-key: ${{ secrets.SOVRAN_SSH_KEY }} - - run: xcodebuild -scheme Segment test -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 15' + - run: xcodebuild -scheme Segment test -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 16' build_and_test_tvos: needs: cancel_previous - runs-on: macos-14 + runs-on: macos-15 steps: - uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: "15.2" + xcode-version: "16.2" - uses: actions/checkout@v2 - uses: webfactory/ssh-agent@v0.8.0 with: ssh-private-key: ${{ secrets.SOVRAN_SSH_KEY }} - - run: xcodebuild -scheme Segment test -sdk appletvsimulator -destination 'platform=tvOS Simulator,name=Apple TV 4K' + - run: xcodebuild -scheme Segment test -sdk appletvsimulator -destination 'platform=tvOS Simulator,name=Apple TV' build_and_test_watchos: needs: cancel_previous - runs-on: macos-14 + runs-on: macos-15 steps: - uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: "15.2" + xcode-version: "16.2" - uses: actions/checkout@v2 - uses: webfactory/ssh-agent@v0.8.0 with: @@ -95,11 +95,11 @@ jobs: build_and_test_visionos: needs: cancel_previous - runs-on: macos-14 + runs-on: macos-15 steps: - uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: "15.2" + xcode-version: "16.2" - uses: actions/checkout@v2 - uses: webfactory/ssh-agent@v0.8.0 with: @@ -112,11 +112,11 @@ jobs: build_and_test_examples: needs: cancel_previous - runs-on: macos-14 + runs-on: macos-15 steps: - uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: "15.2" + xcode-version: "16.2" - uses: actions/checkout@v2 - uses: webfactory/ssh-agent@v0.8.0 with: From 77bdd7e7530281bc6ec3e0c5df6b527aef1ce156 Mon Sep 17 00:00:00 2001 From: Brandon Sneed Date: Wed, 19 Mar 2025 12:27:21 -0700 Subject: [PATCH 5/5] fix watchOS test runner --- .github/workflows/swift.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index dde7df2b..ed354e94 100644 --- a/.github/workflows/swift.yml +++ b/.github/workflows/swift.yml @@ -91,7 +91,7 @@ jobs: - uses: webfactory/ssh-agent@v0.8.0 with: ssh-private-key: ${{ secrets.SOVRAN_SSH_KEY }} - - run: xcodebuild -scheme Segment test -sdk watchsimulator -destination 'platform=watchOS Simulator,name=Apple Watch Series 9 (45mm)' + - run: xcodebuild -scheme Segment test -sdk watchsimulator -destination 'platform=watchOS Simulator,name=Apple Watch Series 10 (42mm)' build_and_test_visionos: needs: cancel_previous