Skip to content

Commit bb3fa25

Browse files
committed
Added better swiftUI support.
1 parent 8655432 commit bb3fa25

File tree

2 files changed

+128
-4
lines changed

2 files changed

+128
-4
lines changed

Sources/Segment/Plugins/Platforms/iOS/iOSLifecycleEvents.swift

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,12 @@ class iOSLifecycleEvents: PlatformPlugin, iOSLifecycle {
5858

5959
func applicationDidEnterBackground(application: UIApplication?) {
6060
_didFinishLaunching.set(false)
61-
_wasBackgrounded.set(true)
62-
63-
if analytics?.configuration.values.trackedApplicationLifecycleEvents.contains(.applicationBackgrounded) == true {
64-
analytics?.trackApplicationBackgrounded()
61+
if !wasBackgrounded {
62+
_wasBackgrounded.set(true)
63+
64+
if analytics?.configuration.values.trackedApplicationLifecycleEvents.contains(.applicationBackgrounded) == true {
65+
analytics?.trackApplicationBackgrounded()
66+
}
6567
}
6668
}
6769

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
//
2+
// SwiftUISupport.swift
3+
// Segment
4+
//
5+
// Created by Brandon Sneed on 11/3/25.
6+
//
7+
8+
#if canImport(SwiftUI)
9+
import SwiftUI
10+
11+
@available(iOS 14.0, tvOS 14.0, watchOS 7.0, macOS 11.0, *)
12+
struct AnalyticsLifecycleModifier: ViewModifier {
13+
let analytics: Analytics
14+
@Environment(\.scenePhase) private var scenePhase
15+
@State private var previousPhase: ScenePhase?
16+
@State private var hasLaunched = false
17+
18+
func body(content: Content) -> some View {
19+
content
20+
.onAppear {
21+
handleInitialLaunch()
22+
}
23+
.onChange(of: scenePhase) { newPhase in
24+
handlePhaseChange(newPhase)
25+
}
26+
}
27+
28+
private func handleInitialLaunch() {
29+
guard !hasLaunched else { return }
30+
31+
// First launch - check install/update and track opened
32+
analytics.checkAndTrackInstallOrUpdate()
33+
34+
if analytics.configuration.values.trackedApplicationLifecycleEvents.contains(.applicationOpened) {
35+
analytics.trackApplicationOpened(fromBackground: false)
36+
}
37+
38+
hasLaunched = true
39+
previousPhase = scenePhase
40+
}
41+
42+
private func handlePhaseChange(_ newPhase: ScenePhase) {
43+
defer { previousPhase = newPhase }
44+
45+
// Only handle transitions after initial launch
46+
guard hasLaunched else { return }
47+
48+
let config = analytics.configuration.values.trackedApplicationLifecycleEvents
49+
50+
switch (previousPhase, newPhase) {
51+
case (.background, .active):
52+
// Coming from background to foreground
53+
if config.contains(.applicationOpened) {
54+
analytics.trackApplicationOpened(fromBackground: true)
55+
}
56+
if config.contains(.applicationForegrounded) {
57+
analytics.trackApplicationForegrounded()
58+
}
59+
60+
case (.active, .background), (.inactive, .background):
61+
// Going to background
62+
/// NOTE: this isn't needed because the regular lifecycle notifications capture this.
63+
/*if config.contains(.applicationBackgrounded) {
64+
analytics.trackApplicationBackgrounded()
65+
}*/
66+
break
67+
68+
case (.background, .inactive):
69+
// Transitioning from background through inactive to active
70+
// Don't track anything here, wait for .active
71+
break
72+
73+
case (.active, .inactive):
74+
// Brief interruption (like control center pull-down)
75+
// Don't track as background yet
76+
break
77+
78+
case (.inactive, .active):
79+
// Resuming from brief interruption
80+
// Don't track as opened from background
81+
break
82+
83+
default:
84+
break
85+
}
86+
}
87+
}
88+
89+
@available(iOS 14.0, tvOS 14.0, watchOS 7.0, macOS 11.0, *)
90+
public extension View {
91+
/// Automatically tracks Analytics lifecycle events for SwiftUI apps.
92+
///
93+
/// Attach this modifier to your root view to enable automatic tracking of:
94+
/// - Application Installed (first launch only)
95+
/// - Application Updated (when version/build changes)
96+
/// - Application Opened (cold start and from background)
97+
/// - Application Backgrounded
98+
/// - Application Foregrounded
99+
///
100+
/// Example:
101+
/// ```swift
102+
/// @main
103+
/// struct MyApp: App {
104+
/// let analytics = Analytics(configuration: config)
105+
///
106+
/// var body: some Scene {
107+
/// WindowGroup {
108+
/// ContentView()
109+
/// .trackAnalyticsLifecycle(analytics)
110+
/// }
111+
/// }
112+
/// }
113+
/// ```
114+
///
115+
/// - Parameter analytics: The Analytics instance to track events with
116+
/// - Returns: A view with lifecycle tracking enabled
117+
func trackAnalyticsLifecycle(_ analytics: Analytics) -> some View {
118+
self.modifier(AnalyticsLifecycleModifier(analytics: analytics))
119+
}
120+
}
121+
122+
#endif

0 commit comments

Comments
 (0)