From e9fc06e54221c96d6682b3a33ac734c5ff3ec1b8 Mon Sep 17 00:00:00 2001 From: Noah Martin Date: Wed, 13 Aug 2025 14:22:08 -0700 Subject: [PATCH 1/2] ref: Convert SentryUIDevice to Swift --- Sentry.xcodeproj/project.pbxproj | 12 +-- .../SentryTestUtils-ObjC-BridgingHeader.h | 1 - Sources/Sentry/SentryCrashWrapper.m | 2 +- Sources/Sentry/SentryDependencyContainer.m | 7 +- Sources/Sentry/SentryExtraContextProvider.h | 8 +- Sources/Sentry/SentryExtraContextProvider.m | 6 +- Sources/Sentry/SentrySDKInternal.m | 1 - Sources/Sentry/SentryUIDeviceWrapper.m | 91 ------------------ .../HybridPublic/SentryDependencyContainer.h | 4 +- .../Sentry/include/SentryUIDeviceWrapper.h | 27 ------ .../Core/Helper/SentryUIDeviceWrapper.swift | 96 +++++++++++++++++++ .../SentryUIDeviceWrapperTests.swift | 4 +- .../TestSentryUIDeviceWrapper.swift | 17 ++-- .../SentryTests/SentryTests-Bridging-Header.h | 1 - 14 files changed, 122 insertions(+), 155 deletions(-) delete mode 100644 Sources/Sentry/SentryUIDeviceWrapper.m delete mode 100644 Sources/Sentry/include/SentryUIDeviceWrapper.h create mode 100644 Sources/Swift/Core/Helper/SentryUIDeviceWrapper.swift diff --git a/Sentry.xcodeproj/project.pbxproj b/Sentry.xcodeproj/project.pbxproj index f124fe8a7d3..db529ccecc8 100644 --- a/Sentry.xcodeproj/project.pbxproj +++ b/Sentry.xcodeproj/project.pbxproj @@ -50,8 +50,6 @@ 0A9E917128DC7E7000FB4182 /* SentryInternalCDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = 0A9E917028DC7E7000FB4182 /* SentryInternalCDefines.h */; }; 0AAE201E28ED9B9400D0CD80 /* SentryReachability.m in Sources */ = {isa = PBXBuildFile; fileRef = 0AAE201D28ED9B9400D0CD80 /* SentryReachability.m */; }; 0AAE202128ED9BCC00D0CD80 /* SentryReachability.h in Headers */ = {isa = PBXBuildFile; fileRef = 0AAE202028ED9BCC00D0CD80 /* SentryReachability.h */; }; - 0ADC33EC28D9BB780078D980 /* SentryUIDeviceWrapper.m in Sources */ = {isa = PBXBuildFile; fileRef = 0ADC33EB28D9BB780078D980 /* SentryUIDeviceWrapper.m */; }; - 0ADC33EE28D9BB890078D980 /* SentryUIDeviceWrapper.h in Headers */ = {isa = PBXBuildFile; fileRef = 0ADC33ED28D9BB890078D980 /* SentryUIDeviceWrapper.h */; }; 0ADC33F128D9BE940078D980 /* TestSentryUIDeviceWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ADC33EF28D9BE690078D980 /* TestSentryUIDeviceWrapper.swift */; }; 0AE455AD28F584D2006680E5 /* SentryReachabilityTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 0AE455AC28F584D2006680E5 /* SentryReachabilityTests.m */; }; 15360CCF2432777500112302 /* SentrySessionTracker.m in Sources */ = {isa = PBXBuildFile; fileRef = 15360CCE2432777400112302 /* SentrySessionTracker.m */; }; @@ -1111,6 +1109,7 @@ FA8E58F12E0AD4270049F69D /* SentryDispatchQueueWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA8E58F02E0AD4220049F69D /* SentryDispatchQueueWrapper.swift */; }; FA90FAA82E06614E008CAAE8 /* SentryExtraPackages.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA90FAA72E06614B008CAAE8 /* SentryExtraPackages.swift */; }; FA90FAFD2E070A3B008CAAE8 /* SentryURLRequestFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA90FAFC2E070A3B008CAAE8 /* SentryURLRequestFactory.swift */; }; + FAAB2F972E4D345800FE8B7E /* SentryUIDeviceWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAAB2F962E4D344F00FE8B7E /* SentryUIDeviceWrapper.swift */; }; FAAB30F32E4E8F2C00FE8B7E /* SentryInAppLogic.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAAB30F22E4E8F2C00FE8B7E /* SentryInAppLogic.swift */; }; FAB359982E05D7E90083D5E3 /* SentryEventSwiftHelper.h in Headers */ = {isa = PBXBuildFile; fileRef = FAB359972E05D7E90083D5E3 /* SentryEventSwiftHelper.h */; }; FAB3599A2E05D8080083D5E3 /* SentryEventSwiftHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = FAB359992E05D8080083D5E3 /* SentryEventSwiftHelper.m */; }; @@ -1288,8 +1287,6 @@ 0A9E917028DC7E7000FB4182 /* SentryInternalCDefines.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryInternalCDefines.h; path = include/SentryInternalCDefines.h; sourceTree = ""; }; 0AAE201D28ED9B9400D0CD80 /* SentryReachability.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryReachability.m; sourceTree = ""; }; 0AAE202028ED9BCC00D0CD80 /* SentryReachability.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryReachability.h; path = include/SentryReachability.h; sourceTree = ""; }; - 0ADC33EB28D9BB780078D980 /* SentryUIDeviceWrapper.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryUIDeviceWrapper.m; sourceTree = ""; }; - 0ADC33ED28D9BB890078D980 /* SentryUIDeviceWrapper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryUIDeviceWrapper.h; path = include/SentryUIDeviceWrapper.h; sourceTree = ""; }; 0ADC33EF28D9BE690078D980 /* TestSentryUIDeviceWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestSentryUIDeviceWrapper.swift; sourceTree = ""; }; 0AE455AC28F584D2006680E5 /* SentryReachabilityTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryReachabilityTests.m; sourceTree = ""; }; 15360CCE2432777400112302 /* SentrySessionTracker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SentrySessionTracker.m; sourceTree = ""; }; @@ -2460,6 +2457,7 @@ FA8E58F02E0AD4220049F69D /* SentryDispatchQueueWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryDispatchQueueWrapper.swift; sourceTree = ""; }; FA90FAA72E06614B008CAAE8 /* SentryExtraPackages.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryExtraPackages.swift; sourceTree = ""; }; FA90FAFC2E070A3B008CAAE8 /* SentryURLRequestFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryURLRequestFactory.swift; sourceTree = ""; }; + FAAB2F962E4D344F00FE8B7E /* SentryUIDeviceWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryUIDeviceWrapper.swift; sourceTree = ""; }; FAAB30F22E4E8F2C00FE8B7E /* SentryInAppLogic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryInAppLogic.swift; sourceTree = ""; }; FAB359972E05D7E90083D5E3 /* SentryEventSwiftHelper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SentryEventSwiftHelper.h; path = include/SentryEventSwiftHelper.h; sourceTree = ""; }; FAB359992E05D8080083D5E3 /* SentryEventSwiftHelper.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SentryEventSwiftHelper.m; sourceTree = ""; }; @@ -2850,8 +2848,6 @@ 7B7D873524864C9D00D2ECFF /* SentryCrashDefaultMachineContextWrapper.m */, 7B7A30C524B48321005A4C6E /* SentryCrashWrapper.h */, 7B7A30C724B48389005A4C6E /* SentryCrashWrapper.m */, - 0ADC33ED28D9BB890078D980 /* SentryUIDeviceWrapper.h */, - 0ADC33EB28D9BB780078D980 /* SentryUIDeviceWrapper.m */, 62C97D372CC64E4900DDA204 /* SentryUncaughtNSExceptions.h */, 62C97D392CC64E6B00DDA204 /* SentryUncaughtNSExceptions.m */, ); @@ -4770,6 +4766,7 @@ FA67DCCC2DDBD4EA00896B02 /* Helper */ = { isa = PBXGroup; children = ( + FAAB2F962E4D344F00FE8B7E /* SentryUIDeviceWrapper.swift */, FA90FAA72E06614B008CAAE8 /* SentryExtraPackages.swift */, FA67DCC82DDBD4EA00896B02 /* Log */, FA67DCC92DDBD4EA00896B02 /* SentryBaggageSerialization.swift */, @@ -4894,7 +4891,6 @@ 92E5F3D62CDBB3BF00B7AD98 /* SentrySampling.h in Headers */, 92672BB629C9A2A9006B021C /* SentryBreadcrumb+Private.h in Headers */, 03BCC38E27E2A377003232C7 /* SentryProfilingConditionals.h in Headers */, - 0ADC33EE28D9BB890078D980 /* SentryUIDeviceWrapper.h in Headers */, 848A451A2BBF8D33006AAAEC /* SentryContinuousProfiler.h in Headers */, 8E133FA625E72EB400ABD0BF /* SentrySamplingContext.h in Headers */, 0A9BF4E428A114B50068D266 /* SentryViewHierarchyIntegration.h in Headers */, @@ -5597,6 +5593,7 @@ 7BDB03BB2513652900BAE198 /* _SentryDispatchQueueWrapperInternal.m in Sources */, FA6FC0A32E0B5ACE00ED2669 /* SentrySdkPackage.swift in Sources */, D8739D142BEE5049007D2F66 /* SentryRRWebSpanEvent.swift in Sources */, + FAAB2F972E4D345800FE8B7E /* SentryUIDeviceWrapper.swift in Sources */, 7B6C5EDE264E8DF00010D138 /* SentryFramesTracker.m in Sources */, D84F833E2A1CC401005828E0 /* SentrySwiftAsyncIntegration.m in Sources */, 7B6438AB26A70F24000D0F65 /* UIViewController+Sentry.m in Sources */, @@ -5723,7 +5720,6 @@ 63FE70D720DA4C1000CDBAE8 /* SentryCrashMonitor_MachException.c in Sources */, 7B96572226830D2400C66E25 /* SentryScopeSyncC.c in Sources */, 0A9BF4E228A114940068D266 /* SentryViewHierarchyIntegration.m in Sources */, - 0ADC33EC28D9BB780078D980 /* SentryUIDeviceWrapper.m in Sources */, 7BBD188B244841FB00427C76 /* SentryHttpDateParser.m in Sources */, D8AFC03D2BDA79BF00118BE1 /* SentryReplayVideoMaker.swift in Sources */, 840A11122B61E27500650D02 /* SentrySamplerDecision.m in Sources */, diff --git a/SentryTestUtils/SentryTestUtils-ObjC-BridgingHeader.h b/SentryTestUtils/SentryTestUtils-ObjC-BridgingHeader.h index 41c12603870..e95a5852a17 100644 --- a/SentryTestUtils/SentryTestUtils-ObjC-BridgingHeader.h +++ b/SentryTestUtils/SentryTestUtils-ObjC-BridgingHeader.h @@ -10,7 +10,6 @@ # import "SentryAppStartTracker.h" # import "SentryDisplayLinkWrapper.h" # import "SentryFramesTracker+TestInit.h" -# import "SentryUIDeviceWrapper.h" # import "SentryUIViewControllerPerformanceTracker.h" #endif // SENTRY_HAS_UIKIT diff --git a/Sources/Sentry/SentryCrashWrapper.m b/Sources/Sentry/SentryCrashWrapper.m index bc66247a7cd..ad67c085723 100644 --- a/Sources/Sentry/SentryCrashWrapper.m +++ b/Sources/Sentry/SentryCrashWrapper.m @@ -7,7 +7,7 @@ #import "SentryNSProcessInfoWrapper.h" #import "SentryScope+PrivateSwift.h" #import "SentryScope.h" -#import "SentryUIDeviceWrapper.h" +#import "SentrySwift.h" #import #import #import diff --git a/Sources/Sentry/SentryDependencyContainer.m b/Sources/Sentry/SentryDependencyContainer.m index 69a50a0a652..8d0f273777a 100644 --- a/Sources/Sentry/SentryDependencyContainer.m +++ b/Sources/Sentry/SentryDependencyContainer.m @@ -20,7 +20,6 @@ #import "SentrySwift.h" #import "SentrySystemWrapper.h" #import "SentryThreadInspector.h" -#import "SentryUIDeviceWrapper.h" #import #import #import @@ -49,10 +48,6 @@ # import #endif // SENTRY_HAS_UIKIT -#if TARGET_OS_IOS -# import "SentryUIDeviceWrapper.h" -#endif // TARGET_OS_IOS - #if TARGET_OS_OSX # import "SentryNSApplication.h" #endif @@ -168,7 +163,7 @@ - (instancetype)init _notificationCenterWrapper = [NSNotificationCenter defaultCenter]; #if SENTRY_HAS_UIKIT - _uiDeviceWrapper = [[SentryUIDeviceWrapper alloc] init]; + _uiDeviceWrapper = [[DefaultSentryUIDeviceWrapper alloc] init]; _application = [[SentryUIApplication alloc] initWithNotificationCenterWrapper:_notificationCenterWrapper dispatchQueueWrapper:_dispatchQueueWrapper]; diff --git a/Sources/Sentry/SentryExtraContextProvider.h b/Sources/Sentry/SentryExtraContextProvider.h index a8db1b3c11d..7304332de01 100644 --- a/Sources/Sentry/SentryExtraContextProvider.h +++ b/Sources/Sentry/SentryExtraContextProvider.h @@ -6,11 +6,7 @@ @class SentryCrashWrapper; @class SentryNSProcessInfoWrapper; -@class SentryUIDeviceWrapper; - -#if TARGET_OS_IOS && SENTRY_HAS_UIKIT -@class SentryUIDeviceWrapper; -#endif // TARGET_OS_IOS && SENTRY_HAS_UIKIT +@protocol SentryUIDeviceWrapper; NS_ASSUME_NONNULL_BEGIN @@ -23,7 +19,7 @@ SENTRY_NO_INIT - (instancetype)initWithCrashWrapper:(SentryCrashWrapper *)crashWrapper processInfoWrapper:(SentryNSProcessInfoWrapper *)processInfoWrapper #if TARGET_OS_IOS && SENTRY_HAS_UIKIT - deviceWrapper:(SentryUIDeviceWrapper *)deviceWrapper + deviceWrapper:(id)deviceWrapper #endif // TARGET_OS_IOS && SENTRY_HAS_UIKIT ; diff --git a/Sources/Sentry/SentryExtraContextProvider.m b/Sources/Sentry/SentryExtraContextProvider.m index 726e38874fa..06d7ddaa681 100644 --- a/Sources/Sentry/SentryExtraContextProvider.m +++ b/Sources/Sentry/SentryExtraContextProvider.m @@ -3,7 +3,7 @@ #import "SentryCrashWrapper.h" #import "SentryLogC.h" #import "SentryNSProcessInfoWrapper.h" -#import "SentryUIDeviceWrapper.h" +#import "SentrySwift.h" NSString *const kSentryProcessInfoThermalStateNominal = @"nominal"; NSString *const kSentryProcessInfoThermalStateFair = @"fair"; @@ -16,7 +16,7 @@ @interface SentryExtraContextProvider () @property (nonatomic, strong) SentryNSProcessInfoWrapper *processInfoWrapper; #if TARGET_OS_IOS && SENTRY_HAS_UIKIT -@property (nonatomic, strong) SentryUIDeviceWrapper *deviceWrapper; +@property (nonatomic, strong) id deviceWrapper; #endif // TARGET_OS_IOS && SENTRY_HAS_UIKIT @end @@ -26,7 +26,7 @@ @implementation SentryExtraContextProvider - (instancetype)initWithCrashWrapper:(id)crashWrapper processInfoWrapper:(id)processInfoWrapper #if TARGET_OS_IOS && SENTRY_HAS_UIKIT - deviceWrapper:(SentryUIDeviceWrapper *)deviceWrapper + deviceWrapper:(id)deviceWrapper #endif // TARGET_OS_IOS && SENTRY_HAS_UIKIT { if (self = [super init]) { diff --git a/Sources/Sentry/SentrySDKInternal.m b/Sources/Sentry/SentrySDKInternal.m index 926309679c5..f8b842c9d25 100644 --- a/Sources/Sentry/SentrySDKInternal.m +++ b/Sources/Sentry/SentrySDKInternal.m @@ -34,7 +34,6 @@ #endif // TARGET_OS_MAC #if SENTRY_HAS_UIKIT -# import "SentryUIDeviceWrapper.h" # import "SentryUIViewControllerPerformanceTracker.h" # if TARGET_OS_IOS # import "SentryFeedbackAPI.h" diff --git a/Sources/Sentry/SentryUIDeviceWrapper.m b/Sources/Sentry/SentryUIDeviceWrapper.m deleted file mode 100644 index 62192c1e575..00000000000 --- a/Sources/Sentry/SentryUIDeviceWrapper.m +++ /dev/null @@ -1,91 +0,0 @@ -#import "SentryUIDeviceWrapper.h" -#import "SentryDependencyContainer.h" -#import "SentrySwift.h" - -#if SENTRY_HAS_UIKIT - -NS_ASSUME_NONNULL_BEGIN - -@interface SentryUIDeviceWrapper () -@property (nonatomic) BOOL cleanupDeviceOrientationNotifications; -@property (nonatomic) BOOL cleanupBatteryMonitoring; -@property (nonatomic, copy) NSString *systemVersion; -@end - -@implementation SentryUIDeviceWrapper - -- (void)start -{ - [SentryDependencyContainer.sharedInstance.dispatchQueueWrapper dispatchAsyncOnMainQueue:^{ - -# if TARGET_OS_IOS - if (!UIDevice.currentDevice.isGeneratingDeviceOrientationNotifications) { - self.cleanupDeviceOrientationNotifications = YES; - [UIDevice.currentDevice beginGeneratingDeviceOrientationNotifications]; - } - - // Needed so we can read the battery level - if (!UIDevice.currentDevice.isBatteryMonitoringEnabled) { - self.cleanupBatteryMonitoring = YES; - UIDevice.currentDevice.batteryMonitoringEnabled = YES; - } -# endif - - self.systemVersion = [UIDevice currentDevice].systemVersion; - }]; -} - -- (void)stop -{ -# if TARGET_OS_IOS - BOOL needsCleanUp = self.cleanupDeviceOrientationNotifications; - BOOL needsDisablingBattery = self.cleanupBatteryMonitoring; - UIDevice *device = [UIDevice currentDevice]; - [SentryDependencyContainer.sharedInstance.dispatchQueueWrapper dispatchAsyncOnMainQueue:^{ - if (needsCleanUp) { - [device endGeneratingDeviceOrientationNotifications]; - } - if (needsDisablingBattery) { - device.batteryMonitoringEnabled = NO; - } - }]; -# endif // TARGET_OS_IOS -} - -- (void)dealloc -{ - [self stop]; -} - -# if TARGET_OS_IOS -- (UIDeviceOrientation)orientation -{ - return (UIDeviceOrientation)[UIDevice currentDevice].orientation; -} - -- (BOOL)isBatteryMonitoringEnabled -{ - return [UIDevice currentDevice].isBatteryMonitoringEnabled; -} - -- (UIDeviceBatteryState)batteryState -{ - return (UIDeviceBatteryState)[UIDevice currentDevice].batteryState; -} - -- (float)batteryLevel -{ - return [UIDevice currentDevice].batteryLevel; -} -# endif // TARGET_OS_IOS - -- (NSString *)getSystemVersion -{ - return self.systemVersion; -} - -@end - -NS_ASSUME_NONNULL_END - -#endif // SENTRY_HAS_UIKIT diff --git a/Sources/Sentry/include/HybridPublic/SentryDependencyContainer.h b/Sources/Sentry/include/HybridPublic/SentryDependencyContainer.h index e199c2869ca..37a699a31dc 100644 --- a/Sources/Sentry/include/HybridPublic/SentryDependencyContainer.h +++ b/Sources/Sentry/include/HybridPublic/SentryDependencyContainer.h @@ -51,7 +51,7 @@ #endif // SENTRY_UIKIT_AVAILABLE #if SENTRY_HAS_UIKIT -@class SentryUIDeviceWrapper; +@protocol SentryUIDeviceWrapper; #endif // TARGET_OS_IOS #if !TARGET_OS_WATCH @@ -100,7 +100,7 @@ SENTRY_NO_INIT #endif // !TARGET_OS_WATCH #if SENTRY_HAS_UIKIT -@property (nonatomic, strong) SentryUIDeviceWrapper *uiDeviceWrapper; +@property (nonatomic, strong) id uiDeviceWrapper; #endif // TARGET_OS_IOS #pragma mark - Lazy Dependencies diff --git a/Sources/Sentry/include/SentryUIDeviceWrapper.h b/Sources/Sentry/include/SentryUIDeviceWrapper.h deleted file mode 100644 index 5dca34d5587..00000000000 --- a/Sources/Sentry/include/SentryUIDeviceWrapper.h +++ /dev/null @@ -1,27 +0,0 @@ -#import "SentryDefines.h" - -#if SENTRY_HAS_UIKIT - -# import - -NS_ASSUME_NONNULL_BEGIN - -@interface SentryUIDeviceWrapper : NSObject - -- (void)start; -- (void)stop; - -# if TARGET_OS_IOS -- (UIDeviceOrientation)orientation; -- (BOOL)isBatteryMonitoringEnabled; -- (UIDeviceBatteryState)batteryState; -- (float)batteryLevel; -# endif // TARGET_OS_IOS - -- (NSString *)getSystemVersion; - -@end - -NS_ASSUME_NONNULL_END - -#endif // SENTRY_HAS_UIKIT diff --git a/Sources/Swift/Core/Helper/SentryUIDeviceWrapper.swift b/Sources/Swift/Core/Helper/SentryUIDeviceWrapper.swift new file mode 100644 index 00000000000..bce3ed06e2e --- /dev/null +++ b/Sources/Swift/Core/Helper/SentryUIDeviceWrapper.swift @@ -0,0 +1,96 @@ +#if !os(watchOS) && !os(macOS) && !SENTRY_NO_UIKIT +import UIKit + +@_spi(Private) @objc public protocol SentryUIDeviceWrapper { + func start() + func stop() + func getSystemVersion() -> String + +#if os(iOS) + var orientation: UIDeviceOrientation { get } + var isBatteryMonitoringEnabled: Bool { get } + var batteryState: UIDevice.BatteryState { get } + var batteryLevel: Float { get } +#endif +} + +@_spi(Private) @objc public final class DefaultSentryUIDeviceWrapper: NSObject, SentryUIDeviceWrapper { + + let queueWrapper: SentryDispatchQueueWrapper + + override convenience init() { + self.init(queueWrapper: Dependencies.dispatchQueueWrapper) + } + + init(queueWrapper: SentryDispatchQueueWrapper) { + self.queueWrapper = queueWrapper + } + + @objc public func start() { + queueWrapper.dispatchAsyncOnMainQueue { +#if os(iOS) + if !UIDevice.current.isGeneratingDeviceOrientationNotifications { + self.cleanupDeviceOrientationNotifications = true + UIDevice.current.beginGeneratingDeviceOrientationNotifications() + } + + // Needed so we can read the battery level + if !UIDevice.current.isBatteryMonitoringEnabled { + self.cleanupBatteryMonitoring = true + UIDevice.current.isBatteryMonitoringEnabled = true + } +#endif + + self.systemVersion = UIDevice.current.systemVersion + } + } + + @objc public func stop() { + #if os(iOS) + let needsCleanup = self.cleanupDeviceOrientationNotifications + let needsDisablingBattery = self.cleanupBatteryMonitoring + let device = UIDevice.current + queueWrapper.dispatchAsyncOnMainQueue { + if needsCleanup { + device.endGeneratingDeviceOrientationNotifications() + } + if needsDisablingBattery { + device.isBatteryMonitoringEnabled = false + } + } + #endif + } + + func dealloc() { + stop() + } + + #if os(iOS) + @objc public var orientation: UIDeviceOrientation { + UIDevice.current.orientation + } + + @objc public var isBatteryMonitoringEnabled: Bool { + UIDevice.current.isBatteryMonitoringEnabled + } + + @objc public var batteryState: UIDevice.BatteryState { + UIDevice.current.batteryState + } + + @objc public var batteryLevel: Float { + UIDevice.current.batteryLevel + } + + #endif // os(iOS) + + @objc public func getSystemVersion() -> String { + systemVersion + } + + private var systemVersion = "" + private var cleanupBatteryMonitoring = false + private var cleanupDeviceOrientationNotifications = false + +} +#endif diff --git a/Tests/SentryTests/SentryCrash/SentryUIDeviceWrapperTests.swift b/Tests/SentryTests/SentryCrash/SentryUIDeviceWrapperTests.swift index 1aaf1b2ea29..2d4b9330f1a 100644 --- a/Tests/SentryTests/SentryCrash/SentryUIDeviceWrapperTests.swift +++ b/Tests/SentryTests/SentryCrash/SentryUIDeviceWrapperTests.swift @@ -1,3 +1,4 @@ +@_spi(Private) @testable import Sentry @_spi(Private) import SentryTestUtils import XCTest @@ -10,8 +11,7 @@ class SentryUIDeviceWrapperTests: XCTestCase { func testExecutesLogicViaDispatchQueue() { let dispatchQueue = TestSentryDispatchQueueWrapper() - SentryDependencyContainer.sharedInstance().dispatchQueueWrapper = dispatchQueue - let sut = SentryUIDeviceWrapper() + let sut = DefaultSentryUIDeviceWrapper(queueWrapper: dispatchQueue) sut.start() XCTAssertEqual(dispatchQueue.blockOnMainInvocations.count, 1) diff --git a/Tests/SentryTests/SentryCrash/TestSentryUIDeviceWrapper.swift b/Tests/SentryTests/SentryCrash/TestSentryUIDeviceWrapper.swift index d834a593299..c80908cfbb1 100644 --- a/Tests/SentryTests/SentryCrash/TestSentryUIDeviceWrapper.swift +++ b/Tests/SentryTests/SentryCrash/TestSentryUIDeviceWrapper.swift @@ -2,33 +2,38 @@ import Sentry #if os(iOS) || targetEnvironment(macCatalyst) class TestSentryUIDeviceWrapper: SentryUIDeviceWrapper { + var internalOrientation = UIDeviceOrientation.portrait var internalIsBatteryMonitoringEnabled = true var internalBatteryLevel: Float = 0.6 var internalBatteryState = UIDevice.BatteryState.charging var started = false - override func start() { + func start() { started = true } - override func stop() { + func stop() { started = false } - override func orientation() -> UIDeviceOrientation { + var orientation: UIDeviceOrientation { return internalOrientation } - override func isBatteryMonitoringEnabled() -> Bool { + var isBatteryMonitoringEnabled: Bool { return internalIsBatteryMonitoringEnabled } - override func batteryLevel() -> Float { + var batteryLevel: Float { return internalBatteryLevel } - override func batteryState() -> UIDevice.BatteryState { + var batteryState: UIDevice.BatteryState { return internalBatteryState } + + func getSystemVersion() -> String { + "" + } } #endif // os(iOS) || targetEnvironment(macCatalyst) diff --git a/Tests/SentryTests/SentryTests-Bridging-Header.h b/Tests/SentryTests/SentryTests-Bridging-Header.h index 720a5e74ca6..5c0f4eb0d92 100644 --- a/Tests/SentryTests/SentryTests-Bridging-Header.h +++ b/Tests/SentryTests/SentryTests-Bridging-Header.h @@ -16,7 +16,6 @@ # import "SentrySessionReplayIntegration+Test.h" # import "SentryUIApplication+Private.h" # import "SentryUIApplication.h" -# import "SentryUIDeviceWrapper.h" # import "SentryUIEventTracker.h" # import "SentryUIEventTrackerTransactionMode.h" # import "SentryUIEventTrackingIntegration.h" From 8333e1ddcf8568e12c8387ed514b44a236e5898f Mon Sep 17 00:00:00 2001 From: Noah Martin Date: Thu, 14 Aug 2025 09:55:05 -0700 Subject: [PATCH 2/2] PR feedback --- Sources/Sentry/SentryDependencyContainer.m | 3 +- .../Core/Helper/SentryUIDeviceWrapper.swift | 25 +++++++++------- .../SentryUIDeviceWrapperTests.swift | 30 ++++++++++++++++++- 3 files changed, 45 insertions(+), 13 deletions(-) diff --git a/Sources/Sentry/SentryDependencyContainer.m b/Sources/Sentry/SentryDependencyContainer.m index 8d0f273777a..cfd4c100c3a 100644 --- a/Sources/Sentry/SentryDependencyContainer.m +++ b/Sources/Sentry/SentryDependencyContainer.m @@ -163,7 +163,8 @@ - (instancetype)init _notificationCenterWrapper = [NSNotificationCenter defaultCenter]; #if SENTRY_HAS_UIKIT - _uiDeviceWrapper = [[DefaultSentryUIDeviceWrapper alloc] init]; + _uiDeviceWrapper = + [[SentryDefaultUIDeviceWrapper alloc] initWithQueueWrapper:_dispatchQueueWrapper]; _application = [[SentryUIApplication alloc] initWithNotificationCenterWrapper:_notificationCenterWrapper dispatchQueueWrapper:_dispatchQueueWrapper]; diff --git a/Sources/Swift/Core/Helper/SentryUIDeviceWrapper.swift b/Sources/Swift/Core/Helper/SentryUIDeviceWrapper.swift index bce3ed06e2e..690d2192236 100644 --- a/Sources/Swift/Core/Helper/SentryUIDeviceWrapper.swift +++ b/Sources/Swift/Core/Helper/SentryUIDeviceWrapper.swift @@ -14,20 +14,27 @@ import UIKit #endif } -@_spi(Private) @objc public final class DefaultSentryUIDeviceWrapper: NSObject, SentryUIDeviceWrapper { +@_spi(Private) @objc public final class SentryDefaultUIDeviceWrapper: NSObject, SentryUIDeviceWrapper { - let queueWrapper: SentryDispatchQueueWrapper + private let queueWrapper: SentryDispatchQueueWrapper + private var systemVersion = "" + private var cleanupBatteryMonitoring = false + private var cleanupDeviceOrientationNotifications = false + // This one shouldn't be used because it acceccess `Dependencies` directly rather than being + // initialized with dependencies, but since we need to sublcass NSObject it has to be here. + @available(*, unavailable) override convenience init() { self.init(queueWrapper: Dependencies.dispatchQueueWrapper) } - - init(queueWrapper: SentryDispatchQueueWrapper) { + + @objc public init(queueWrapper: SentryDispatchQueueWrapper) { self.queueWrapper = queueWrapper } - + @objc public func start() { - queueWrapper.dispatchAsyncOnMainQueue { + queueWrapper.dispatchAsyncOnMainQueue { [weak self] in + guard let self else { return } #if os(iOS) if !UIDevice.current.isGeneratingDeviceOrientationNotifications { self.cleanupDeviceOrientationNotifications = true @@ -61,7 +68,7 @@ import UIKit #endif } - func dealloc() { + deinit { stop() } @@ -88,9 +95,5 @@ import UIKit systemVersion } - private var systemVersion = "" - private var cleanupBatteryMonitoring = false - private var cleanupDeviceOrientationNotifications = false - } #endif diff --git a/Tests/SentryTests/SentryCrash/SentryUIDeviceWrapperTests.swift b/Tests/SentryTests/SentryCrash/SentryUIDeviceWrapperTests.swift index 2d4b9330f1a..fdb841ecc2e 100644 --- a/Tests/SentryTests/SentryCrash/SentryUIDeviceWrapperTests.swift +++ b/Tests/SentryTests/SentryCrash/SentryUIDeviceWrapperTests.swift @@ -11,12 +11,40 @@ class SentryUIDeviceWrapperTests: XCTestCase { func testExecutesLogicViaDispatchQueue() { let dispatchQueue = TestSentryDispatchQueueWrapper() - let sut = DefaultSentryUIDeviceWrapper(queueWrapper: dispatchQueue) + let sut = SentryDefaultUIDeviceWrapper(queueWrapper: dispatchQueue) sut.start() XCTAssertEqual(dispatchQueue.blockOnMainInvocations.count, 1) sut.stop() XCTAssertEqual(dispatchQueue.blockOnMainInvocations.count, 2) } + + func testGetsSystemVersion() { + let dispatchQueue = TestSentryDispatchQueueWrapper() + let sut = SentryDefaultUIDeviceWrapper(queueWrapper: dispatchQueue) + XCTAssertNotNil(sut.getSystemVersion()) + } + + func testBatteryLevel() { + let dispatchQueue = TestSentryDispatchQueueWrapper() + let sut = SentryDefaultUIDeviceWrapper(queueWrapper: dispatchQueue) + XCTAssertNotNil(sut.batteryLevel) + } + + func testBatteryState() { + let dispatchQueue = TestSentryDispatchQueueWrapper() + let sut = SentryDefaultUIDeviceWrapper(queueWrapper: dispatchQueue) + XCTAssertNotNil(sut.batteryState) + } + + func testCleansUp() { + let dispatchQueue = TestSentryDispatchQueueWrapper() + autoreleasepool { + let sut = SentryDefaultUIDeviceWrapper(queueWrapper: dispatchQueue) + sut.start() + XCTAssertEqual(dispatchQueue.blockOnMainInvocations.count, 1) + } + XCTAssertEqual(dispatchQueue.blockOnMainInvocations.count, 2) + } } #endif