Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Always include traits in event payload, even if empty. #384

Merged
merged 5 commits into from
Mar 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 16 additions & 50 deletions .github/workflows/swift.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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/[email protected]
with:
Expand All @@ -37,55 +37,21 @@ 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/[email protected]
with:
ssh-private-key: ${{ secrets.SOVRAN_SSH_KEY }}
- 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/[email protected]
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: |
Expand All @@ -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/[email protected]
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/[email protected]
with:
Expand All @@ -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/[email protected]
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/[email protected]
with:
Expand All @@ -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/[email protected]
with:
Expand Down
18 changes: 0 additions & 18 deletions Package.resolved
Original file line number Diff line number Diff line change
@@ -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",
Expand All @@ -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",
Expand Down
4 changes: 2 additions & 2 deletions Sources/Segment/Analytics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -241,15 +241,15 @@ extension Analytics {
/// Returns the traits that were specified in the last identify call.
public func traits<T: Codable>() -> T? {
if let userInfo: UserInfo = store.currentState() {
return userInfo.traits?.codableValue()
return userInfo.traits.codableValue()
}
return nil
}

/// 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
}
Expand Down
10 changes: 5 additions & 5 deletions Sources/Segment/State.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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?
Expand All @@ -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)
}
}

Expand All @@ -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)
}
}

Expand All @@ -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)
}
}

Expand Down Expand Up @@ -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
Expand Down
15 changes: 14 additions & 1 deletion Tests/Segment-Tests/Analytics_Tests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -347,21 +347,34 @@ 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: "[email protected]"))

let identifyEvent: IdentifyEvent? = outputReader.lastEvent as? IdentifyEvent
XCTAssertTrue(identifyEvent?.userId == "brandon")
let traits = identifyEvent?.traits?.dictionaryValue
XCTAssertTrue(traits?["email"] as? String == "[email protected]")

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)
Expand Down Expand Up @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion Tests/Segment-Tests/JSON_Tests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class JSONTests: XCTestCase {
}

func testJSONBasic() throws {
let traits = try? JSON(["email": "[email protected]"])
let traits = try! JSON(["email": "[email protected]"])
let userInfo = UserInfo(anonymousId: "1234", userId: "brandon", traits: traits, referrer: nil)

let encoder = JSONSafeEncoder.default
Expand Down