diff --git a/.github/workflows/swift.yml b/.github/workflows/swift.yml index a8a43bd..ed354e9 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: @@ -49,43 +49,9 @@ 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 + runs-on: macos-15 steps: - name: Install yeetd run: | @@ -94,20 +60,20 @@ 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: @@ -116,24 +82,24 @@ jobs: 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: 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 - 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: @@ -146,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: diff --git a/Package.resolved b/Package.resolved index 319773e..6cf82b7 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 bcaf696..aec6c59 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 0c8e798..1772572 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 4585bac..1eb9cad 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 01479e5..b445365 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