Skip to content
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
38 changes: 17 additions & 21 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-15
runs-on: macos-26
steps:
- uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: "16.2"
xcode-version: "26"
- uses: actions/checkout@v2
- uses: webfactory/ssh-agent@v0.8.0
with:
Expand All @@ -37,11 +37,11 @@ jobs:

build_and_test_spm_mac:
needs: cancel_previous
runs-on: macos-15
runs-on: macos-26
steps:
- uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: "16.2"
xcode-version: "26"
- uses: actions/checkout@v2
- uses: webfactory/ssh-agent@v0.8.0
with:
Expand All @@ -51,24 +51,24 @@ jobs:

build_and_test_ios:
needs: cancel_previous
runs-on: macos-15
runs-on: macos-26
steps:
- uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: "16.2"
xcode-version: "26"
- 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 16'
- run: xcodebuild -scheme Segment test -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 17'

build_and_test_tvos:
needs: cancel_previous
runs-on: macos-15
runs-on: macos-26
steps:
- uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: "16.2"
xcode-version: "26"
- uses: actions/checkout@v2
- uses: webfactory/ssh-agent@v0.8.0
with:
Expand All @@ -77,41 +77,37 @@ jobs:

build_and_test_watchos:
needs: cancel_previous
runs-on: macos-15
runs-on: macos-26
steps:
- uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: "16.2"
xcode-version: "26"
- 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 10 (42mm)'
- run: xcodebuild -scheme Segment test -sdk watchsimulator -destination 'platform=watchOS Simulator,name=Apple Watch Ultra 3 (49mm)'

build_and_test_visionos:
needs: cancel_previous
runs-on: macos-15
runs-on: macos-26
steps:
- uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: "16.2"
xcode-version: "26"
- uses: actions/checkout@v2
- uses: webfactory/ssh-agent@v0.8.0
with:
ssh-private-key: ${{ secrets.SOVRAN_SSH_KEY }}
- run: defaults write com.apple.dt.Xcode AllowUnsupportedVisionOSHost -bool YES
- run: defaults write com.apple.CoreSimulator AllowUnsupportedVisionOSHost -bool YES
- run: xcodebuild -downloadPlatform visionOS
- run: echo - skip until apple fixes this - xcodebuild -scheme Segment test -sdk xrsimulator -destination 'platform=visionOS Simulator,name=Apple Vision Pro'
- run: xcodebuild -scheme Segment -sdk xrsimulator -destination 'platform=visionOS Simulator,name=Apple Vision Pro'
- run: xcodebuild -scheme Segment test -sdk xrsimulator -destination 'platform=visionOS Simulator,os=26,name=Apple Vision Pro'

build_and_test_examples:
needs: cancel_previous
runs-on: macos-15
runs-on: macos-26
steps:
- uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: "16.2"
xcode-version: "26"
- uses: actions/checkout@v2
- uses: webfactory/ssh-agent@v0.8.0
with:
Expand Down
61 changes: 60 additions & 1 deletion Sources/Segment/Analytics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,66 @@ extension Analytics {
}
}
}


/// Subscribes to UserInfo state changes.
///
/// The handler is called immediately with the current UserInfo, then again whenever
/// the user's identity, traits, or referrer changes. The subscription remains active
/// for the lifetime of the Analytics instance unless explicitly unsubscribed.
///
/// - Parameter handler: A closure called on the main queue with updated UserInfo.
///
/// - Returns: A subscription ID that can be passed to `unsubscribe(_:)` to stop
/// receiving updates. If you don't need to unsubscribe, you can ignore the return value.
///
/// - Note: Multiple calls create multiple independent subscriptions.
///
/// ## Example
/// ```swift
/// // Subscribe for the lifetime of Analytics
/// analytics.subscribeToUserInfo { userInfo in
/// print("User: \(userInfo.userId ?? userInfo.anonymousId)")
/// if let referrer = userInfo.referrer {
/// print("Referred from: \(referrer)")
/// }
/// }
///
/// // Subscribe with manual cleanup
/// let subscriptionId = analytics.subscribeToUserInfo { userInfo in
/// // ... handle update
/// }
/// // Later, when you're done...
/// analytics.unsubscribe(subscriptionId)
/// ```
@discardableResult
public func subscribeToUserInfo(handler: @escaping (UserInfo) -> ()) -> Int {
return store.subscribe(self, initialState: true, queue: .main) { (state: UserInfo) in
handler(state)
}
}

