Skip to content

Commit 7a330b0

Browse files
Add visionOS support (#291)
* Add support for visionOS * Fix watchOS build error * Remove one more sessionReplay API on visionOS * feat: add native visionOS sample project * chore: update changelog * fix: application lifecycle events * fix: remove visionOS from untested integrations * chore: debug CI * fix: add RealityKitContent package --------- Co-authored-by: Ioannis J <[email protected]>
1 parent 1c12f08 commit 7a330b0

File tree

36 files changed

+1550
-121
lines changed

36 files changed

+1550
-121
lines changed

CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
## Next
22

3+
- fix: visionOS builds ([#291](https://github.com/PostHog/posthog-ios/pull/291))
4+
Thanks @harlanhaskins ❤️
5+
36
- feat: improve session replay throttle logic ([#322](https://github.com/PostHog/posthog-ios/pull/322))
47
> Note: `debouncerDelay` is deprecated and will be removed in next major update. Use `throttleDelay` instead which provides identical functionality for controlling session replay capture frequency.
58

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ buildExamplePods: \
2424
buildExamplesPlatforms:
2525
set -o pipefail && xcrun xcodebuild -downloadAllPlatforms
2626
set -o pipefail && xcrun xcodebuild clean build -scheme PostHogExample -destination generic/platform=ios | xcpretty #ios
27-
set -o pipefail && xcrun xcodebuild clean build -scheme PostHogExample -destination 'platform=visionOS Simulator,name=Apple Vision Pro' | xcpretty #visionOS
27+
set -o pipefail && xcrun xcodebuild clean build -scheme PostHogExampleVisionOS -destination 'platform=visionOS Simulator,name=Apple Vision Pro' | xcpretty #visionOS
2828
set -o pipefail && xcrun xcodebuild clean build -scheme PostHogObjCExample -destination generic/platform=ios | xcpretty #ObjC
2929
set -o pipefail && xcrun xcodebuild clean build -scheme PostHogExampleMacOS -destination generic/platform=macos | xcpretty #macOS
3030
set -o pipefail && xcrun xcodebuild clean build -scheme 'PostHogExampleWatchOS Watch App' -destination generic/platform=watchos | xcpretty #watchOS

PostHog.xcodeproj/project.pbxproj

+136-72
Large diffs are not rendered by default.

PostHog/App Life Cycle/ApplicationLifecyclePublisher.swift

+14-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
// Created by Yiannis Josephides on 16/12/2024.
66
//
77

8-
#if os(iOS) || os(tvOS)
8+
#if os(iOS) || os(tvOS) || os(visionOS)
99
import UIKit
1010
#elseif os(macOS)
1111
import AppKit
@@ -62,6 +62,19 @@ final class ApplicationLifecyclePublisher: BaseApplicationLifecyclePublisher {
6262
selector: #selector(appDidBecomeActive),
6363
name: UIApplication.didBecomeActiveNotification,
6464
object: nil)
65+
#elseif os(visionOS)
66+
defaultCenter.addObserver(self,
67+
selector: #selector(appDidFinishLaunching),
68+
name: UIApplication.didFinishLaunchingNotification,
69+
object: nil)
70+
defaultCenter.addObserver(self,
71+
selector: #selector(appDidEnterBackground),
72+
name: UIScene.willDeactivateNotification,
73+
object: nil)
74+
defaultCenter.addObserver(self,
75+
selector: #selector(appDidBecomeActive),
76+
name: UIScene.didActivateNotification,
77+
object: nil)
6578
#elseif os(macOS)
6679
defaultCenter.addObserver(self,
6780
selector: #selector(appDidFinishLaunching),

PostHog/PostHogContext.swift

+17-9
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
import Foundation
99

10-
#if os(iOS) || os(tvOS)
10+
#if os(iOS) || os(tvOS) || os(visionOS)
1111
import UIKit
1212
#elseif os(macOS)
1313
import AppKit
@@ -55,7 +55,7 @@ class PostHogContext {
5555

5656
// iOS app running in compatibility mode (Designed for iPad/iPhone)
5757
var isiOSAppOnMac = false
58-
#if os(iOS)
58+
#if os(iOS) || os(visionOS)
5959
if #available(iOS 14.0, *) {
6060
isiOSAppOnMac = ProcessInfo.processInfo.isiOSAppOnMac
6161
}
@@ -71,7 +71,7 @@ class PostHogContext {
7171
properties["$is_ios_running_on_mac"] = isiOSAppOnMac
7272
properties["$is_mac_catalyst_app"] = isMacCatalystApp
7373

74-
#if os(iOS) || os(tvOS)
74+
#if os(iOS) || os(tvOS) || os(visionOS)
7575
let device = UIDevice.current
7676
// use https://github.com/devicekit/DeviceKit
7777
let processInfo = ProcessInfo.processInfo
@@ -118,6 +118,8 @@ class PostHogContext {
118118
deviceType = "CarPlay"
119119
case UIUserInterfaceIdiom.mac:
120120
deviceType = "Desktop"
121+
case UIUserInterfaceIdiom.vision:
122+
deviceType = "Vision"
121123
default:
122124
deviceType = nil
123125
}
@@ -188,7 +190,7 @@ class PostHogContext {
188190
// - "hw.model" returns mac model
189191
#if targetEnvironment(macCatalyst)
190192
sysctlName = "hw.model"
191-
#elseif os(iOS)
193+
#elseif os(iOS) || os(visionOS)
192194
if #available(iOS 14.0, *) {
193195
if ProcessInfo.processInfo.isiOSAppOnMac {
194196
sysctlName = "hw.model"
@@ -211,8 +213,14 @@ class PostHogContext {
211213
properties["$screen_height"] = Float(screenSize.height)
212214
}
213215

214-
if Locale.current.languageCode != nil {
215-
properties["$locale"] = Locale.current.languageCode
216+
if #available(iOS 16.0, macOS 13.0, tvOS 16.0, watchOS 9.0, *) {
217+
if let languageCode = Locale.current.language.languageCode {
218+
properties["$locale"] = languageCode.identifier
219+
}
220+
} else {
221+
if Locale.current.languageCode != nil {
222+
properties["$locale"] = Locale.current.languageCode
223+
}
216224
}
217225
properties["$timezone"] = TimeZone.current.identifier
218226

@@ -227,7 +235,7 @@ class PostHogContext {
227235
}
228236

229237
private func registerNotifications() {
230-
#if os(iOS) || os(tvOS)
238+
#if os(iOS) || os(tvOS) || os(visionOS)
231239
#if os(iOS)
232240
NotificationCenter.default.addObserver(self,
233241
selector: #selector(onOrientationDidChange),
@@ -262,7 +270,7 @@ class PostHogContext {
262270
}
263271

264272
private func unregisterNotifications() {
265-
#if os(iOS) || os(tvOS)
273+
#if os(iOS) || os(tvOS) || os(visionOS)
266274
#if os(iOS)
267275
NotificationCenter.default.removeObserver(self,
268276
name: UIDevice.orientationDidChangeNotification,
@@ -293,7 +301,7 @@ class PostHogContext {
293301

294302
/// Retrieves the current screen size of the application window based on platform
295303
private func getScreenSize() -> CGSize? {
296-
#if os(iOS) || os(tvOS)
304+
#if os(iOS) || os(tvOS) || os(visionOS)
297305
return UIApplication.getCurrentWindow(filterForegrounded: false)?.bounds.size
298306
#elseif os(macOS)
299307
// NSScreen.frame represents the full screen rectangle and includes any space occupied by menu, dock or camera bezel

PostHog/Utils/Reachability.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -305,7 +305,7 @@ import Foundation
305305
}
306306

307307
var isOnWWANFlagSet: Bool {
308-
#if os(iOS)
308+
#if os(iOS) || os(visionOS)
309309
return contains(.isWWAN)
310310
#else
311311
return false

PostHog/Utils/UIApplication+.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
// Created by Yiannis Josephides on 11/11/2024.
66
//
77

8-
#if os(iOS) || os(tvOS)
8+
#if os(iOS) || os(tvOS) || os(visionOS)
99
import UIKit
1010

1111
extension UIApplication {

PostHogExample/AppDelegate.swift

+8-5
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,13 @@ class AppDelegate: NSObject, UIApplicationDelegate {
2121
// config.flushIntervalSeconds = 30
2222
config.debug = true
2323
config.sendFeatureFlagEvent = false
24-
config.sessionReplay = true
25-
config.sessionReplayConfig.screenshotMode = true
26-
config.sessionReplayConfig.maskAllTextInputs = true
27-
config.sessionReplayConfig.maskAllImages = true
24+
25+
#if os(iOS)
26+
config.sessionReplay = true
27+
config.sessionReplayConfig.screenshotMode = true
28+
config.sessionReplayConfig.maskAllTextInputs = true
29+
config.sessionReplayConfig.maskAllImages = true
30+
#endif
2831

2932
PostHogSDK.shared.setup(config)
3033
// PostHogSDK.shared.debug()
@@ -35,7 +38,7 @@ class AppDelegate: NSObject, UIApplicationDelegate {
3538

3639
let defaultCenter = NotificationCenter.default
3740

38-
#if os(iOS) || os(tvOS)
41+
#if os(iOS) || os(tvOS) || os(visionOS)
3942
defaultCenter.addObserver(self,
4043
selector: #selector(receiveFeatureFlags),
4144
name: PostHogSDK.didReceiveFeatureFlags,

PostHogExample/ContentView.swift

+40-29
Original file line numberDiff line numberDiff line change
@@ -93,39 +93,43 @@ struct ContentView: View {
9393
var body: some View {
9494
NavigationStack {
9595
List {
96-
Section("Manual Session Recording Control") {
97-
Text("\(sessionRecordingStatus) SID: \(PostHogSDK.shared.getSessionId() ?? "NA")")
98-
.lineLimit(1)
99-
.truncationMode(.middle)
100-
.multilineTextAlignment(.leading)
101-
.id(refreshStatusID)
96+
#if os(iOS)
97+
Section("Manual Session Recording Control") {
98+
Text("\(sessionRecordingStatus) SID: \(PostHogSDK.shared.getSessionId() ?? "NA")")
99+
.lineLimit(1)
100+
.truncationMode(.middle)
101+
.multilineTextAlignment(.leading)
102+
.id(refreshStatusID)
102103

103-
Button("Stop") {
104-
PostHogSDK.shared.stopSessionRecording()
105-
DispatchQueue.main.async {
106-
refreshStatusID = UUID()
104+
Button("Stop") {
105+
PostHogSDK.shared.stopSessionRecording()
106+
DispatchQueue.main.async {
107+
refreshStatusID = UUID()
108+
}
107109
}
108-
}
109-
Button("Resume") {
110-
PostHogSDK.shared.startSessionRecording()
111-
DispatchQueue.main.async {
112-
refreshStatusID = UUID()
110+
Button("Resume") {
111+
PostHogSDK.shared.startSessionRecording()
112+
DispatchQueue.main.async {
113+
refreshStatusID = UUID()
114+
}
113115
}
114-
}
115-
Button("Start New Session") {
116-
PostHogSDK.shared.startSessionRecording(resumeCurrent: false)
117-
DispatchQueue.main.async {
118-
refreshStatusID = UUID()
116+
Button("Start New Session") {
117+
PostHogSDK.shared.startSessionRecording(resumeCurrent: false)
118+
DispatchQueue.main.async {
119+
refreshStatusID = UUID()
120+
}
119121
}
120122
}
121-
}
123+
#endif
122124
Section("General") {
123125
NavigationLink {
124126
ContentView()
125127
} label: {
126128
Text("Infinite navigation")
127129
}
130+
#if os(iOS)
128131
.postHogMask()
132+
#endif
129133

130134
HStack {
131135
Spacer()
@@ -169,14 +173,19 @@ struct ContentView: View {
169173
RepresentedExampleUIView()
170174
}
171175

172-
Text("Sensitive text!!").postHogMask()
173-
Button(action: incCounter) {
174-
Text(String(counter))
175-
}
176-
.postHogMask()
176+
#if os(iOS)
177+
Text("Sensitive text!!").postHogMask()
178+
Button(action: incCounter) {
179+
Text(String(counter))
180+
}
181+
.postHogMask()
182+
#endif
177183

178184
TextField("Enter your name", text: $name)
185+
#if os(iOS)
179186
.postHogMask()
187+
#endif
188+
180189
Text("Hello, \(name)!")
181190
Button(action: triggerAuthentication) {
182191
Text("Trigger fake authentication!")
@@ -244,9 +253,11 @@ struct ContentView: View {
244253
}
245254
}
246255

247-
private var sessionRecordingStatus: String {
248-
PostHogSDK.shared.isSessionReplayActive() ? "🟢" : "🔴"
249-
}
256+
#if os(iOS)
257+
private var sessionRecordingStatus: String {
258+
PostHogSDK.shared.isSessionReplayActive() ? "🟢" : "🔴"
259+
}
260+
#endif
250261
}
251262

252263
struct ContentView_Previews: PreviewProvider {

0 commit comments

Comments
 (0)