Skip to content

Commit

Permalink
Merge branch 'main' into fix/zero-touch-coordinates
Browse files Browse the repository at this point in the history
# Conflicts:
#	CHANGELOG.md
  • Loading branch information
ioannisj committed Nov 19, 2024
2 parents 81a4a34 + cccf985 commit 7f4cae4
Show file tree
Hide file tree
Showing 3 changed files with 153 additions and 19 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## Next

- fix: reading screen size could sometimes lead to a deadlock ([#252](https://github.com/PostHog/posthog-ios/pull/252))
- fix: avoid zero touch locations ([#256](https://github.com/PostHog/posthog-ios/pull/256))

## 3.15.3 - 2024-11-18
Expand Down
151 changes: 132 additions & 19 deletions PostHog/PostHogContext.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,13 @@ import Foundation
#endif

class PostHogContext {
@ReadWriteLock
private var screenSize: CGSize?

#if !os(watchOS)
private let reachability: Reachability?
#endif

private var screenSize: CGSize? {
let getWindowSize: () -> CGSize? = {
#if os(iOS) || os(tvOS)
return UIApplication.getCurrentWindow(filterForegrounded: false)?.bounds.size
#elseif os(macOS)
return NSScreen.main?.visibleFrame.size
#elseif os(watchOS)
return WKInterfaceDevice.current().screenBounds.size
#else
return nil
#endif
}

return Thread.isMainThread
? getWindowSize()
: DispatchQueue.main.sync { getWindowSize() }
}

private lazy var theStaticContext: [String: Any] = {
// Properties that do not change over the lifecycle of an application
var properties: [String: Any] = [:]
Expand Down Expand Up @@ -111,11 +96,28 @@ class PostHogContext {
#if !os(watchOS)
init(_ reachability: Reachability?) {
self.reachability = reachability
registerNotifications()
}
#else
init() {}
init() {
if #available(watchOS 7.0, *) {
registerNotifications()
} else {
onShouldUpdateScreenSize()
}
}
#endif

deinit {
#if !os(watchOS)
unregisterNotifications()
#else
if #available(watchOS 7.0, *) {
unregisterNotifications()
}
#endif
}

private lazy var theSdkInfo: [String: Any] = {
var sdkInfo: [String: Any] = [:]
sdkInfo["$lib"] = postHogSdkName
Expand Down Expand Up @@ -161,4 +163,115 @@ class PostHogContext {

return properties
}

private func registerNotifications() {
#if os(iOS) || os(tvOS)
#if os(iOS)
NotificationCenter.default.addObserver(self,
selector: #selector(onOrientationDidChange),
name: UIDevice.orientationDidChangeNotification,
object: nil)
#endif
NotificationCenter.default.addObserver(self,
selector: #selector(onShouldUpdateScreenSize),
name: UIWindow.didBecomeKeyNotification,
object: nil)
#elseif os(macOS)
NotificationCenter.default.addObserver(self,
selector: #selector(onShouldUpdateScreenSize),
name: NSWindow.didBecomeKeyNotification,
object: nil)
NotificationCenter.default.addObserver(self,
selector: #selector(onShouldUpdateScreenSize),
name: NSWindow.didChangeScreenNotification,
object: nil)
NotificationCenter.default.addObserver(self,
selector: #selector(onShouldUpdateScreenSize),
name: NSApplication.didBecomeActiveNotification,
object: nil)
#elseif os(watchOS)
if #available(watchOS 7.0, *) {
NotificationCenter.default.addObserver(self,
selector: #selector(onShouldUpdateScreenSize),
name: WKApplication.didBecomeActiveNotification,
object: nil)
}
#endif
}

private func unregisterNotifications() {
#if os(iOS) || os(tvOS)
#if os(iOS)
NotificationCenter.default.removeObserver(self,
name: UIDevice.orientationDidChangeNotification,
object: nil)
#endif
NotificationCenter.default.removeObserver(self,
name: UIWindow.didBecomeKeyNotification,
object: nil)

#elseif os(macOS)
NotificationCenter.default.removeObserver(self,
name: NSWindow.didBecomeKeyNotification,
object: nil)
NotificationCenter.default.removeObserver(self,
name: NSWindow.didChangeScreenNotification,
object: nil)
NotificationCenter.default.removeObserver(self,
name: NSApplication.didBecomeActiveNotification,
object: nil)
#elseif os(watchOS)
if #available(watchOS 7.0, *) {
NotificationCenter.default.removeObserver(self,
name: WKApplication.didBecomeActiveNotification,
object: nil)
}
#endif
}

/// Retrieves the current screen size of the application window based on platform
private func getScreenSize() -> CGSize? {
#if os(iOS) || os(tvOS)
return UIApplication.getCurrentWindow(filterForegrounded: false)?.bounds.size
#elseif os(macOS)
// NSScreen.frame represents the full screen rectangle and includes any space occupied by menu, dock or camera bezel
return NSApplication.shared.windows.first { $0.isKeyWindow }?.screen?.frame.size
#elseif os(watchOS)
return WKInterfaceDevice.current().screenBounds.size
#else
return nil
#endif
}

#if os(iOS)
// Special treatment for `orientationDidChangeNotification` since the notification seems to be _sometimes_ called early, before screen bounds are flipped
@objc private func onOrientationDidChange() {
updateScreenSize {
self.getScreenSize().map { size in
// manually set width and height based on device orientation. (Needed for fast orientation changes)
if UIDevice.current.orientation.isLandscape {
CGSize(width: max(size.width, size.height), height: min(size.height, size.width))
} else {
CGSize(width: min(size.width, size.height), height: max(size.height, size.width))
}
}
}
}
#endif

@objc private func onShouldUpdateScreenSize() {
updateScreenSize(getScreenSize)
}

private func updateScreenSize(_ getSize: @escaping () -> CGSize?) {
let block = {
self.screenSize = getSize()
}
// ensure block is executed on `main` since closure accesses non thread-safe UI objects like UIApplication
if Thread.isMainThread {
block()
} else {
DispatchQueue.main.async(execute: block)
}
}
}
20 changes: 20 additions & 0 deletions PostHog/PostHogSDK.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import Foundation
import UIKit
#elseif os(macOS)
import AppKit
#elseif os(watchOS)
import WatchKit
#endif

let retryDelay = 5.0
Expand Down Expand Up @@ -1042,6 +1044,12 @@ let maxRetryDelay = 30.0
defaultCenter.removeObserver(self, name: NSApplication.didFinishLaunchingNotification, object: nil)
defaultCenter.removeObserver(self, name: NSApplication.didResignActiveNotification, object: nil)
defaultCenter.removeObserver(self, name: NSApplication.didBecomeActiveNotification, object: nil)
#elseif os(watchOS)
if #available(watchOS 7.0, *) {
NotificationCenter.default.removeObserver(self,
name: WKApplication.didBecomeActiveNotification,
object: nil)
}
#endif
}

Expand Down Expand Up @@ -1075,6 +1083,18 @@ let maxRetryDelay = 30.0
selector: #selector(handleAppDidBecomeActive),
name: NSApplication.didBecomeActiveNotification,
object: nil)
#elseif os(watchOS)
if #available(watchOS 7.0, *) {
NotificationCenter.default.addObserver(self,
selector: #selector(handleAppDidBecomeActive),
name: WKApplication.didBecomeActiveNotification,
object: nil)
} else {
NotificationCenter.default.addObserver(self,
selector: #selector(handleAppDidBecomeActive),
name: .init("UIApplicationDidBecomeActiveNotification"),
object: nil)
}
#endif
}

Expand Down

0 comments on commit 7f4cae4

Please sign in to comment.