/// Unsubscribes from state updates.
///
/// Stops receiving updates for the subscription associated with the given ID.
/// After calling this, the handler will no longer be invoked for state changes.
///
/// - Parameter id: The subscription ID returned from a previous subscribe call.
///
/// - Note: Unsubscribing an already-unsubscribed or invalid ID is a no-op.
///
/// ## Example
/// ```swift
/// let id = analytics.subscribeToUserInfo { userInfo in
/// print("User changed: \(userInfo.userId ?? "anonymous")")
/// }
///
/// // Later, stop listening
/// analytics.unsubscribe(id)
/// ```
public func unsubscribe(_ id: Int) {
store.unsubscribe(identifier: id)
}

/// Retrieve the version of this library in use.
/// - Returns: A string representing the version in "BREAKING.FEATURE.FIX" format.
public func version() -> String {
Expand Down
3 changes: 3 additions & 0 deletions Sources/Segment/Settings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ public struct Settings: Codable {
public var plan: JSON? = nil
public var edgeFunction: JSON? = nil
public var middlewareSettings: JSON? = nil
public var autoInstrumentation: JSON? = nil
public var metrics: JSON? = nil
public var consentSettings: JSON? = nil

Expand Down Expand Up @@ -39,6 +40,7 @@ public struct Settings: Codable {
self.plan = try? values.decode(JSON.self, forKey: CodingKeys.plan)
self.edgeFunction = try? values.decode(JSON.self, forKey: CodingKeys.edgeFunction)
self.middlewareSettings = try? values.decode(JSON.self, forKey: CodingKeys.middlewareSettings)
self.autoInstrumentation = try? values.decode(JSON.self, forKey: CodingKeys.autoInstrumentation)
self.metrics = try? values.decode(JSON.self, forKey: CodingKeys.metrics)
self.consentSettings = try? values.decode(JSON.self, forKey: CodingKeys.consentSettings)
}
Expand All @@ -60,6 +62,7 @@ public struct Settings: Codable {
case plan
case edgeFunction
case middlewareSettings
case autoInstrumentation
case metrics
case consentSettings
}
Expand Down
6 changes: 4 additions & 2 deletions Sources/Segment/State.swift
Original file line number Diff line number Diff line change
Expand Up @@ -166,14 +166,16 @@ struct System: State {

// MARK: - User information

struct UserInfo: Codable, State {
public struct UserInfo: Codable, State {
let anonymousId: String
let userId: String?
let traits: JSON
let referrer: URL?

@Noncodable var anonIdGenerator: AnonymousIdGenerator?

}

extension UserInfo {
struct ResetAction: Action {
func reduce(state: UserInfo) -> UserInfo {
var anonId: String
Expand Down
8 changes: 4 additions & 4 deletions Sources/Segment/Utilities/Logging.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@
import Foundation

extension Analytics {
internal enum LogKind: CustomStringConvertible, CustomDebugStringConvertible {
public enum LogKind: CustomStringConvertible, CustomDebugStringConvertible {
case error
case warning
case debug
case none

var description: String { return string }
var debugDescription: String { return string }
public var description: String { return string }
public var debugDescription: String { return string }

var string: String {
switch self {
Expand All @@ -35,7 +35,7 @@ extension Analytics {
Self.segmentLog(message: message, kind: .none)
}

static internal func segmentLog(message: String, kind: LogKind) {
static public func segmentLog(message: String, kind: LogKind) {
#if DEBUG
if Self.debugLogsEnabled {
print("\(kind)\(message)")
Expand Down
Loading
Loading