From db7aa9eeafb28a0d91788fac91b23bcc9fb0f439 Mon Sep 17 00:00:00 2001 From: Lorenzo Blasa Date: Fri, 21 Jul 2023 04:47:13 -0700 Subject: [PATCH] OSS Summary: Move UIDebugger plugin to OSS space. Reviewed By: passy Differential Revision: D47634848 fbshipit-source-id: 90e8c0181a2434d0e5d76bdb99b902051e6d702e --- .../Analytics/UIDFrameworkEventManager.h | 30 + .../UIDSerialFrameworkEventManager.h | 21 + .../UIDSerialFrameworkEventManager.m | 75 +++ .../Core/UIDContext.h | 40 ++ .../Core/UIDContext.mm | 33 + .../Core/UIDUpdate.h | 31 + .../Core/UIDUpdate.m | 15 + .../Core/UIDUpdateDigester.h | 23 + .../Descriptors/UIDChainedDescriptor.h | 52 ++ .../Descriptors/UIDChainedDescriptor.m | 87 +++ .../Descriptors/UIDDescriptor.h | 69 ++ .../Descriptors/UIDDescriptorRegister.h | 26 + .../Descriptors/UIDDescriptorRegister.m | 103 +++ .../Descriptors/UIDMetadataRegister.h | 43 ++ .../Descriptors/UIDMetadataRegister.m | 126 ++++ .../Descriptors/UIDNodeDescriptor.h | 72 +++ .../Descriptors/UIDNodeDescriptor.m | 56 ++ .../Descriptors/UIDUIApplicationDescriptor.h | 21 + .../Descriptors/UIDUIApplicationDescriptor.mm | 50 ++ .../Descriptors/UIDUILabelDescriptor.h | 21 + .../Descriptors/UIDUILabelDescriptor.m | 248 +++++++ .../UIDUINavigationControllerDescriptor.h | 34 + .../UIDUINavigationControllerDescriptor.m | 28 + .../UIDUITabBarControllerDescriptor.h | 31 + .../UIDUITabBarControllerDescriptor.m | 28 + .../UIDUIViewControllerDescriptor.h | 21 + .../UIDUIViewControllerDescriptor.m | 30 + .../Descriptors/UIDUIViewDescriptor.h | 21 + .../Descriptors/UIDUIViewDescriptor.m | 34 + .../Descriptors/UIDUIWindowDescriptor.h | 21 + .../Descriptors/UIDUIWindowDescriptor.m | 25 + .../Descriptors/UIView+UIDDescriptor.h | 23 + .../Descriptors/UIView+UIDDescriptor.m | 606 ++++++++++++++++++ .../Events/UIDInitEvent.h | 28 + .../Events/UIDInitEvent.m | 20 + .../Events/UIDMetadataUpdateEvent.h | 27 + .../Events/UIDMetadataUpdateEvent.m | 20 + .../Events/UIDPerfStatsEvent.h | 38 ++ .../Events/UIDPerfStatsEvent.m | 20 + .../Events/UIDSubtreeUpdateEvent.h | 31 + .../Events/UIDSubtreeUpdateEvent.m | 20 + .../FlipperKitUIDebuggerPlugin.h | 25 + .../FlipperKitUIDebuggerPlugin.mm | 85 +++ .../Model/UIDBounds.h | 32 + .../Model/UIDBounds.m | 37 ++ .../Model/UIDEdgeInsets.h | 32 + .../Model/UIDEdgeInsets.m | 37 ++ .../Model/UIDFrameworkEvent.h | 29 + .../Model/UIDFrameworkEvent.m | 16 + .../Model/UIDFrameworkEventMetadata.h | 30 + .../Model/UIDFrameworkEventMetadata.m | 30 + .../Model/UIDInspectable.h | 175 +++++ .../Model/UIDInspectable.m | 275 ++++++++ .../Model/UIDMetadata.h | 56 ++ .../Model/UIDMetadata.m | 63 ++ .../Model/UIDNode.h | 41 ++ .../Model/UIDNode.mm | 36 ++ .../Observer/UIDTreeObserver.h | 34 + .../Observer/UIDTreeObserver.m | 75 +++ .../Observer/UIDTreeObserverBuilder.h | 28 + .../Observer/UIDTreeObserverFactory.h | 34 + .../Observer/UIDTreeObserverFactory.m | 66 ++ .../Observer/UIDTreeObserverManager.h | 27 + .../Observer/UIDTreeObserverManager.m | 167 +++++ .../Observer/UIDUIApplicationObserver.h | 25 + .../Observer/UIDUIApplicationObserver.m | 121 ++++ .../Observer/UIDUIKitObserver.h | 42 ++ .../Observer/UIDUIKitObserver.m | 39 ++ .../Observer/UIDUIViewBasicObserver.h | 27 + .../Observer/UIDUIViewBasicObserver.m | 63 ++ .../Observer/UIDUIViewObserverDelegate.h | 16 + .../Observer/UIView+Observer.h | 25 + .../Observer/UIView+Observer.m | 158 +++++ .../Serializers/NSArray+Foundation.h | 23 + .../Serializers/NSArray+Foundation.m | 25 + .../Serializers/NSDictionary+Foundation.h | 23 + .../Serializers/NSDictionary+Foundation.m | 28 + .../Serializers/NSNull+Foundation.h | 23 + .../Serializers/NSNull+Foundation.m | 21 + .../Serializers/NSSet+Foundation.h | 23 + .../Serializers/NSSet+Foundation.m | 25 + .../Serializers/NSString+Foundation.h | 23 + .../Serializers/NSString+Foundation.m | 21 + .../Serializers/NSValue+Foundation.h | 23 + .../Serializers/NSValue+Foundation.m | 21 + .../Serializers/UIDBounds+Foundation.h | 24 + .../Serializers/UIDBounds+Foundation.m | 26 + .../Serializers/UIDEdgeInsets+Foundation.h | 24 + .../Serializers/UIDEdgeInsets+Foundation.m | 26 + .../Serializers/UIDFoundation.h | 16 + .../UIDFrameworkEvent+Foundation.h | 24 + .../UIDFrameworkEvent+Foundation.m | 34 + .../UIDFrameworkEventMetadata+Foundation.h | 24 + .../UIDFrameworkEventMetadata+Foundation.m | 25 + .../Serializers/UIDInitEvent+Foundation.h | 24 + .../Serializers/UIDInitEvent+Foundation.m | 27 + .../Serializers/UIDInspectable+Foundation.h | 24 + .../Serializers/UIDInspectable+Foundation.m | 21 + .../UIDInspectableArray+Foundation.h | 24 + .../UIDInspectableArray+Foundation.m | 27 + .../UIDInspectableObject+Foundation.h | 24 + .../UIDInspectableObject+Foundation.m | 22 + .../UIDInspectableValue+Foundation.h | 69 ++ .../UIDInspectableValue+Foundation.mm | 183 ++++++ .../Serializers/UIDJSONSerializer.h | 27 + .../Serializers/UIDJSONSerializer.mm | 68 ++ .../UIDLazyInspectable+Foundation.h | 24 + .../UIDLazyInspectable+Foundation.m | 31 + .../Serializers/UIDMetadata+Foundation.h | 24 + .../Serializers/UIDMetadata+Foundation.m | 27 + .../UIDMetadataUpdateEvent+Foundation.h | 24 + .../UIDMetadataUpdateEvent+Foundation.m | 24 + .../Serializers/UIDNode+Foundation.h | 24 + .../Serializers/UIDNode+Foundation.m | 47 ++ .../UIDPerfStatsEvent+Foundation.h | 24 + .../UIDPerfStatsEvent+Foundation.m | 43 ++ .../UIDSubtreeUpdateEvent+Foundation.h | 24 + .../UIDSubtreeUpdateEvent+Foundation.m | 32 + .../Serializers/UIImage+Foundation.h | 23 + .../Serializers/UIImage+Foundation.m | 33 + .../Traversal/UIDHierarchyTraversal.h | 30 + .../Traversal/UIDHierarchyTraversal.m | 142 ++++ .../Utilities/UIDMainThread.h | 24 + .../Utilities/UIDMainThread.mm | 37 ++ .../Utilities/UIDPerformance.h | 24 + .../Utilities/UIDPerformance.m | 49 ++ .../Utilities/UIDSnapshot.h | 23 + .../Utilities/UIDSnapshot.m | 49 ++ .../Utilities/UIDSwizzle.h | 14 + .../Utilities/UIDSwizzle.m | 57 ++ .../Utilities/UIDThrottle.h | 29 + .../Utilities/UIDThrottle.m | 73 +++ .../Utilities/UIDTimeUtilities.h | 25 + .../Utilities/UIDTimeUtilities.m | 52 ++ 134 files changed, 6368 insertions(+) create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Analytics/UIDFrameworkEventManager.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Analytics/UIDSerialFrameworkEventManager.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Analytics/UIDSerialFrameworkEventManager.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Core/UIDContext.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Core/UIDContext.mm create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Core/UIDUpdate.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Core/UIDUpdate.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Core/UIDUpdateDigester.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDChainedDescriptor.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDChainedDescriptor.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDDescriptor.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDDescriptorRegister.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDDescriptorRegister.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDMetadataRegister.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDMetadataRegister.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDNodeDescriptor.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDNodeDescriptor.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUIApplicationDescriptor.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUIApplicationDescriptor.mm create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUILabelDescriptor.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUILabelDescriptor.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUINavigationControllerDescriptor.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUINavigationControllerDescriptor.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUITabBarControllerDescriptor.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUITabBarControllerDescriptor.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUIViewControllerDescriptor.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUIViewControllerDescriptor.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUIViewDescriptor.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUIViewDescriptor.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUIWindowDescriptor.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUIWindowDescriptor.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIView+UIDDescriptor.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIView+UIDDescriptor.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Events/UIDInitEvent.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Events/UIDInitEvent.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Events/UIDMetadataUpdateEvent.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Events/UIDMetadataUpdateEvent.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Events/UIDPerfStatsEvent.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Events/UIDPerfStatsEvent.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Events/UIDSubtreeUpdateEvent.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Events/UIDSubtreeUpdateEvent.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin.mm create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDBounds.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDBounds.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDEdgeInsets.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDEdgeInsets.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDFrameworkEvent.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDFrameworkEvent.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDFrameworkEventMetadata.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDFrameworkEventMetadata.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDInspectable.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDInspectable.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDMetadata.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDMetadata.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDNode.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDNode.mm create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDTreeObserver.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDTreeObserver.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDTreeObserverBuilder.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDTreeObserverFactory.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDTreeObserverFactory.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDTreeObserverManager.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDTreeObserverManager.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDUIApplicationObserver.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDUIApplicationObserver.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDUIKitObserver.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDUIKitObserver.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDUIViewBasicObserver.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDUIViewBasicObserver.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDUIViewObserverDelegate.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIView+Observer.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIView+Observer.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/NSArray+Foundation.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/NSArray+Foundation.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/NSDictionary+Foundation.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/NSDictionary+Foundation.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/NSNull+Foundation.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/NSNull+Foundation.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/NSSet+Foundation.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/NSSet+Foundation.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/NSString+Foundation.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/NSString+Foundation.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/NSValue+Foundation.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/NSValue+Foundation.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDBounds+Foundation.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDBounds+Foundation.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDEdgeInsets+Foundation.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDEdgeInsets+Foundation.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDFoundation.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDFrameworkEvent+Foundation.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDFrameworkEvent+Foundation.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDFrameworkEventMetadata+Foundation.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDFrameworkEventMetadata+Foundation.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDInitEvent+Foundation.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDInitEvent+Foundation.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDInspectable+Foundation.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDInspectable+Foundation.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDInspectableArray+Foundation.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDInspectableArray+Foundation.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDInspectableObject+Foundation.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDInspectableObject+Foundation.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDInspectableValue+Foundation.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDInspectableValue+Foundation.mm create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDJSONSerializer.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDJSONSerializer.mm create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDLazyInspectable+Foundation.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDLazyInspectable+Foundation.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDMetadata+Foundation.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDMetadata+Foundation.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDMetadataUpdateEvent+Foundation.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDMetadataUpdateEvent+Foundation.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDNode+Foundation.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDNode+Foundation.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDPerfStatsEvent+Foundation.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDPerfStatsEvent+Foundation.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDSubtreeUpdateEvent+Foundation.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDSubtreeUpdateEvent+Foundation.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIImage+Foundation.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIImage+Foundation.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Traversal/UIDHierarchyTraversal.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Traversal/UIDHierarchyTraversal.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Utilities/UIDMainThread.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Utilities/UIDMainThread.mm create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Utilities/UIDPerformance.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Utilities/UIDPerformance.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Utilities/UIDSnapshot.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Utilities/UIDSnapshot.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Utilities/UIDSwizzle.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Utilities/UIDSwizzle.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Utilities/UIDThrottle.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Utilities/UIDThrottle.m create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Utilities/UIDTimeUtilities.h create mode 100644 iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Utilities/UIDTimeUtilities.m diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Analytics/UIDFrameworkEventManager.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Analytics/UIDFrameworkEventManager.h new file mode 100644 index 000000000..654ef387b --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Analytics/UIDFrameworkEventManager.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import + +NS_ASSUME_NONNULL_BEGIN + +@class UIDFrameworkEventMetadata; +@class UIDFrameworkEvent; + +@protocol UIDFrameworkEventManager + +- (void)registerEventMetadata:(UIDFrameworkEventMetadata*)eventMetadata; +- (void)emitEvent:(UIDFrameworkEvent*)event; +- (NSArray*)eventsMetadata; +- (NSArray*)events; +- (void)enable; +- (void)disable; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Analytics/UIDSerialFrameworkEventManager.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Analytics/UIDSerialFrameworkEventManager.h new file mode 100644 index 000000000..efdecc574 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Analytics/UIDSerialFrameworkEventManager.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import +#import "UIDFrameworkEventManager.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface UIDSerialFrameworkEventManager : NSObject + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Analytics/UIDSerialFrameworkEventManager.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Analytics/UIDSerialFrameworkEventManager.m new file mode 100644 index 000000000..c547e388c --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Analytics/UIDSerialFrameworkEventManager.m @@ -0,0 +1,75 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "UIDSerialFrameworkEventManager.h" + +@interface UIDSerialFrameworkEventManager () { + dispatch_queue_t _queue; + NSMutableArray* _eventMetadata; + NSMutableArray* _events; + BOOL _enabled; +} + +@end + +@implementation UIDSerialFrameworkEventManager + +- (instancetype)init { + if (self = [super init]) { + _enabled = false; + _eventMetadata = [NSMutableArray new]; + _events = [NSMutableArray new]; + _queue = dispatch_queue_create( + "ui-debugger.framework-events", DISPATCH_QUEUE_SERIAL); + } + return self; +} + +- (void)emitEvent:(nonnull UIDFrameworkEvent*)event { + dispatch_async(_queue, ^{ + if (self->_enabled) { + [self->_events addObject:event]; + } + }); +} + +- (void)registerEventMetadata: + (nonnull UIDFrameworkEventMetadata*)eventMetadata { + [_eventMetadata addObject:eventMetadata]; +} + +- (NSArray*)eventsMetadata { + return [_eventMetadata copy]; +} + +- (NSArray*)events { + __block NSArray* events = nil; + dispatch_sync(_queue, ^{ + events = [_events copy]; + [_events removeAllObjects]; + }); + return events ?: @[]; +} + +- (void)enable { + dispatch_async(_queue, ^{ + self->_enabled = true; + }); +} + +- (void)disable { + dispatch_async(_queue, ^{ + self->_enabled = false; + [self->_events removeAllObjects]; + }); +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Core/UIDContext.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Core/UIDContext.h new file mode 100644 index 000000000..7b5c39c26 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Core/UIDContext.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import +#import +#import "UIDFrameworkEventManager.h" +#import "UIDUpdateDigester.h" + +NS_ASSUME_NONNULL_BEGIN + +@class UIDDescriptorRegister; +@class UIDTreeObserverFactory; +@class UIDFrameworkEvent; +@class UIDFrameworkEventMetadata; + +@interface UIDContext : NSObject + +@property(nonatomic, nullable) UIApplication* application; +@property(nonatomic, nullable) id connection; +@property(nonatomic, readonly) UIDDescriptorRegister* descriptorRegister; +@property(nonatomic, readonly) UIDTreeObserverFactory* observerFactory; +@property(nonatomic, nullable) id updateDigester; +@property(nonatomic, strong, readonly) id + frameworkEventManager; + +- (instancetype)initWithApplication:(UIApplication*)application + descriptorRegister:(UIDDescriptorRegister*)descriptorRegister + observerFactory:(UIDTreeObserverFactory*)observerFactory; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Core/UIDContext.mm b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Core/UIDContext.mm new file mode 100644 index 000000000..686a53382 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Core/UIDContext.mm @@ -0,0 +1,33 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "UIDContext.h" +#import "UIDDescriptorRegister.h" +#import "UIDSerialFrameworkEventManager.h" +#import "UIDTreeObserverFactory.h" + +@implementation UIDContext + +- (instancetype)initWithApplication:(UIApplication*)application + descriptorRegister:(UIDDescriptorRegister*)descriptorRegister + observerFactory:(UIDTreeObserverFactory*)observerFactory { + self = [super init]; + if (self) { + _application = application; + _descriptorRegister = descriptorRegister; + _observerFactory = observerFactory; + _connection = nil; + _frameworkEventManager = [UIDSerialFrameworkEventManager new]; + } + return self; +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Core/UIDUpdate.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Core/UIDUpdate.h new file mode 100644 index 000000000..d522284e7 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Core/UIDUpdate.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import + +NS_ASSUME_NONNULL_BEGIN + +@protocol UIDUpdate +@end + +@interface UIDSubtreeUpdate : NSObject + +@property(nonatomic) NSString* observerType; +@property(nonatomic) NSUInteger rootId; +@property(nonatomic) NSArray* nodes; +@property(nonatomic) NSTimeInterval timestamp; +@property(nonatomic) long traversalMS; +@property(nonatomic) long snapshotMS; +@property(nonatomic) UIImage* snapshot; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Core/UIDUpdate.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Core/UIDUpdate.m new file mode 100644 index 000000000..ff21a128a --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Core/UIDUpdate.m @@ -0,0 +1,15 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "UIDUpdate.h" + +@implementation UIDSubtreeUpdate +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Core/UIDUpdateDigester.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Core/UIDUpdateDigester.h new file mode 100644 index 000000000..816d1fd87 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Core/UIDUpdateDigester.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import +#import "UIDUpdate.h" + +NS_ASSUME_NONNULL_BEGIN + +@protocol UIDUpdateDigester + +- (void)digest:(id)update; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDChainedDescriptor.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDChainedDescriptor.h new file mode 100644 index 000000000..924a1f10c --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDChainedDescriptor.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import +#import "UIDNodeDescriptor.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + A chained descriptor is a special type of descriptor that models the + inheritance hierarchy in native UI frameworks. With this setup you can define + a descriptor for each class in the inheritance chain and given that we record + the super class's descriptor we can automatically aggregate the results for + each descriptor in the inheritance hierarchy in a chain. + + The result is that each descriptor in the inheritance chain only exports the + attributes that it knows about but we still get all the attributes from the + parent classes. + */ +@interface UIDChainedDescriptor<__covariant T> : UIDNodeDescriptor + +@property(strong, nonatomic) UIDChainedDescriptor* parent; + +/** The children this node exposes in the inspector. */ +- (NSArray>*)aggregateChildrenOfNode:(T)node; + +/** + Get the attributes to show for this node in the sidebar of the inspector. The + object will be shown in order and with a header matching the given name. + */ +- (void)aggregateAttributes:(UIDMutableAttributes*)attributes forNode:(id)node; + +/** + These are shown inline in the tree view on the desktop, will likely be removed + in the future. + */ +- (void)aggregateInlineAttributes:(UIDMutableInlineAttributes*)attributes + forNode:(id)node; + +- (NSSet*)aggregateTagsForNode:(id)node; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDChainedDescriptor.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDChainedDescriptor.m new file mode 100644 index 000000000..1b5a5d01e --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDChainedDescriptor.m @@ -0,0 +1,87 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "UIDChainedDescriptor.h" +#import "UIDInspectable.h" + +@implementation UIDChainedDescriptor + +- (NSSet*)tagsForNode:(id)node { + NSSet* tags = [self aggregateTagsForNode:node] ?: [_parent tagsForNode:node]; + return tags ?: [NSSet set]; +} + +- (NSSet*)aggregateTagsForNode:(id)node { + return nil; +} + +- (UIDBounds*)boundsForNode:(id)node { + return [_parent boundsForNode:node]; +} + +- (id)activeChildForNode:(id)node { + return [_parent activeChildForNode:node]; +} + +- (UIImage*)snapshotForNode:(id)node { + return [_parent snapshotForNode:node]; +} + +- (NSArray>*)childrenOfNode:(id)node { + NSArray>* children = [self aggregateChildrenOfNode:node]; + if (!children) { + children = [_parent childrenOfNode:node]; + } + + return children != NULL ? children : [NSArray array]; +} + +- (NSArray>*)aggregateChildrenOfNode:(id)node { + return nil; +} + +- (UIDAttributes*)attributesForNode:(id)node { + UIDMutableAttributes* attributes = [NSMutableDictionary new]; + [self aggregateAttributes:attributes forNode:node]; + + UIDChainedDescriptor* currentDescriptor = _parent; + while (currentDescriptor) { + [currentDescriptor aggregateAttributes:attributes forNode:node]; + currentDescriptor = currentDescriptor.parent; + } + + return attributes; +} + +- (void)aggregateAttributes:(UIDMutableAttributes*)attributes forNode:(id)node { +} + +- (UIDInlineAttributes*)inlineAttributesForNode:(id)node { + UIDMutableInlineAttributes* attributes = [NSMutableDictionary new]; + + [attributes addEntriesFromDictionary:[super inlineAttributesForNode:node]]; + + [self aggregateInlineAttributes:attributes forNode:node]; + + UIDChainedDescriptor* currentDescriptor = _parent; + while (currentDescriptor) { + [currentDescriptor aggregateInlineAttributes:attributes forNode:node]; + currentDescriptor = currentDescriptor.parent; + } + + return attributes; +} + +- (void)aggregateInlineAttributes:(UIDMutableInlineAttributes*)attributes + forNode:(id)node { +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDDescriptor.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDDescriptor.h new file mode 100644 index 000000000..4e903a4fb --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDDescriptor.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import +#import "UIDBounds.h" +#import "UIDInspectable.h" + +NS_ASSUME_NONNULL_BEGIN + +@protocol UIDDescriptor + +/** + A globally unique ID used to identify a node in the hierarchy. + */ +- (NSUInteger)UID_identifier; + +/** + The name used to identify this node in the inspector. Does not need to be + unique. A good default is to use the class name of the node. + */ +- (NSString*)UID_name; + +/** + Get the attributes to show for this node in the sidebar of the inspector. The + object will be shown in order and with a header matching the given name. + */ +- (void)UID_aggregateAttributes:(UIDMutableAttributes*)attributes; + +@optional + +/** + These are shown inline in the tree view on the desktop, will likely be removed + in the future. + */ +- (UIDInlineAttributes*)UID_inlineAttributes; + +/** The children this node exposes in the inspector. */ +- (NSArray>*)UID_children; + +/** Should be w.r.t the direct parent */ +- (UIDBounds*)UID_bounds; + +/** + If the type has the concept of overlapping children, then this indicates + which child is active / on top, we will only listen to / traverse this child. + If return null we assume all children are 'active'. + */ +- (id)UID_activeChild; + +/** Get a snapshot of the node. */ +- (UIImage*)UID_snapshot; + +/** + Set of tags to describe this node in an abstract way for the UI. Unfortunately + this can't be an enum as we have to plugin 3rd party frameworks dynamically. + */ +- (NSSet*)UID_tags; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDDescriptorRegister.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDDescriptorRegister.h new file mode 100644 index 000000000..9e428534b --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDDescriptorRegister.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import +#import "UIDNodeDescriptor.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface UIDDescriptorRegister : NSObject + ++ (instancetype)defaultRegister; + +- (void)registerDescriptor:(UIDNodeDescriptor*)descriptor forClass:(Class)clazz; +- (UIDNodeDescriptor*)descriptorForClass:(Class)clazz; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDDescriptorRegister.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDDescriptorRegister.m new file mode 100644 index 000000000..815f926f6 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDDescriptorRegister.m @@ -0,0 +1,103 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "UIDDescriptorRegister.h" +#import +#import "UIDChainedDescriptor.h" +#import "UIDUIApplicationDescriptor.h" +#import "UIDUILabelDescriptor.h" +#import "UIDUINavigationControllerDescriptor.h" +#import "UIDUITabBarControllerDescriptor.h" +#import "UIDUIViewControllerDescriptor.h" +#import "UIDUIViewDescriptor.h" +#import "UIDUIWindowDescriptor.h" + +@interface UIDDescriptorRegister () + +- (void)prepareChain; + +@end + +@implementation UIDDescriptorRegister { + NSMutableDictionary* _register; +} + +- (instancetype)init { + self = [super init]; + if (self) { + _register = [NSMutableDictionary new]; + } + return self; +} + ++ (instancetype)defaultRegister { + static UIDDescriptorRegister* defaultRegister = nil; + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + defaultRegister = [UIDDescriptorRegister new]; + + [defaultRegister registerDescriptor:[UIDUIApplicationDescriptor new] + forClass:[UIApplication class]]; + [defaultRegister registerDescriptor:[UIDUIWindowDescriptor new] + forClass:[UIWindow class]]; + [defaultRegister registerDescriptor:[UIDUIViewControllerDescriptor new] + forClass:[UIViewController class]]; + [defaultRegister registerDescriptor:[UIDUIViewDescriptor new] + forClass:[UIView class]]; + [defaultRegister registerDescriptor:[UIDUILabelDescriptor new] + forClass:[UILabel class]]; + }); + + return defaultRegister; +} + +- (void)registerDescriptor:(UIDNodeDescriptor*)descriptor + forClass:(Class)clazz { + NSString* key = NSStringFromClass(clazz); + _register[key] = descriptor; + + [self prepareChain]; +} + +- (UIDNodeDescriptor*)descriptorForClass:(Class)clazz { + UIDNodeDescriptor* classDescriptor = nil; + + while (classDescriptor == nil && clazz != nil) { + classDescriptor = [_register objectForKey:NSStringFromClass(clazz)]; + clazz = [clazz superclass]; + } + + return classDescriptor; +} + +- (void)prepareChain { + NSEnumerator* enumerator = [_register keyEnumerator]; + NSString* key; + + while ((key = [enumerator nextObject])) { + UIDNodeDescriptor* descriptor = [_register objectForKey:key]; + if ([descriptor isKindOfClass:[UIDChainedDescriptor class]]) { + UIDChainedDescriptor* chainedDescriptor = + (UIDChainedDescriptor*)descriptor; + + Class clazz = NSClassFromString(key); + Class superclass = [clazz superclass]; + + UIDNodeDescriptor* superDescriptor = [self descriptorForClass:superclass]; + if ([superDescriptor isKindOfClass:[UIDChainedDescriptor class]]) { + chainedDescriptor.parent = (UIDChainedDescriptor*)superDescriptor; + } + } + } +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDMetadataRegister.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDMetadataRegister.h new file mode 100644 index 000000000..ef880b9e5 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDMetadataRegister.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import +#import "UIDMetadata.h" + +NS_ASSUME_NONNULL_BEGIN + +FOUNDATION_EXPORT NSString* const UIDEBUGGER_METADATA_TYPE_IDENTIFIER; +FOUNDATION_EXPORT NSString* const UIDEBUGGER_METADATA_TYPE_ATTRIBUTE; +FOUNDATION_EXPORT NSString* const UIDEBUGGER_METADATA_TYPE_LAYOUT; +FOUNDATION_EXPORT NSString* const UIDEBUGGER_METADATA_TYPE_DOCUMENTATION; + +@interface UIDMetadataRegister : NSObject + ++ (instancetype)shared; + +- (UIDMetadataId)registerMetadataWithType:(NSString*)type name:(NSString*)name; + +- (UIDMetadataId)registerMetadataWithType:(NSString*)type + name:(NSString*)name + isMutable:(bool)isMutable + definedBy:(UIDMetadataId)definedById; + +- (NSDictionary*)extractPendingMetadata; + +- (UIDMetadataId)findMetadataDefinedBy:(UIDMetadataId)definedById + name:(NSString*)name; +- (UIDMetadataId)findRootMetadataWithName:(NSString*)name; + +- (void)reset; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDMetadataRegister.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDMetadataRegister.m new file mode 100644 index 000000000..519cb87e0 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDMetadataRegister.m @@ -0,0 +1,126 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "UIDMetadataRegister.h" + +NSString* const UIDEBUGGER_METADATA_TYPE_IDENTIFIER = @"identity"; +NSString* const UIDEBUGGER_METADATA_TYPE_ATTRIBUTE = @"attribute"; +NSString* const UIDEBUGGER_METADATA_TYPE_LAYOUT = @"layout"; +NSString* const UIDEBUGGER_METADATA_TYPE_DOCUMENTATION = @"documentation"; + +typedef NSMutableDictionary* UIDNamedMetadata; +@interface UIDMetadataRegister () { + NSMutableDictionary* _register; + NSMutableArray* _pending; + uint32_t _generator; + + UIDMetadataId _rootId; + NSLock* _lock; +} + +@end + +@implementation UIDMetadataRegister + +- (instancetype)init { + self = [super init]; + if (self) { + _lock = [NSLock new]; + _register = [NSMutableDictionary new]; + _pending = [NSMutableArray new]; + + _rootId = @0; + _generator = 0; + + [_register setObject:[NSMutableDictionary new] forKey:_rootId]; + } + return self; +} + ++ (instancetype)shared { + static UIDMetadataRegister* instance = nil; + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + instance = [UIDMetadataRegister new]; + }); + + return instance; +} + +- (UIDMetadataId)registerMetadataWithType:(NSString*)type name:(NSString*)name { + return [self registerMetadataWithType:type + name:name + isMutable:false + definedBy:_rootId]; +} + +- (UIDMetadataId)registerMetadataWithType:(NSString*)type + name:(NSString*)name + isMutable:(bool)isMutable + definedBy:(UIDMetadataId)parent { + UIDMetadataId identifier = @(++_generator); + UIDMetadata* metadata = [[UIDMetadata alloc] initWithIdentifier:identifier + type:type + name:name + isMutable:isMutable + parent:parent]; + + [_lock lock]; + if (![_register objectForKey:parent]) { + [_register setObject:[NSMutableDictionary new] forKey:parent]; + } + [[_register objectForKey:parent] setObject:metadata forKey:name]; + + [_pending addObject:metadata]; + [_lock unlock]; + + return identifier; +} + +- (UIDMetadataId)findRootMetadataWithName:(NSString*)name { + return [self findMetadataDefinedBy:_rootId name:name]; +} + +- (UIDMetadataId)findMetadataDefinedBy:(UIDMetadataId)parentId + name:(NSString*)name { + [_lock lock]; + UIDMetadata* metadata = [[_register objectForKey:parentId] objectForKey:name]; + [_lock unlock]; + if (metadata) { + return metadata.identifier; + } + + return nil; +} + +- (NSDictionary*)extractPendingMetadata { + NSMutableDictionary* pendingMetadata = [NSMutableDictionary new]; + [_lock lock]; + for (UIDMetadata* metadata in _pending) { + [pendingMetadata setObject:metadata forKey:metadata.identifier]; + } + [_pending removeAllObjects]; + [_lock unlock]; + return pendingMetadata; +} + +- (void)reset { + [_lock lock]; + [_pending removeAllObjects]; + for (id key in _register) { + UIDNamedMetadata value = [_register objectForKey:key]; + [_pending addObjectsFromArray:value.allValues]; + } + [_lock unlock]; +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDNodeDescriptor.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDNodeDescriptor.h new file mode 100644 index 000000000..c4e851dd3 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDNodeDescriptor.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import +#import "UIDBounds.h" +#import "UIDInspectable.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface UIDNodeDescriptor<__covariant T> : NSObject + +/** + A globally unique ID used to identify a node in the hierarchy. + */ +- (NSUInteger)identifierForNode:(T)node; + +/** + The name used to identify this node in the inspector. Does not need to be + unique. A good default is to use the class name of the node. + */ +- (NSString*)nameForNode:(T)node; + +/** + Get the attributes to show for this node in the sidebar of the inspector. The + object will be shown in order and with a header matching the given name. + */ +- (UIDAttributes*)attributesForNode:(T)node; + +/** + These are shown inline in the tree view on the desktop, will likely be removed + in the future. + */ +- (UIDInlineAttributes*)inlineAttributesForNode:(T)node; + +/** + These contain additional contextual data (currently: Bloks node metadata). + */ +- (UIDGenericAttributes*)hiddenAttributesForNode:(T)node; + +/** The children this node exposes in the inspector. */ +- (NSArray>*)childrenOfNode:(T)node; + +/** Should be w.r.t the direct parent */ +- (UIDBounds*)boundsForNode:(T)node; + +/** + If the type has the concept of overlapping children, then this indicates + which child is active / on top, we will only listen to / traverse this child. + If return null we assume all children are 'active'. + */ +- (id)activeChildForNode:(T)node; + +/** Get a snapshot of the node. */ +- (UIImage*)snapshotForNode:(T)node; + +/** + Set of tags to describe this node in an abstract way for the UI. Unfortunately + this can't be an enum as we have to plugin 3rd party frameworks dynamically. + */ +- (NSSet*)tagsForNode:(T)node; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDNodeDescriptor.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDNodeDescriptor.m new file mode 100644 index 000000000..e86b3ebe7 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDNodeDescriptor.m @@ -0,0 +1,56 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "UIDNodeDescriptor.h" + +@implementation UIDNodeDescriptor + +- (NSUInteger)identifierForNode:(id)node { + return [node hash]; +} + +- (NSString*)nameForNode:(id)node { + return NSStringFromClass([node class]); +} + +- (UIDInlineAttributes*)inlineAttributesForNode:(id)node { + return @{@"address" : [NSString stringWithFormat:@"%p", node]}; +} + +- (UIDGenericAttributes*)hiddenAttributesForNode:(id)node { + return nil; +} + +- (UIDAttributes*)attributesForNode:(id)node { + return [NSDictionary dictionary]; +} + +- (NSArray>*)childrenOfNode:(id)node { + return [NSArray array]; +} + +- (NSSet*)tagsForNode:(id)node { + return [NSSet set]; +} + +- (UIDBounds*)boundsForNode:(id)node { + return nil; +} + +- (id)activeChildForNode:(id)node { + return nil; +} + +- (UIImage*)snapshotForNode:(id)node { + return nil; +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUIApplicationDescriptor.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUIApplicationDescriptor.h new file mode 100644 index 000000000..be80e89f4 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUIApplicationDescriptor.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import +#import "UIDNodeDescriptor.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface UIDUIApplicationDescriptor : UIDNodeDescriptor + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUIApplicationDescriptor.mm b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUIApplicationDescriptor.mm new file mode 100644 index 000000000..40e42cf86 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUIApplicationDescriptor.mm @@ -0,0 +1,50 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "UIDUIApplicationDescriptor.h" +#import +#import +#import +#import "UIDBounds.h" +#import "UIDSnapshot.h" + +@implementation UIDUIApplicationDescriptor + +- (NSArray>*)childrenOfNode:(UIApplication*)node { + static std::unordered_set ignoredWindows( + {"FBStatusBarTrackingWindow", + "FBAccessibilityOverlayWindow", + "UITextEffectsWindow"}); + + NSMutableArray* children = [NSMutableArray new]; + for (UIWindow* window in node.windows) { + if (ignoredWindows.find(class_getName(window.class)) != + ignoredWindows.end()) { + continue; + } + [children addObject:window]; + } + return children; +} + +- (id)activeChildForNode:(UIApplication*)node { + return node.keyWindow; +} + +- (UIDBounds*)boundsForNode:(UIApplication*)node { + return [UIDBounds fromRect:[UIScreen mainScreen].bounds]; +} + +- (UIImage*)snapshotForNode:(UIApplication*)node { + return UIDApplicationSnapshot(node, [self childrenOfNode:node]); +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUILabelDescriptor.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUILabelDescriptor.h new file mode 100644 index 000000000..c32d92af2 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUILabelDescriptor.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import +#import "UIDChainedDescriptor.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface UIDUILabelDescriptor : UIDChainedDescriptor + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUILabelDescriptor.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUILabelDescriptor.m new file mode 100644 index 000000000..be2299cc4 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUILabelDescriptor.m @@ -0,0 +1,248 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "UIDUILabelDescriptor.h" +#import "UIDInspectable.h" +#import "UIDMetadata.h" +#import "UIDMetadataRegister.h" + +@interface UIDUILabelDescriptor () { + UIDMetadataId UILabelAttributeId; + UIDMetadataId TextAttributeId; + UIDMetadataId AttributedTextAttributeId; + UIDMetadataId FontAttributeId; + UIDMetadataId TextColorAttributeId; + UIDMetadataId TextAlignmentAttributeId; + UIDMetadataId LineBreakModeAttributeId; + UIDMetadataId LineBreakStrategyAttributeId; + UIDMetadataId EnabledAttributeId; + UIDMetadataId ShowExpansionTextAttributeId; + UIDMetadataId AdjustsFontSizeToFitWidthAttributeId; + UIDMetadataId AllowsDefaultTightneningForTruncationAttributeId; + UIDMetadataId BaselineAdjustmentAttributeId; + UIDMetadataId MinScaleFactorAttributeId; + UIDMetadataId NumberOfLinesAttributeId; + UIDMetadataId HighlightedTextColorAttributeId; + UIDMetadataId HighlightedAttributeId; + UIDMetadataId ShadowColorAttributeId; + UIDMetadataId ShadowOffsetAttributeId; + + NSDictionary* NSTextAlignmentEnum; + NSDictionary* NSLineBreakModeEnum; + NSDictionary* NSLineBreakStrategyEnum; + NSDictionary* UIBaselineAdjustmentEnum; +} + +@end + +@implementation UIDUILabelDescriptor + +- (instancetype)init { + self = [super init]; + if (self) { + NSTextAlignmentEnum = @{ + @(NSTextAlignmentLeft) : @"LEFT", + @(NSTextAlignmentRight) : @"RIGHT", + @(NSTextAlignmentCenter) : @"CENTER", + @(NSTextAlignmentJustified) : @"JUSTIFIED", + @(NSTextAlignmentNatural) : @"NATURAL", + }; + NSLineBreakModeEnum = @{ + @(NSLineBreakByWordWrapping) : @"WORD WRAPPING", + @(NSLineBreakByCharWrapping) : @"CHAR WRAPPING", + @(NSLineBreakByClipping) : @"CLIPPING", + @(NSLineBreakByTruncatingHead) : @"TRUNCATING HEAD", + @(NSLineBreakByTruncatingTail) : @"TRUNCATING TAIL", + @(NSLineBreakByTruncatingMiddle) : @"TRUNCATING MIDDLE", + }; + if (@available(iOS 14.0, *)) { + NSLineBreakStrategyEnum = @{ + @(NSLineBreakStrategyNone) : @"NONE", + @(NSLineBreakStrategyPushOut) : @"PUSH OUT", + @(NSLineBreakStrategyHangulWordPriority) : @"HANGUL WORD PRIORITY", + @(NSLineBreakStrategyStandard) : @"STANDARD", + }; + } else { + NSLineBreakStrategyEnum = @{ + @(NSLineBreakStrategyNone) : @"NONE", + @(NSLineBreakStrategyPushOut) : @"PUSH OUT", + }; + } + UIBaselineAdjustmentEnum = @{ + @(UIBaselineAdjustmentAlignBaselines) : @"ALIGN BASELINES", + @(UIBaselineAdjustmentAlignCenters) : @"ALIGN CENTERS", + @(UIBaselineAdjustmentNone) : @"NONE", + }; + + UILabelAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"UILabel"]; + TextAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"text" + isMutable:false + definedBy:UILabelAttributeId]; + AttributedTextAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"attributedText" + isMutable:false + definedBy:UILabelAttributeId]; + FontAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"font" + isMutable:false + definedBy:UILabelAttributeId]; + TextColorAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"textColor" + isMutable:false + definedBy:UILabelAttributeId]; + TextAlignmentAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"textAlignment" + isMutable:false + definedBy:UILabelAttributeId]; + LineBreakModeAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"lineBreakMode" + isMutable:false + definedBy:UILabelAttributeId]; + LineBreakStrategyAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"lineBreakStrategy" + isMutable:false + definedBy:UILabelAttributeId]; + EnabledAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"enabled" + isMutable:false + definedBy:UILabelAttributeId]; + if (@available(iOS 15.0, macCatalyst 15.0, *)) { + ShowExpansionTextAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"showsExpansionTextWhenTruncated" + isMutable:false + definedBy:UILabelAttributeId]; + } + AdjustsFontSizeToFitWidthAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"adjustsFontSizeToFitWidth" + isMutable:false + definedBy:UILabelAttributeId]; + AllowsDefaultTightneningForTruncationAttributeId = + [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"allowsDefaultTighteningForTruncation" + isMutable:false + definedBy:UILabelAttributeId]; + BaselineAdjustmentAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"baselineAdjustment" + isMutable:false + definedBy:UILabelAttributeId]; + MinScaleFactorAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"minimumScaleFactor" + isMutable:false + definedBy:UILabelAttributeId]; + NumberOfLinesAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"numberOfLines" + isMutable:false + definedBy:UILabelAttributeId]; + HighlightedTextColorAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"highlightedTextColor" + isMutable:false + definedBy:UILabelAttributeId]; + HighlightedAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"highligted" + isMutable:false + definedBy:UILabelAttributeId]; + ShadowColorAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"shadowColor" + isMutable:false + definedBy:UILabelAttributeId]; + ShadowOffsetAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"shadowOffset" + isMutable:false + definedBy:UILabelAttributeId]; + } + return self; +} + +- (void)aggregateAttributes:(UIDMutableAttributes*)attributes + forNode:(UILabel*)node { + NSMutableDictionary* labelAttributes = [NSMutableDictionary new]; + + [labelAttributes setObject:[UIDInspectableText fromText:node.text] + forKey:TextAttributeId]; + [labelAttributes + setObject:[UIDInspectableText fromText:[node.attributedText string]] + forKey:AttributedTextAttributeId]; + [labelAttributes setObject:[UIDInspectableText fromText:node.font.fontName] + forKey:FontAttributeId]; + [labelAttributes setObject:[UIDInspectableColor fromColor:node.textColor] + forKey:TextColorAttributeId]; + [labelAttributes + setObject:[UIDInspectableEnum + from:NSTextAlignmentEnum[@(node.textAlignment)]] + forKey:TextAlignmentAttributeId]; + [labelAttributes + setObject:[UIDInspectableEnum + from:NSLineBreakModeEnum[@(node.lineBreakMode)]] + forKey:LineBreakModeAttributeId]; + [labelAttributes setObject:[UIDInspectableBoolean fromBoolean:node.enabled] + forKey:EnabledAttributeId]; + if (@available(iOS 15.0, macCatalyst 15.0, *)) { + [labelAttributes + setObject:[UIDInspectableBoolean + fromBoolean:node.showsExpansionTextWhenTruncated] + forKey:ShowExpansionTextAttributeId]; + } + + [labelAttributes setObject:[UIDInspectableBoolean + fromBoolean:node.adjustsFontSizeToFitWidth] + forKey:AdjustsFontSizeToFitWidthAttributeId]; + [labelAttributes + setObject:[UIDInspectableBoolean + fromBoolean:node.allowsDefaultTighteningForTruncation] + forKey:AllowsDefaultTightneningForTruncationAttributeId]; + [labelAttributes + setObject:[UIDInspectableEnum + from:UIBaselineAdjustmentEnum[@(node.baselineAdjustment)]] + forKey:BaselineAdjustmentAttributeId]; + [labelAttributes + setObject:[UIDInspectableNumber fromCGFloat:node.minimumScaleFactor] + forKey:MinScaleFactorAttributeId]; + [labelAttributes + setObject:[UIDInspectableNumber + fromNumber:[NSNumber numberWithInt:node.numberOfLines]] + forKey:NumberOfLinesAttributeId]; + [labelAttributes + setObject:[UIDInspectableColor fromColor:node.highlightedTextColor] + forKey:HighlightedTextColorAttributeId]; + [labelAttributes + setObject:[UIDInspectableBoolean fromBoolean:node.highlighted] + forKey:HighlightedAttributeId]; + [labelAttributes setObject:[UIDInspectableColor fromColor:node.shadowColor] + forKey:ShadowColorAttributeId]; + [labelAttributes setObject:[UIDInspectableSize fromSize:node.shadowOffset] + forKey:ShadowOffsetAttributeId]; + + [attributes setObject:[UIDInspectableObject fromFields:labelAttributes] + forKey:UILabelAttributeId]; +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUINavigationControllerDescriptor.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUINavigationControllerDescriptor.h new file mode 100644 index 000000000..10655718f --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUINavigationControllerDescriptor.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import +#import "UIDNodeDescriptor.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + UINavigationController + (https://developer.apple.com/documentation/uikit/uinavigationcontroller?language=objc) + Propeties: + - viewControllers: the view controllers currently on the navigation stack. + - topViewController: last controller pushed on top of the navigation + stack. + - visibleViewController: the view controller associated with the + currently visible view in the navigation stack. View Controller can also be + presented instead of pushed. If presented, then it hasn't changed the stack + even though its view is visible. + */ +@interface UIDUINavigationControllerDescriptor + : UIDNodeDescriptor + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUINavigationControllerDescriptor.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUINavigationControllerDescriptor.m new file mode 100644 index 000000000..b300fea99 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUINavigationControllerDescriptor.m @@ -0,0 +1,28 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "UIDUINavigationControllerDescriptor.h" + +@implementation UIDUINavigationControllerDescriptor + +- (NSArray>*)childrenOfNode:(UINavigationController*)node { + return node.viewControllers; +} + +- (id)activeChildForNode:(UINavigationController*)node { + return node.visibleViewController; +} + +- (UIDBounds*)boundsForNode:(UINavigationController*)node { + return [UIDBounds fromRect:node.view.bounds]; +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUITabBarControllerDescriptor.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUITabBarControllerDescriptor.h new file mode 100644 index 000000000..09fe38584 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUITabBarControllerDescriptor.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import +#import "UIDNodeDescriptor.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + UITabBarController + (https://developer.apple.com/documentation/uikit/uitabbarcontroller?language=objc) + Properties: + - viewControllers: root view controllers displayed by the tab bar user + interface. + - selectedViewController: the view controller associated with the + currently selected tab bar item. +*/ +@interface UIDUITabBarControllerDescriptor + : UIDNodeDescriptor + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUITabBarControllerDescriptor.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUITabBarControllerDescriptor.m new file mode 100644 index 000000000..c15af50f2 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUITabBarControllerDescriptor.m @@ -0,0 +1,28 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "UIDUITabBarControllerDescriptor.h" + +@implementation UIDUITabBarControllerDescriptor + +- (NSArray>*)childrenOfNode:(UITabBarController*)node { + return node.viewControllers; +} + +- (id)activeChildForNode:(UITabBarController*)node { + return node.selectedViewController; +} + +- (UIDBounds*)boundsForNode:(UITabBarController*)node { + return [UIDBounds fromRect:node.view.bounds]; +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUIViewControllerDescriptor.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUIViewControllerDescriptor.h new file mode 100644 index 000000000..79079410b --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUIViewControllerDescriptor.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import +#import "UIDNodeDescriptor.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface UIDUIViewControllerDescriptor : UIDNodeDescriptor + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUIViewControllerDescriptor.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUIViewControllerDescriptor.m new file mode 100644 index 000000000..578fb4c1f --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUIViewControllerDescriptor.m @@ -0,0 +1,30 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "UIDUIViewControllerDescriptor.h" + +@implementation UIDUIViewControllerDescriptor + +- (NSArray>*)childrenOfNode:(UIViewController*)node { + if (node.view != nil) { + return [NSArray arrayWithObject:node.view]; + } + + return [NSArray array]; +} + +- (UIDBounds*)boundsForNode:(UIViewController*)node { + CGRect bounds = CGRectMake( + 0, 0, node.view.bounds.size.width, node.view.bounds.size.height); + return [UIDBounds fromRect:bounds]; +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUIViewDescriptor.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUIViewDescriptor.h new file mode 100644 index 000000000..33e36b40e --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUIViewDescriptor.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import +#import "UIDChainedDescriptor.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface UIDUIViewDescriptor : UIDChainedDescriptor + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUIViewDescriptor.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUIViewDescriptor.m new file mode 100644 index 000000000..e2859e70b --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUIViewDescriptor.m @@ -0,0 +1,34 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "UIDUIViewDescriptor.h" +#import "UIView+UIDDescriptor.h" + +@implementation UIDUIViewDescriptor + +- (void)aggregateAttributes:(UIDMutableAttributes*)attributes + forNode:(UIView*)node { + [node UID_aggregateAttributes:attributes]; +} + +- (NSArray>*)childrenOfNode:(UIView*)node { + return [node UID_children]; +} + +- (UIImage*)snapshotForNode:(UIView*)node { + return [node UID_snapshot]; +} + +- (UIDBounds*)boundsForNode:(UIView*)node { + return [node UID_bounds]; +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUIWindowDescriptor.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUIWindowDescriptor.h new file mode 100644 index 000000000..b3046367b --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUIWindowDescriptor.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import +#import "UIDChainedDescriptor.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface UIDUIWindowDescriptor : UIDChainedDescriptor + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUIWindowDescriptor.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUIWindowDescriptor.m new file mode 100644 index 000000000..a667e1bf2 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIDUIWindowDescriptor.m @@ -0,0 +1,25 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "UIDUIWindowDescriptor.h" +#import "UIView+UIDDescriptor.h" + +@implementation UIDUIWindowDescriptor + +- (id)activeChildForNode:(UIWindow*)node { + return [node UID_activeChild]; +} + +- (UIDBounds*)boundsForNode:(UIWindow*)node { + return [UIDBounds fromRect:node.bounds]; +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIView+UIDDescriptor.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIView+UIDDescriptor.h new file mode 100644 index 000000000..13fb5faca --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIView+UIDDescriptor.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +FB_LINK_REQUIRE_CATEGORY(UIView_UIDDescriptor) +@interface UIView (UIDDescriptor) + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIView+UIDDescriptor.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIView+UIDDescriptor.m new file mode 100644 index 000000000..37a55269a --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Descriptors/UIView+UIDDescriptor.m @@ -0,0 +1,606 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "UIDInspectable.h" +#import "UIDMetadata.h" +#import "UIDMetadataRegister.h" +#import "UIDSnapshot.h" +#import "UIView+UIDDescriptor.h" + +FB_LINKABLE(UIView_UIDDescriptor) +@implementation UIView (UIDDescriptor) + +- (NSUInteger)UID_identifier { + return [self hash]; +} + +- (nonnull NSString*)UID_name { + return NSStringFromClass([self class]); +} + +- (void)UID_aggregateAttributes:(nonnull UIDMutableAttributes*)attributes { + static UIDMetadataId UIViewAttributeId; + static UIDMetadataId FrameAttributeId; + static UIDMetadataId BoundsAttributeId; + static UIDMetadataId CenterAttributeId; + static UIDMetadataId AnchorPointAttributeId; + static UIDMetadataId SafeAreaInsetsAttributeId; + static UIDMetadataId ClipsToBoundsAttributeId; + static UIDMetadataId HiddenAttributeId; + static UIDMetadataId AlphaAttributeId; + static UIDMetadataId OpaqueAttributeId; + static UIDMetadataId ClearContextBeforeDrawingAttributeId; + static UIDMetadataId BackgroundColorAttributeId; + static UIDMetadataId TintColorAttributeId; + static UIDMetadataId TagAttributeId; + + static UIDMetadataId CALayerAttributeId; + static UIDMetadataId CALayerShadowColorAttributeId; + static UIDMetadataId CALayerShadowOpacityAttributeId; + static UIDMetadataId CALayerShadowRadiusAttributeId; + static UIDMetadataId CALayerShadowOffsetAttributeId; + static UIDMetadataId CALayerBackgroundColorAttributeId; + static UIDMetadataId CALayerBorderColorAttributeId; + static UIDMetadataId CALayerBorderWidthAttributeId; + static UIDMetadataId CALayerCornerRadiusAttributeId; + static UIDMetadataId CALayerMasksToBoundsAttributeId; + + static UIDMetadataId AccessibilityAttributeId; + static UIDMetadataId IsAccessibilityElementAttributeId; + static UIDMetadataId AccessibilityLabelAttributeId; + static UIDMetadataId AccessibilityIdentifierAttributeId; + static UIDMetadataId AccessibilityValueAttributeId; + static UIDMetadataId AccessibilityHintAttributeId; + static UIDMetadataId AccessibilityTraitsAttributeId; + static UIDMetadataId AccessibilityViewIsModalAttributeId; + static UIDMetadataId ShouldGroupAccessibilityChildrenAttributeId; + + static UIDMetadataId AccessibilityTraitNoneAttributeId; + static UIDMetadataId AccessibilityTraitButtonAttributeId; + static UIDMetadataId AccessibilityTraitLinkAttributeId; + static UIDMetadataId AccessibilityTraitImageAttributeId; + static UIDMetadataId AccessibilityTraitSearchFieldAttributeId; + static UIDMetadataId AccessibilityTraitKeyboardKeyAttributeId; + static UIDMetadataId AccessibilityTraitStaticTextAttributeId; + static UIDMetadataId AccessibilityTraitHeaderAttributeId; + static UIDMetadataId AccessibilityTraitTabBarAttributeId; + static UIDMetadataId AccessibilityTraitSummaryElementAttributeId; + static UIDMetadataId AccessibilityTraitSelectedAttributeId; + static UIDMetadataId AccessibilityTraitNotEnabledAttributeId; + static UIDMetadataId AccessibilityTraitAdjustableAttributeId; + static UIDMetadataId AccessibilityTraitAllowsDirectInteractionAttributeId; + static UIDMetadataId AccessibilityTraitUpdatesFrequentlyAttributeId; + static UIDMetadataId AccessibilityTraitCausesPageTurnAttributeId; + static UIDMetadataId AccessibilityTraitPlaysSoundAttributeId; + static UIDMetadataId AccessibilityTraitStartsMediaSessionAttributeId; + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + UIViewAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"UIView"]; + FrameAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_LAYOUT + name:@"frame" + isMutable:false + definedBy:UIViewAttributeId]; + BoundsAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_LAYOUT + name:@"bounds" + isMutable:false + definedBy:UIViewAttributeId]; + CenterAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_LAYOUT + name:@"center" + isMutable:false + definedBy:UIViewAttributeId]; + AnchorPointAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_LAYOUT + name:@"anchorPoint" + isMutable:false + definedBy:UIViewAttributeId]; + SafeAreaInsetsAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_LAYOUT + name:@"safeAreaInset" + isMutable:false + definedBy:UIViewAttributeId]; + ClipsToBoundsAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_LAYOUT + name:@"clipsToBounds" + isMutable:false + definedBy:UIViewAttributeId]; + HiddenAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"hidden" + isMutable:false + definedBy:UIViewAttributeId]; + AlphaAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"alpha" + isMutable:false + definedBy:UIViewAttributeId]; + OpaqueAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"opaque" + isMutable:false + definedBy:UIViewAttributeId]; + ClearContextBeforeDrawingAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"clearContextBeforeDrawing" + isMutable:false + definedBy:UIViewAttributeId]; + BackgroundColorAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"backgroundColor" + isMutable:false + definedBy:UIViewAttributeId]; + TintColorAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"tintColor" + isMutable:false + definedBy:UIViewAttributeId]; + + TagAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"tag" + isMutable:false + definedBy:UIViewAttributeId]; + + CALayerAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"CALayer"]; + + CALayerShadowColorAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"shadowColor" + isMutable:false + definedBy:CALayerAttributeId]; + CALayerShadowOpacityAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"shadowOpacity" + isMutable:false + definedBy:CALayerAttributeId]; + CALayerShadowRadiusAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"shadowRadius" + isMutable:false + definedBy:CALayerAttributeId]; + CALayerShadowOffsetAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"shadowOffset" + isMutable:false + definedBy:CALayerAttributeId]; + CALayerBackgroundColorAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"shadowBackgroundColor" + isMutable:false + definedBy:CALayerAttributeId]; + CALayerBorderColorAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"borderColor" + isMutable:false + definedBy:CALayerAttributeId]; + CALayerBorderWidthAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"borderWidth" + isMutable:false + definedBy:CALayerAttributeId]; + CALayerCornerRadiusAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"cornerRadius" + isMutable:false + definedBy:CALayerAttributeId]; + CALayerMasksToBoundsAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"masksToBounds" + isMutable:false + definedBy:CALayerAttributeId]; + + AccessibilityAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"Accessibility"]; + + IsAccessibilityElementAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"isAccessibilityElement" + isMutable:false + definedBy:AccessibilityAttributeId]; + AccessibilityLabelAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"accessibilityLabel" + isMutable:false + definedBy:AccessibilityAttributeId]; + AccessibilityIdentifierAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"accessibilityIdentifier" + isMutable:false + definedBy:AccessibilityAttributeId]; + AccessibilityValueAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"accessibilityValue" + isMutable:false + definedBy:AccessibilityAttributeId]; + AccessibilityHintAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"accessibilityHint" + isMutable:false + definedBy:AccessibilityAttributeId]; + AccessibilityTraitsAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"accessibilityTraits" + isMutable:false + definedBy:AccessibilityAttributeId]; + AccessibilityViewIsModalAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"accessibilityViewIsModal" + isMutable:false + definedBy:AccessibilityAttributeId]; + ShouldGroupAccessibilityChildrenAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"shouldGroupAccessibilityChildren" + isMutable:false + definedBy:AccessibilityAttributeId]; + + AccessibilityTraitNoneAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"none" + isMutable:false + definedBy:AccessibilityTraitsAttributeId]; + AccessibilityTraitButtonAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"button" + isMutable:false + definedBy:AccessibilityTraitsAttributeId]; + AccessibilityTraitLinkAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"link" + isMutable:false + definedBy:AccessibilityTraitsAttributeId]; + AccessibilityTraitImageAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"image" + isMutable:false + definedBy:AccessibilityTraitsAttributeId]; + AccessibilityTraitSearchFieldAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"searchField" + isMutable:false + definedBy:AccessibilityTraitsAttributeId]; + AccessibilityTraitKeyboardKeyAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"keyboardKey" + isMutable:false + definedBy:AccessibilityTraitsAttributeId]; + AccessibilityTraitStaticTextAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"staticText" + isMutable:false + definedBy:AccessibilityTraitsAttributeId]; + AccessibilityTraitHeaderAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"header" + isMutable:false + definedBy:AccessibilityTraitsAttributeId]; + AccessibilityTraitTabBarAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"tabBar" + isMutable:false + definedBy:AccessibilityTraitsAttributeId]; + AccessibilityTraitSummaryElementAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"summaryElement" + isMutable:false + definedBy:AccessibilityTraitsAttributeId]; + AccessibilityTraitSelectedAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"selected" + isMutable:false + definedBy:AccessibilityTraitsAttributeId]; + AccessibilityTraitNotEnabledAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"notEnabled" + isMutable:false + definedBy:AccessibilityTraitsAttributeId]; + AccessibilityTraitAdjustableAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"adjustable" + isMutable:false + definedBy:AccessibilityTraitsAttributeId]; + AccessibilityTraitAllowsDirectInteractionAttributeId = + [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"directInteraction" + isMutable:false + definedBy:AccessibilityTraitsAttributeId]; + AccessibilityTraitUpdatesFrequentlyAttributeId = + [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"updatedFrequently" + isMutable:false + definedBy:AccessibilityTraitsAttributeId]; + AccessibilityTraitCausesPageTurnAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"causesPageTurn" + isMutable:false + definedBy:AccessibilityTraitsAttributeId]; + AccessibilityTraitPlaysSoundAttributeId = [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"playsSound" + isMutable:false + definedBy:AccessibilityTraitsAttributeId]; + AccessibilityTraitStartsMediaSessionAttributeId = + [[UIDMetadataRegister shared] + registerMetadataWithType:UIDEBUGGER_METADATA_TYPE_ATTRIBUTE + name:@"startsMediaSession" + isMutable:false + definedBy:AccessibilityTraitsAttributeId]; + }); + + NSMutableDictionary* viewAttributes = [NSMutableDictionary new]; + + [viewAttributes setObject:[UIDInspectableBounds fromRect:self.frame] + forKey:FrameAttributeId]; + [viewAttributes setObject:[UIDInspectableBounds fromRect:self.bounds] + forKey:BoundsAttributeId]; + [viewAttributes setObject:[UIDInspectableCoordinate fromPoint:self.center] + forKey:CenterAttributeId]; + if (@available(iOS 16.0, *)) { + [viewAttributes + setObject:[UIDInspectableCoordinate fromPoint:self.anchorPoint] + forKey:AnchorPointAttributeId]; + } + [viewAttributes + setObject:[UIDInspectableEdgeInsets fromUIEdgeInsets:self.safeAreaInsets] + forKey:SafeAreaInsetsAttributeId]; + [viewAttributes + setObject:[UIDInspectableBoolean fromBoolean:self.clipsToBounds] + forKey:ClipsToBoundsAttributeId]; + [viewAttributes setObject:[UIDInspectableBoolean fromBoolean:self.isHidden] + forKey:HiddenAttributeId]; + [viewAttributes setObject:[UIDInspectableNumber fromCGFloat:self.alpha] + forKey:AlphaAttributeId]; + [viewAttributes setObject:[UIDInspectableBoolean fromBoolean:self.opaque] + forKey:OpaqueAttributeId]; + [viewAttributes setObject:[UIDInspectableBoolean + fromBoolean:self.clearsContextBeforeDrawing] + forKey:ClearContextBeforeDrawingAttributeId]; + [viewAttributes setObject:[UIDInspectableColor fromColor:self.backgroundColor] + forKey:BackgroundColorAttributeId]; + [viewAttributes setObject:[UIDInspectableColor fromColor:self.tintColor] + forKey:TintColorAttributeId]; + [viewAttributes setObject:[UIDInspectableNumber fromNumber:@(self.tag)] + forKey:TagAttributeId]; + + [attributes setObject:[UIDInspectableObject fromFields:viewAttributes] + forKey:UIViewAttributeId]; + + NSMutableDictionary* layerAttributes = [NSMutableDictionary new]; + + [layerAttributes + setObject:[UIDInspectableColor + fromColor:[UIColor colorWithCGColor:self.layer.shadowColor]] + forKey:CALayerShadowColorAttributeId]; + [layerAttributes + setObject:[UIDInspectableNumber fromCGFloat:self.layer.shadowOpacity] + forKey:CALayerShadowOpacityAttributeId]; + [layerAttributes + setObject:[UIDInspectableNumber fromCGFloat:self.layer.shadowRadius] + forKey:CALayerShadowRadiusAttributeId]; + [layerAttributes + setObject:[UIDInspectableSize fromSize:self.layer.shadowOffset] + forKey:CALayerShadowOffsetAttributeId]; + [layerAttributes + setObject:[UIDInspectableColor + fromColor:[UIColor + colorWithCGColor:self.layer.backgroundColor]] + forKey:CALayerBackgroundColorAttributeId]; + [layerAttributes + setObject:[UIDInspectableColor + fromColor:[UIColor colorWithCGColor:self.layer.borderColor]] + forKey:CALayerBorderColorAttributeId]; + [layerAttributes + setObject:[UIDInspectableNumber fromCGFloat:self.layer.borderWidth] + forKey:CALayerBorderWidthAttributeId]; + [layerAttributes + setObject:[UIDInspectableNumber fromCGFloat:self.layer.cornerRadius] + forKey:CALayerCornerRadiusAttributeId]; + [layerAttributes + setObject:[UIDInspectableBoolean fromBoolean:self.layer.masksToBounds] + forKey:CALayerMasksToBoundsAttributeId]; + + [attributes setObject:[UIDInspectableObject fromFields:layerAttributes] + forKey:CALayerAttributeId]; + + NSMutableDictionary* accessibilityAttributes = [NSMutableDictionary new]; + + [accessibilityAttributes + setObject:[UIDInspectableBoolean fromBoolean:self.isAccessibilityElement] + forKey:IsAccessibilityElementAttributeId]; + [accessibilityAttributes + setObject:[UIDInspectableText fromText:self.accessibilityLabel] + forKey:AccessibilityLabelAttributeId]; + [accessibilityAttributes + setObject:[UIDInspectableText fromText:self.accessibilityIdentifier] + forKey:AccessibilityIdentifierAttributeId]; + [accessibilityAttributes + setObject:[UIDInspectableText fromText:self.accessibilityValue] + forKey:AccessibilityValueAttributeId]; + [accessibilityAttributes + setObject:[UIDInspectableText fromText:self.accessibilityHint] + forKey:AccessibilityHintAttributeId]; + [accessibilityAttributes + setObject:[UIDInspectableBoolean + fromBoolean:self.accessibilityViewIsModal] + forKey:AccessibilityViewIsModalAttributeId]; + [accessibilityAttributes + setObject:[UIDInspectableBoolean + fromBoolean:self.shouldGroupAccessibilityChildren] + forKey:ShouldGroupAccessibilityChildrenAttributeId]; + + NSMutableDictionary* accessibilityTraitsAttributes = + [NSMutableDictionary new]; + [accessibilityTraitsAttributes + setObject:[UIDInspectableBoolean fromBoolean:(self.accessibilityTraits & + UIAccessibilityTraitNone)] + forKey:AccessibilityTraitNoneAttributeId]; + + [accessibilityTraitsAttributes + setObject:[UIDInspectableBoolean fromBoolean:(self.accessibilityTraits & + UIAccessibilityTraitButton)] + forKey:AccessibilityTraitButtonAttributeId]; + [accessibilityTraitsAttributes + setObject:[UIDInspectableBoolean fromBoolean:(self.accessibilityTraits & + UIAccessibilityTraitLink)] + forKey:AccessibilityTraitLinkAttributeId]; + [accessibilityTraitsAttributes + setObject:[UIDInspectableBoolean fromBoolean:(self.accessibilityTraits & + UIAccessibilityTraitImage)] + forKey:AccessibilityTraitImageAttributeId]; + [accessibilityTraitsAttributes + setObject:[UIDInspectableBoolean + fromBoolean:(self.accessibilityTraits & + UIAccessibilityTraitSearchField)] + forKey:AccessibilityTraitSearchFieldAttributeId]; + [accessibilityTraitsAttributes + setObject:[UIDInspectableBoolean + fromBoolean:(self.accessibilityTraits & + UIAccessibilityTraitKeyboardKey)] + forKey:AccessibilityTraitKeyboardKeyAttributeId]; + [accessibilityTraitsAttributes + setObject:[UIDInspectableBoolean + fromBoolean:(self.accessibilityTraits & + UIAccessibilityTraitStaticText)] + forKey:AccessibilityTraitStaticTextAttributeId]; + [accessibilityTraitsAttributes + setObject:[UIDInspectableBoolean fromBoolean:(self.accessibilityTraits & + UIAccessibilityTraitHeader)] + forKey:AccessibilityTraitHeaderAttributeId]; + [accessibilityTraitsAttributes + setObject:[UIDInspectableBoolean fromBoolean:(self.accessibilityTraits & + UIAccessibilityTraitTabBar)] + forKey:AccessibilityTraitTabBarAttributeId]; + [accessibilityTraitsAttributes + setObject:[UIDInspectableBoolean + fromBoolean:(self.accessibilityTraits & + UIAccessibilityTraitSummaryElement)] + forKey:AccessibilityTraitSummaryElementAttributeId]; + [accessibilityTraitsAttributes + setObject:[UIDInspectableBoolean + fromBoolean:(self.accessibilityTraits & + UIAccessibilityTraitSelected)] + forKey:AccessibilityTraitSelectedAttributeId]; + [accessibilityTraitsAttributes + setObject:[UIDInspectableBoolean + fromBoolean:(self.accessibilityTraits & + UIAccessibilityTraitNotEnabled)] + forKey:AccessibilityTraitNotEnabledAttributeId]; + [accessibilityTraitsAttributes + setObject:[UIDInspectableBoolean + fromBoolean:(self.accessibilityTraits & + UIAccessibilityTraitAdjustable)] + forKey:AccessibilityTraitAdjustableAttributeId]; + [accessibilityTraitsAttributes + setObject:[UIDInspectableBoolean + fromBoolean:(self.accessibilityTraits & + UIAccessibilityTraitAllowsDirectInteraction)] + forKey:AccessibilityTraitAllowsDirectInteractionAttributeId]; + [accessibilityTraitsAttributes + setObject:[UIDInspectableBoolean + fromBoolean:(self.accessibilityTraits & + UIAccessibilityTraitUpdatesFrequently)] + forKey:AccessibilityTraitUpdatesFrequentlyAttributeId]; + [accessibilityTraitsAttributes + setObject:[UIDInspectableBoolean + fromBoolean:(self.accessibilityTraits & + UIAccessibilityTraitCausesPageTurn)] + forKey:AccessibilityTraitCausesPageTurnAttributeId]; + [accessibilityTraitsAttributes + setObject:[UIDInspectableBoolean + fromBoolean:(self.accessibilityTraits & + UIAccessibilityTraitPlaysSound)] + forKey:AccessibilityTraitPlaysSoundAttributeId]; + [accessibilityTraitsAttributes + setObject:[UIDInspectableBoolean + fromBoolean:(self.accessibilityTraits & + UIAccessibilityTraitStartsMediaSession)] + forKey:AccessibilityTraitStartsMediaSessionAttributeId]; + + [accessibilityAttributes + setObject:[UIDInspectableObject fromFields:accessibilityTraitsAttributes] + forKey:AccessibilityTraitsAttributeId]; + + [attributes + setObject:[UIDInspectableObject fromFields:accessibilityAttributes] + forKey:AccessibilityAttributeId]; +} + +- (NSArray>*)UID_children { + NSMutableArray* children = [NSMutableArray new]; + + // Use UIViewControllers for children which responds to a different + // view controller than their parent. + for (UIView* child in self.subviews) { + BOOL isController = + [child.nextResponder isKindOfClass:[UIViewController class]]; + + if (!child.isHidden) { + if (isController && child.nextResponder != self.nextResponder) { + [children addObject:child.nextResponder]; + } else { + [children addObject:child]; + } + } + } + + return children; +} + +/** + In the context of UIView, the active child is defined as the last view in + the subviews collection. If said item's next responder is a UIViewController, + then return this instead. + */ +- (id)UID_activeChild { + if (self.subviews && self.subviews.count > 0) { + UIView* activeChild = [self.subviews lastObject]; + BOOL isController = + [activeChild.nextResponder isKindOfClass:[UIViewController class]]; + + if (!activeChild.isHidden) { + if (isController && activeChild.nextResponder != self.nextResponder) { + return activeChild.nextResponder; + } + return activeChild; + } + } + return nil; +} + +- (UIImage*)UID_snapshot { + return UIDViewSnapshot(self); +} + +- (UIDBounds*)UID_bounds { + if ([self.superview isKindOfClass:[UIScrollView class]]) { + UIScrollView* parent = (UIScrollView*)self.superview; + + CGFloat x = self.frame.origin.x - parent.contentOffset.x; + CGFloat y = self.frame.origin.y - parent.contentOffset.y; + CGFloat width = self.frame.size.width; + CGFloat height = self.frame.size.height; + + return [UIDBounds fromRect:CGRectMake(x, y, width, height)]; + } + + return [UIDBounds fromRect:self.frame]; +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Events/UIDInitEvent.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Events/UIDInitEvent.h new file mode 100644 index 000000000..a05f78d27 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Events/UIDInitEvent.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import + +NS_ASSUME_NONNULL_BEGIN + +@class UIDFrameworkEventMetadata; + +@interface UIDInitEvent : NSObject + +@property(nonatomic) NSUInteger rootId; +@property(nonatomic, strong) + NSArray* frameworkEventMetadata; + ++ (NSString*)name; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Events/UIDInitEvent.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Events/UIDInitEvent.m new file mode 100644 index 000000000..f396c2aad --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Events/UIDInitEvent.m @@ -0,0 +1,20 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "UIDInitEvent.h" + +@implementation UIDInitEvent + ++ (NSString*)name { + return @"init"; +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Events/UIDMetadataUpdateEvent.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Events/UIDMetadataUpdateEvent.h new file mode 100644 index 000000000..1234a937d --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Events/UIDMetadataUpdateEvent.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import + +NS_ASSUME_NONNULL_BEGIN + +@class UIDMetadata; + +@interface UIDMetadataUpdateEvent : NSObject + +@property(nonatomic, strong) + NSDictionary* attributeMetadata; + ++ (NSString*)name; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Events/UIDMetadataUpdateEvent.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Events/UIDMetadataUpdateEvent.m new file mode 100644 index 000000000..a78462523 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Events/UIDMetadataUpdateEvent.m @@ -0,0 +1,20 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "UIDMetadataUpdateEvent.h" + +@implementation UIDMetadataUpdateEvent + ++ (NSString*)name { + return @"metadataUpdate"; +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Events/UIDPerfStatsEvent.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Events/UIDPerfStatsEvent.h new file mode 100644 index 000000000..cf0dc5854 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Events/UIDPerfStatsEvent.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface UIDPerfStatsEvent : NSObject + +@property(nonatomic) double txId; +@property(nonatomic, strong) NSString* observerType; +@property(nonatomic) NSUInteger nodesCount; + +@property(nonatomic) long start; +@property(nonatomic) long traversalMS; +@property(nonatomic) long snapshotMS; +@property(nonatomic) long queuingMS; +@property(nonatomic) long deferredComputationMS; +@property(nonatomic) long serializationMS; +@property(nonatomic) long socketMS; +@property(nonatomic) long frameworkEventsMS; +@property(nonatomic) long payloadSize; +@property(nonatomic) NSUInteger eventsCount; +@property(nonatomic) NSDictionary* dynamicMeasures; + ++ (NSString*)name; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Events/UIDPerfStatsEvent.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Events/UIDPerfStatsEvent.m new file mode 100644 index 000000000..5ddf4ee8d --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Events/UIDPerfStatsEvent.m @@ -0,0 +1,20 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "UIDPerfStatsEvent.h" + +@implementation UIDPerfStatsEvent + ++ (NSString*)name { + return @"performanceStats"; +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Events/UIDSubtreeUpdateEvent.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Events/UIDSubtreeUpdateEvent.h new file mode 100644 index 000000000..03447dc72 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Events/UIDSubtreeUpdateEvent.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import +#import "UIDFrameworkEvent.h" +#import "UIDNode.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface UIDSubtreeUpdateEvent : NSObject + +@property(nonatomic) double txId; +@property(nonatomic, strong) NSString* observerType; +@property(nonatomic) NSUInteger rootId; +@property(nonatomic, strong) NSArray* nodes; +@property(nonatomic, strong) UIImage* snapshot; +@property(nonatomic, strong) NSArray* frameworkEvents; + ++ (NSString*)name; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Events/UIDSubtreeUpdateEvent.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Events/UIDSubtreeUpdateEvent.m new file mode 100644 index 000000000..d112d5adf --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Events/UIDSubtreeUpdateEvent.m @@ -0,0 +1,20 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "UIDSubtreeUpdateEvent.h" + +@implementation UIDSubtreeUpdateEvent + ++ (NSString*)name { + return @"subtreeUpdate"; +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin.h new file mode 100644 index 000000000..7c7bcb1e8 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import +#import + +@class FlipperClient; + +@interface FlipperKitUIDebuggerPlugin : NSObject + +@end + +FB_EXTERN_C_BEGIN + +void FlipperKitUIDebuggerAddPlugin(FlipperClient*); + +FB_EXTERN_C_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin.mm b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin.mm new file mode 100644 index 000000000..47e02dfe4 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin.mm @@ -0,0 +1,85 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "FlipperKitUIDebuggerPlugin.h" +#import + +#import +#import +#import + +#import "Core/UIDContext.h" +#import "PluginSockets.h" +#import "Plugins.h" + +#import "Descriptors/UIDDescriptorRegister.h" +#import "Observer/UIDTreeObserverFactory.h" +#import "Observer/UIDTreeObserverManager.h" + +@implementation FlipperKitUIDebuggerPlugin { + UIDContext* _context; +} + +- (instancetype)initWithContext:(UIDContext*)context { + if (self = [super init]) { + self->_context = context; + } + return self; +} + +- (instancetype)init { + UIDContext* context = [[UIDContext alloc] + initWithApplication:[UIApplication sharedApplication] + descriptorRegister:[UIDDescriptorRegister defaultRegister] + observerFactory:[UIDTreeObserverFactory shared]]; + return [self initWithContext:context]; +} + +- (NSString*)identifier { + return @"ui-debugger"; +} + +- (void)didConnect:(id)connection { + if (!_context.application) { + _context.application = [UIApplication sharedApplication]; + } + + _context.connection = connection; + + [[UIDTreeObserverManager shared] startWithContext:_context]; +} + +- (void)didDisconnect { + _context.connection = nil; + [[UIDTreeObserverManager shared] stop]; +} + +@end + +void FlipperKitUIDebuggerAddPlugin(FlipperClient* client) { + UIDContext* context = [[UIDContext alloc] + initWithApplication:[UIApplication sharedApplication] + descriptorRegister:[UIDDescriptorRegister defaultRegister] + observerFactory:[UIDTreeObserverFactory shared]]; + + FlipperKitUIDebuggerPlugin* plugin = + [[FlipperKitUIDebuggerPlugin alloc] initWithContext:context]; + + for (const auto& p : FlipperUIDebuggerDescriptorRegisterSocket_Plugins()) { + FlipperUIDebuggerDescriptorRegisterSocket_InvokeConfigure(p, context); + } + + [client addPlugin:plugin]; +} + +void FlipperKitUIDebuggerPluginInit(FlipperClient* client) { + FlipperKitUIDebuggerAddPlugin(client); +} + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDBounds.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDBounds.h new file mode 100644 index 000000000..49e5c63d4 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDBounds.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface UIDBounds : NSObject + +@property(nonatomic) NSInteger x; +@property(nonatomic) NSInteger y; +@property(nonatomic) NSInteger width; +@property(nonatomic) NSInteger height; + +- (instancetype)initWithX:(NSInteger)x + y:(NSInteger)y + width:(NSInteger)width + height:(NSInteger)height; + ++ (instancetype)fromRect:(CGRect)rect; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDBounds.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDBounds.m new file mode 100644 index 000000000..1beb1354a --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDBounds.m @@ -0,0 +1,37 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "UIDBounds.h" + +@implementation UIDBounds + +- (instancetype)initWithX:(NSInteger)x + y:(NSInteger)y + width:(NSInteger)width + height:(NSInteger)height { + self = [super init]; + if (self) { + self.x = x; + self.y = y; + self.width = width; + self.height = height; + } + return self; +} + ++ (instancetype)fromRect:(CGRect)rect { + return [[UIDBounds alloc] initWithX:rect.origin.x + y:rect.origin.y + width:rect.size.width + height:rect.size.height]; +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDEdgeInsets.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDEdgeInsets.h new file mode 100644 index 000000000..3a2fbc438 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDEdgeInsets.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface UIDEdgeInsets : NSObject + +@property(nonatomic) CGFloat top; +@property(nonatomic) CGFloat right; +@property(nonatomic) CGFloat bottom; +@property(nonatomic) CGFloat left; + +- (instancetype)initWithTop:(CGFloat)top + right:(CGFloat)right + bottom:(CGFloat)bottom + left:(CGFloat)left; + ++ (instancetype)fromUIEdgeInsets:(UIEdgeInsets)edgeInsets; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDEdgeInsets.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDEdgeInsets.m new file mode 100644 index 000000000..4176e0e47 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDEdgeInsets.m @@ -0,0 +1,37 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "UIDEdgeInsets.h" + +@implementation UIDEdgeInsets + +- (instancetype)initWithTop:(CGFloat)top + right:(CGFloat)right + bottom:(CGFloat)bottom + left:(CGFloat)left { + self = [super init]; + if (self) { + _top = top; + _right = right; + _bottom = bottom; + _left = left; + } + return self; +} + ++ (instancetype)fromUIEdgeInsets:(UIEdgeInsets)edgeInsets { + return [[UIDEdgeInsets alloc] initWithTop:edgeInsets.top + right:edgeInsets.right + bottom:edgeInsets.bottom + left:edgeInsets.left]; +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDFrameworkEvent.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDFrameworkEvent.h new file mode 100644 index 000000000..87d85de95 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDFrameworkEvent.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import + +NS_ASSUME_NONNULL_BEGIN + +typedef NSDictionary* UIDEventPayload; +typedef NSArray* UIDStacktrace; + +@interface UIDFrameworkEvent : NSObject + +@property(nonatomic) NSUInteger nodeIdentifier; +@property(nonatomic, strong) NSString* type; +@property(nonatomic, strong) NSDate* timestamp; +@property(nonatomic, strong) UIDEventPayload payload; +@property(nonatomic, strong) UIDStacktrace stacktrace; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDFrameworkEvent.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDFrameworkEvent.m new file mode 100644 index 000000000..fd27271e7 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDFrameworkEvent.m @@ -0,0 +1,16 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "UIDFrameworkEvent.h" + +@implementation UIDFrameworkEvent + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDFrameworkEventMetadata.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDFrameworkEventMetadata.h new file mode 100644 index 000000000..7614dd625 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDFrameworkEventMetadata.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface UIDFrameworkEventMetadata : NSObject + +@property(nonatomic, strong) NSString* type; +@property(nonatomic, strong) NSString* documentation; + ++ (instancetype)newWithType:(NSString*)type + documentation:(NSString*)documentation; + ++ (instancetype)new NS_UNAVAILABLE; + +- (instancetype)init NS_UNAVAILABLE; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDFrameworkEventMetadata.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDFrameworkEventMetadata.m new file mode 100644 index 000000000..e1bdd307c --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDFrameworkEventMetadata.m @@ -0,0 +1,30 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "UIDFrameworkEventMetadata.h" + +@implementation UIDFrameworkEventMetadata + +- (instancetype)initWithType:(NSString*)type + documentation:(NSString*)documentation { + if (self = [super init]) { + self->_type = type; + self->_documentation = documentation; + } + return self; +} + ++ (instancetype)newWithType:(NSString*)type + documentation:(NSString*)documentation { + return [[self alloc] initWithType:type documentation:documentation]; +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDInspectable.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDInspectable.h new file mode 100644 index 000000000..60ef77a5e --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDInspectable.h @@ -0,0 +1,175 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import +#import "UIDFoundation.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface UIDInspectable : NSObject +@end + +/** + Lazy inspectables can be used to defer materialisation + of the inspectable until a later stage, like for example, + during serialisation. + */ +@interface UIDLazyInspectable : UIDInspectable + +- (UIDInspectable*)value; ++ (instancetype)from:(UIDInspectable* (^)(void))loader; + +@end + +@interface UIDInspectableValue : UIDInspectable + ++ (instancetype)createWithText:(NSString*)text; ++ (instancetype)createWithBoolean:(bool)boolean; ++ (instancetype)createWithNumber:(NSNumber*)number; + +@end + +@interface UIDInspectableObject : UIDInspectable + +@property(nonatomic, strong, readonly) + NSDictionary* fields; + +- (instancetype)initWithFields: + (NSDictionary*)fields; + ++ (instancetype)fromFields:(NSDictionary*)fields; + +@end + +@interface UIDInspectableArray : UIDInspectable + +@property(nonatomic, strong, readonly) NSArray* items; + +- (instancetype)initWithItems:(NSArray*)items; + ++ (instancetype)fromItems:(NSArray*)items; + +@end + +@interface UIDInspectableText : UIDInspectableValue + +@property(nonatomic, strong, readonly) NSString* value; + +- (instancetype)initWithValue:(NSString*)value; + ++ (instancetype)fromText:(NSString*)value; + +@end + +@interface UIDInspectableBoolean : UIDInspectableValue + +@property(nonatomic, readonly) bool value; + +- (instancetype)initWithValue:(bool)value; + ++ (instancetype)fromBoolean:(bool)value; + +@end + +@interface UIDInspectableNumber : UIDInspectableValue + +@property(nonatomic, strong, readonly) NSNumber* value; + +- (instancetype)initWithValue:(NSNumber*)value; + ++ (instancetype)fromNumber:(NSNumber*)value; ++ (instancetype)fromCGFloat:(CGFloat)value; + +@end + +@class UIDBounds; +@interface UIDInspectableBounds : UIDInspectableValue + +@property(nonatomic, strong, readonly) UIDBounds* value; + +- (instancetype)initWithValue:(UIDBounds*)value; + ++ (instancetype)fromBounds:(UIDBounds*)value; ++ (instancetype)fromRect:(CGRect)rect; + +@end + +@interface UIDInspectableCoordinate : UIDInspectableValue + +@property(nonatomic, readonly) CGPoint value; + +- (instancetype)initWithValue:(CGPoint)value; + ++ (instancetype)fromPoint:(CGPoint)value; + +@end + +@interface UIDInspectableSize : UIDInspectableValue + +@property(nonatomic, readonly) CGSize value; + +- (instancetype)initWithValue:(CGSize)value; + ++ (instancetype)fromSize:(CGSize)value; + +@end + +@interface UIDInspectableUnknown : UIDInspectableValue + +@property(nonatomic, readonly) NSString* value; + +- (instancetype)initWithValue:(NSString*)value; + ++ (instancetype)unknown; ++ (instancetype)undefined; ++ (instancetype)null; + +@end + +@class UIDEdgeInsets; +@interface UIDInspectableEdgeInsets : UIDInspectableValue + +@property(nonatomic, readonly) UIDEdgeInsets* value; + +- (instancetype)initWithValue:(UIDEdgeInsets*)value; + ++ (instancetype)fromEdgeInsets:(UIDEdgeInsets*)value; ++ (instancetype)fromUIEdgeInsets:(UIEdgeInsets)value; + +@end + +@interface UIDInspectableColor : UIDInspectableValue + +@property(nonatomic, strong, readonly) UIColor* value; + +- (instancetype)initWithValue:(UIColor*)value; + ++ (instancetype)fromColor:(UIColor*)value; + +@end + +@interface UIDInspectableEnum : UIDInspectableValue + +@property(nonatomic, readonly) NSString* value; + +- (instancetype)initWithValue:(NSString*)value; + ++ (instancetype)from:(NSString*)value; + +@end + +typedef NSDictionary UIDAttributes; +typedef NSMutableDictionary UIDMutableAttributes; +typedef NSDictionary UIDInlineAttributes; +typedef NSMutableDictionary UIDMutableInlineAttributes; +typedef NSDictionary> UIDGenericAttributes; + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDInspectable.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDInspectable.m new file mode 100644 index 000000000..34a17ff33 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDInspectable.m @@ -0,0 +1,275 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "UIDInspectable.h" +#import "UIDBounds.h" +#import "UIDEdgeInsets.h" + +@implementation UIDInspectable +@end + +@interface UIDLazyInspectable () { + UIDInspectable* _value; + UIDInspectable* (^_loader)(void); +} + +@end + +@implementation UIDLazyInspectable +- (instancetype)initWithLoader:(UIDInspectable* (^)(void))loader { + if (self = [super init]) { + self->_value = nil; + self->_loader = loader; + } + return self; +} + +- (UIDInspectable*)value { + if (!_value && _loader) { + _value = _loader(); + } + return _value; +} + ++ (instancetype)from:(UIDInspectable* (^)(void))loader { + return [[self alloc] initWithLoader:loader]; +} + +@end + +@implementation UIDInspectableObject + +- (instancetype)initWithFields: + (NSDictionary*)fields { + self = [super init]; + if (self) { + _fields = fields; + } + return self; +} + ++ (instancetype)fromFields:(NSDictionary*)fields { + return [[UIDInspectableObject alloc] initWithFields:fields]; +} + +@end + +@implementation UIDInspectableArray + +- (instancetype)initWithItems:(NSArray*)items { + self = [super init]; + if (self) { + _items = items; + } + return self; +} + ++ (instancetype)fromItems:(NSArray*)items { + return [[UIDInspectableArray alloc] initWithItems:items]; +} + +@end + +@implementation UIDInspectableValue + ++ (instancetype)createWithText:(NSString*)text { + return [[UIDInspectableText alloc] initWithValue:text]; +} + ++ (instancetype)createWithBoolean:(bool)boolean { + return [[UIDInspectableBoolean alloc] initWithValue:boolean]; +} + ++ (instancetype)createWithNumber:(NSNumber*)number { + return [[UIDInspectableNumber alloc] initWithValue:number]; +} + +@end + +@implementation UIDInspectableText + +- (instancetype)initWithValue:(NSString*)value { + self = [super init]; + if (self) { + _value = value; + } + return self; +} + ++ (instancetype)fromText:(NSString*)value { + return [[UIDInspectableText alloc] initWithValue:value]; +} + +@end + +@implementation UIDInspectableBoolean + +- (instancetype)initWithValue:(bool)value { + self = [super init]; + if (self) { + _value = value; + } + return self; +} + ++ (instancetype)fromBoolean:(bool)value { + return [[UIDInspectableBoolean alloc] initWithValue:value]; +} + +@end + +@implementation UIDInspectableNumber + +- (instancetype)initWithValue:(NSNumber*)value { + self = [super init]; + if (self) { + _value = value; + } + return self; +} + ++ (instancetype)fromNumber:(NSNumber*)value { + return [[UIDInspectableNumber alloc] initWithValue:value]; +} + ++ (instancetype)fromCGFloat:(CGFloat)value { + return [self fromNumber:[NSNumber numberWithFloat:value]]; +} + +@end + +@implementation UIDInspectableBounds + +- (instancetype)initWithValue:(UIDBounds*)value { + self = [super init]; + if (self) { + _value = value; + } + return self; +} + ++ (instancetype)fromBounds:(UIDBounds*)bounds { + return [[UIDInspectableBounds alloc] initWithValue:bounds]; +} + ++ (instancetype)fromRect:(CGRect)rect { + return [self fromBounds:[UIDBounds fromRect:rect]]; +} + +@end + +@implementation UIDInspectableCoordinate + +- (instancetype)initWithValue:(CGPoint)value { + self = [super init]; + if (self) { + _value = value; + } + return self; +} + ++ (instancetype)fromPoint:(CGPoint)value { + return [[UIDInspectableCoordinate alloc] initWithValue:value]; +} + +@end + +@implementation UIDInspectableSize + +- (instancetype)initWithValue:(CGSize)value { + self = [super init]; + if (self) { + _value = value; + } + return self; +} + ++ (instancetype)fromSize:(CGSize)value { + return [[UIDInspectableSize alloc] initWithValue:value]; +} + +@end + +@implementation UIDInspectableEdgeInsets + +- (instancetype)initWithValue:(UIDEdgeInsets*)value { + self = [super init]; + if (self) { + _value = value; + } + return self; +} + ++ (instancetype)fromEdgeInsets:(UIDEdgeInsets*)value { + return [[UIDInspectableEdgeInsets alloc] initWithValue:value]; +} + ++ (instancetype)fromUIEdgeInsets:(UIEdgeInsets)value { + return [self fromEdgeInsets:[UIDEdgeInsets fromUIEdgeInsets:value]]; +} + +@end + +@implementation UIDInspectableColor + +- (instancetype)initWithValue:(UIColor*)value { + self = [super init]; + if (self) { + _value = value; + } + return self; +} + ++ (instancetype)fromColor:(UIColor*)value { + return [[UIDInspectableColor alloc] initWithValue:value]; +} + +@end + +@implementation UIDInspectableUnknown + +- (instancetype)initWithValue:(NSString*)value { + self = [super init]; + if (self) { + _value = value; + } + return self; +} + ++ (instancetype)unknown { + return [[UIDInspectableUnknown alloc] initWithValue:@"unknown"]; +} + ++ (instancetype)undefined { + return [[UIDInspectableUnknown alloc] initWithValue:@"undefined"]; +} + ++ (instancetype)null { + return [[UIDInspectableUnknown alloc] initWithValue:@"null"]; +} + +@end + +@implementation UIDInspectableEnum + +- (instancetype)initWithValue:(NSString*)value { + self = [super init]; + if (self) { + _value = value; + } + return self; +} + ++ (instancetype)from:(NSString*)value { + return [[UIDInspectableEnum alloc] initWithValue:value ?: @"UNKNOWN"]; +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDMetadata.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDMetadata.h new file mode 100644 index 000000000..e1c46b220 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDMetadata.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import + +NS_ASSUME_NONNULL_BEGIN + +typedef NSNumber* UIDMetadataId; + +@class UIDInspectableValue; + +/** + Represent metadata associated for an attribute. Metadata identifier is a + unique identifier used by attributes to refer to its metadata. Type refers to + attribute semantics. It can represent: identity, attributes, layout, + documentation, or a custom type. + */ +@interface UIDMetadata : NSObject + +@property(nonatomic, readonly) UIDMetadataId identifier; +@property(nonatomic, strong, readonly) NSString* type; +@property(nonatomic, readonly) UIDMetadataId parent; +@property(nonatomic, strong, readonly) NSString* name; +@property(nonatomic, readonly) bool isMutable; +@property(nonatomic, strong, readonly) + NSSet* possibleValues; +@property(nonatomic, strong, readonly) NSSet* tags; + +- (instancetype)initWithIdentifier:(UIDMetadataId)identifier + type:(NSString*)type + name:(NSString*)name; + +- (instancetype)initWithIdentifier:(UIDMetadataId)identifier + type:(NSString*)type + name:(NSString*)name + isMutable:(bool)isMutable + parent:(UIDMetadataId)parent; + +- (instancetype)initWithIdentifier:(UIDMetadataId)identifier + type:(NSString*)type + name:(NSString*)name + isMutable:(bool)isMutable + parent:(UIDMetadataId)parent + possibleValues:(NSSet*)possibleValues + tags:(NSSet*)tags; +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDMetadata.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDMetadata.m new file mode 100644 index 000000000..81bc2afdb --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDMetadata.m @@ -0,0 +1,63 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "UIDMetadata.h" +#import "UIDInspectable.h" + +@implementation UIDMetadata + +- (instancetype)initWithIdentifier:(UIDMetadataId)identifier + type:(NSString*)type + name:(NSString*)name { + return [self initWithIdentifier:identifier + type:type + name:name + isMutable:false + parent:@0 + possibleValues:[NSSet set] + tags:[NSSet set]]; +} + +- (instancetype)initWithIdentifier:(UIDMetadataId)identifier + type:(NSString*)type + name:(NSString*)name + isMutable:(bool)isMutable + parent:(UIDMetadataId)parent { + return [self initWithIdentifier:identifier + type:type + name:name + isMutable:isMutable + parent:parent + possibleValues:[NSSet set] + tags:[NSSet set]]; +} + +- (instancetype)initWithIdentifier:(UIDMetadataId)identifier + type:(NSString*)type + name:(NSString*)name + isMutable:(bool)isMutable + parent:(UIDMetadataId)parent + possibleValues:(NSSet*)possibleValues + tags:(NSSet*)tags { + self = [super init]; + if (self) { + _identifier = identifier; + _type = type; + _name = name; + _isMutable = isMutable; + _parent = parent; + _possibleValues = possibleValues; + _tags = tags; + } + return self; +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDNode.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDNode.h new file mode 100644 index 000000000..24ebf346d --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDNode.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import +#import "UIDBounds.h" +#import "UIDInspectable.h" +#import "UIDMetadata.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface UIDNode : NSObject + +@property(nonatomic) NSUInteger identifier; +@property(nonatomic, strong) NSString* qualifiedName; +@property(nonatomic, strong) NSString* name; +@property(nonatomic, strong) UIDBounds* bounds; +@property(nonatomic, strong) NSSet* tags; +@property(nonatomic, strong) UIDAttributes* attributes; +@property(nonatomic, strong) UIDInlineAttributes* inlineAttributes; +@property(nonatomic, strong) UIDGenericAttributes* hiddenAttributes; +@property(nonatomic, nullable) NSNumber* parent; +@property(nonatomic, strong) NSArray* children; +@property(nonatomic) NSNumber* activeChild; + +- (instancetype)initWithIdentifier:(NSUInteger)identifier + qualifiedName:(NSString*)qualifiedName + name:(NSString*)name + bounds:(UIDBounds*)bounds + tags:(NSSet*)tags; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDNode.mm b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDNode.mm new file mode 100644 index 000000000..08a450c5c --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Model/UIDNode.mm @@ -0,0 +1,36 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "UIDNode.h" + +@implementation UIDNode + +- (instancetype)initWithIdentifier:(NSUInteger)identifier + qualifiedName:(NSString*)qualifiedName + name:(NSString*)name + bounds:(UIDBounds*)bounds + tags:(NSSet*)tags { + self = [super init]; + if (self) { + self.identifier = identifier; + self.qualifiedName = qualifiedName; + self.name = name; + self.bounds = bounds; + self.tags = tags; + self.parent = nil; + self.children = [NSArray array]; + self.attributes = [NSDictionary dictionary]; + } + + return self; +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDTreeObserver.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDTreeObserver.h new file mode 100644 index 000000000..cc9441b70 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDTreeObserver.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import + +NS_ASSUME_NONNULL_BEGIN + +@class UIDContext; + +@interface UIDTreeObserver<__covariant T> : NSObject + +@property(nonatomic, strong) + NSMutableDictionary*>* children; +@property(nonatomic, strong) NSString* type; + +- (void)subscribe:(nullable T)node; +- (void)unsubscribe; +- (void)processNode:(id)node withContext:(UIDContext*)context; +- (void)processNode:(id)node + withSnapshot:(BOOL)snapshot + withContext:(UIDContext*)context; +- (void)cleanup; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDTreeObserver.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDTreeObserver.m new file mode 100644 index 000000000..2acbf4765 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDTreeObserver.m @@ -0,0 +1,75 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "UIDTreeObserver.h" +#import "UIDContext.h" +#import "UIDHierarchyTraversal.h" +#import "UIDTimeUtilities.h" + +@implementation UIDTreeObserver + +- (instancetype)init { + self = [super init]; + if (self) { + _children = [NSMutableDictionary new]; + } + return self; +} + +- (void)subscribe:(id)node { +} + +- (void)unsubscribe { +} + +- (void)processNode:(id)node withContext:(UIDContext*)context { + [self processNode:node withSnapshot:false withContext:context]; +} + +- (void)processNode:(id)node + withSnapshot:(BOOL)snapshot + withContext:(UIDContext*)context { + NSTimeInterval timestamp = UIDPerformanceTimeIntervalSince1970(); + + uint64_t t0 = UIDPerformanceNow(); + + UIDHierarchyTraversal* traversal = [UIDHierarchyTraversal + createWithDescriptorRegister:context.descriptorRegister]; + + UIDNodeDescriptor* descriptor = + [context.descriptorRegister descriptorForClass:[node class]]; + UIDNodeDescriptor* rootDescriptor = [context.descriptorRegister + descriptorForClass:[context.application class]]; + + NSArray* nodes = [traversal traverse:node]; + + uint64_t t1 = UIDPerformanceNow(); + + UIImage* screenshot = [rootDescriptor snapshotForNode:context.application]; + + uint64_t t2 = UIDPerformanceNow(); + + UIDSubtreeUpdate* update = [UIDSubtreeUpdate new]; + update.observerType = _type; + update.rootId = [descriptor identifierForNode:node]; + update.nodes = nodes; + update.snapshot = screenshot; + update.timestamp = timestamp; + update.traversalMS = UIDMonotonicTimeConvertMachUnitsToMS(t1 - t0); + update.snapshotMS = UIDMonotonicTimeConvertMachUnitsToMS(t2 - t1); + + [context.updateDigester digest:update]; +} + +- (void)cleanup { +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDTreeObserverBuilder.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDTreeObserverBuilder.h new file mode 100644 index 000000000..ccb283f6b --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDTreeObserverBuilder.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import +#import "UIDTreeObserver.h" + +@class UIDContext; + +/** + UIDTreeObserverBuilder, as the name suggests, can create an observers for a + node. Basically, observer builders are registered in the factory. Then, when + an observer is needed for a node, builders are asked whether they can create + an observer for it. If it can, then it will create an observer for it. + */ +@protocol UIDTreeObserverBuilder + +- (BOOL)canBuildFor:(id)node; +- (UIDTreeObserver*)buildWithContext:(UIDContext*)context; + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDTreeObserverFactory.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDTreeObserverFactory.h new file mode 100644 index 000000000..539f9e152 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDTreeObserverFactory.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import +#import "UIDTreeObserverBuilder.h" + +NS_ASSUME_NONNULL_BEGIN + +@class UIDContext; + +/** + Factory of TreeObserver. It allows registration of different builders which + are the ones responsible of actually building observes for nodes. + */ +@interface UIDTreeObserverFactory : NSObject + ++ (instancetype)shared; + +- (void)registerBuilder:(id)builder; +- (UIDTreeObserver*)createObserverForNode:(id)node + withContext:(UIDContext*)context; +- (BOOL)hasObserverForNode:(id)node; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDTreeObserverFactory.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDTreeObserverFactory.m new file mode 100644 index 000000000..83a47659b --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDTreeObserverFactory.m @@ -0,0 +1,66 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "UIDTreeObserverFactory.h" +#import "UIDUIApplicationObserver.h" + +@interface UIDTreeObserverFactory () { + NSMutableArray>* _builders; +} + +@end + +@implementation UIDTreeObserverFactory + +- (instancetype)init { + self = [super init]; + if (self) { + _builders = [NSMutableArray new]; + } + return self; +} + ++ (instancetype)shared { + static UIDTreeObserverFactory* factory = nil; + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + factory = [UIDTreeObserverFactory new]; + [factory registerBuilder:[UIDUIApplicationObserverBuilder new]]; + }); + + return factory; +} + +- (void)registerBuilder:(id)builder { + [_builders addObject:builder]; +} + +- (UIDTreeObserver*)createObserverForNode:(id)node + withContext:(UIDContext*)context { + for (id builder in _builders) { + if ([builder canBuildFor:node]) { + return [builder buildWithContext:context]; + } + } + return nil; +} + +- (BOOL)hasObserverForNode:(id)node { + for (id builder in _builders) { + if ([builder canBuildFor:node]) { + return true; + } + } + return false; +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDTreeObserverManager.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDTreeObserverManager.h new file mode 100644 index 000000000..f4e20a7b4 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDTreeObserverManager.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import + +NS_ASSUME_NONNULL_BEGIN + +@class UIDContext; + +@interface UIDTreeObserverManager : NSObject + ++ (instancetype)shared; + +- (void)startWithContext:(UIDContext*)context; +- (void)stop; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDTreeObserverManager.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDTreeObserverManager.m new file mode 100644 index 000000000..1c3cd7567 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDTreeObserverManager.m @@ -0,0 +1,167 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "UIDTreeObserverManager.h" +#import "UIDContext.h" +#import "UIDDescriptorRegister.h" +#import "UIDInitEvent.h" +#import "UIDJSONSerializer.h" +#import "UIDMetadataRegister.h" +#import "UIDMetadataUpdateEvent.h" +#import "UIDPerfStatsEvent.h" +#import "UIDPerformance.h" +#import "UIDSubtreeUpdateEvent.h" +#import "UIDTimeUtilities.h" +#import "UIDTreeObserverFactory.h" + +@interface UIDTreeObserverManager () { + dispatch_queue_t _queue; + UIDTreeObserver* _rootObserver; + + UIDContext* _context; +} + +@property(nonatomic, strong) UIDTreeObserver* viewObserver; + +@end + +@implementation UIDTreeObserverManager + +- (instancetype)init { + self = [super init]; + if (self) { + _queue = + dispatch_queue_create("ui-debugger.background", DISPATCH_QUEUE_SERIAL); + _context = nil; + } + return self; +} + ++ (instancetype)shared { + static UIDTreeObserverManager* instance = nil; + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + instance = [UIDTreeObserverManager new]; + }); + + return instance; +} + +- (void)startWithContext:(UIDContext*)context { + _context = context; + _context.updateDigester = self; + + [self sendInit]; + + if (!_rootObserver) { + _rootObserver = + [_context.observerFactory createObserverForNode:context.application + withContext:_context]; + } + + [_rootObserver subscribe:_context.application]; + + [_context.frameworkEventManager enable]; +} + +- (void)stop { + [_rootObserver unsubscribe]; + + [[UIDMetadataRegister shared] reset]; + + [_context.frameworkEventManager disable]; +} + +- (void)sendInit { + UIDNodeDescriptor* descriptor = + [_context.descriptorRegister descriptorForClass:[UIApplication class]]; + + UIDInitEvent* init = [UIDInitEvent new]; + init.rootId = [descriptor identifierForNode:_context.application]; + init.frameworkEventMetadata = [_context.frameworkEventManager eventsMetadata]; + + [_context.connection send:[UIDInitEvent name] withRawParams:UID_toJSON(init)]; +} + +- (void)sendMetadataUpdate { + NSDictionary* pendingMetadata = + [[UIDMetadataRegister shared] extractPendingMetadata]; + if (![pendingMetadata count]) { + return; + } + + UIDMetadataUpdateEvent* metadataUpdateEvent = [UIDMetadataUpdateEvent new]; + metadataUpdateEvent.attributeMetadata = pendingMetadata; + + id JSON = UID_toJSON(metadataUpdateEvent); + + [_context.connection send:[UIDMetadataUpdateEvent name] withRawParams:JSON]; +} + +- (void)digest:(nonnull id)update { + uint64_t t0 = UIDPerformanceNow(); + dispatch_async(_queue, ^{ + uint64_t t1 = UIDPerformanceNow(); + + UIDPerformanceClear(); + + UIDSubtreeUpdate* subtreeUpdate = (UIDSubtreeUpdate*)update; + + UIDSubtreeUpdateEvent* subtreeUpdateEvent = [UIDSubtreeUpdateEvent new]; + subtreeUpdateEvent.txId = UIDPerformanceTimeIntervalSince1970(); + subtreeUpdateEvent.observerType = subtreeUpdate.observerType; + subtreeUpdateEvent.rootId = subtreeUpdate.rootId; + subtreeUpdateEvent.nodes = subtreeUpdate.nodes; + subtreeUpdateEvent.snapshot = subtreeUpdate.snapshot; + subtreeUpdateEvent.frameworkEvents = + [self->_context.frameworkEventManager events]; + + uint64_t t2 = UIDPerformanceNow(); + + id intermediate = UID_toFoundation(subtreeUpdateEvent); + + uint64_t t3 = UIDPerformanceNow(); + + NSString* JSON = UID_FoundationtoJSON(intermediate); + uint64_t t4 = UIDPerformanceNow(); + NSUInteger payloadSize = JSON.length; + + [self sendMetadataUpdate]; + + [self->_context.connection send:[UIDSubtreeUpdateEvent name] + withRawParams:JSON]; + + uint64_t t5 = UIDPerformanceNow(); + + UIDPerfStatsEvent* perfStats = [UIDPerfStatsEvent new]; + perfStats.txId = subtreeUpdateEvent.txId; + perfStats.observerType = subtreeUpdate.observerType; + perfStats.nodesCount = subtreeUpdate.nodes.count; + perfStats.eventsCount = subtreeUpdateEvent.frameworkEvents.count; + perfStats.start = UIDTimeIntervalToMS(subtreeUpdate.timestamp); + perfStats.traversalMS = subtreeUpdate.traversalMS; + perfStats.snapshotMS = subtreeUpdate.snapshotMS; + perfStats.queuingMS = UIDMonotonicTimeConvertMachUnitsToMS(t1 - t0); + perfStats.frameworkEventsMS = UIDMonotonicTimeConvertMachUnitsToMS(t2 - t1); + perfStats.deferredComputationMS = + UIDMonotonicTimeConvertMachUnitsToMS(t3 - t2); + perfStats.serializationMS = UIDMonotonicTimeConvertMachUnitsToMS(t4 - t3); + perfStats.socketMS = UIDMonotonicTimeConvertMachUnitsToMS(t5 - t4); + perfStats.payloadSize = payloadSize; + perfStats.dynamicMeasures = UIDPerformanceGet(); + + [self->_context.connection send:[UIDPerfStatsEvent name] + withRawParams:UID_toJSON(perfStats)]; + }); +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDUIApplicationObserver.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDUIApplicationObserver.h new file mode 100644 index 000000000..656087589 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDUIApplicationObserver.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import +#import "UIDTreeObserverBuilder.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface UIDUIApplicationObserver : UIDTreeObserver + +@end + +@interface UIDUIApplicationObserverBuilder : NSObject + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDUIApplicationObserver.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDUIApplicationObserver.m new file mode 100644 index 000000000..84d57fc06 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDUIApplicationObserver.m @@ -0,0 +1,121 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "UIDUIApplicationObserver.h" +#import "UIDContext.h" +#import "UIDMainThread.h" +#import "UIDUIViewBasicObserver.h" + +#define UID_THROTTLE_SECONDS 1 + +@interface UIDUIApplicationObserver () { + UIDContext* _context; + UIDUIViewBasicObserver* _viewObserver; + + CFRunLoopObserverRef _observer; + NSTimeInterval _lastInvocationTimestamp; + bool _dirty; +} + +@end + +@implementation UIDUIApplicationObserver + +- (instancetype)initWithContext:(UIDContext*)context { + self = [super init]; + if (self) { + _context = context; + _lastInvocationTimestamp = 0; + _dirty = true; + _viewObserver = [[UIDUIViewBasicObserver alloc] initWithContext:_context + delegate:self]; + self.type = @"UIApplication"; + } + return self; +} + +- (void)subscribe:(UIApplication*)node { + __weak typeof(self) weakSelf = self; + _observer = CFRunLoopObserverCreateWithHandler( + kCFAllocatorDefault, + kCFRunLoopBeforeWaiting, + true, + INT_MAX, + ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) { + typeof(self) strongSelf = weakSelf; + if (strongSelf) { + NSTimeInterval currentTimestamp = + [NSDate timeIntervalSinceReferenceDate]; + if (strongSelf->_lastInvocationTimestamp == 0 || + (currentTimestamp - strongSelf->_lastInvocationTimestamp >= + UID_THROTTLE_SECONDS)) { + strongSelf->_lastInvocationTimestamp = currentTimestamp; + if (strongSelf->_dirty) { + strongSelf->_dirty = false; + [self processNode:strongSelf->_context.application + withSnapshot:true + withContext:strongSelf->_context]; + } + } + } + }); + CFRunLoopAddObserver(CFRunLoopGetMain(), _observer, kCFRunLoopCommonModes); + + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(orientationChanged:) + name:UIDeviceOrientationDidChangeNotification + object:nil]; +} + +- (void)unsubscribe { + CFRunLoopRemoveObserver(CFRunLoopGetMain(), _observer, kCFRunLoopCommonModes); + CFRelease(_observer); + _observer = nil; + + [[NSNotificationCenter defaultCenter] + removeObserver:self + name:UIDeviceOrientationDidChangeNotification + object:nil]; + + _dirty = true; +} + +- (void)orientationChanged:(NSNotification*)notification { + /** + Do not mark as dirty immediately as the view draws before + is animated into position. Dispatch after the same throttle amount + achieves the desired effect. + */ + UIDRunBlockOnMainThreadAfter( + ^{ + self->_dirty = true; + }, + UID_THROTTLE_SECONDS); +} + +- (void)viewUpdateWith:(UIView*)node { + _dirty = true; +} + +@end + +@implementation UIDUIApplicationObserverBuilder + +- (BOOL)canBuildFor:(id)node { + return [node isKindOfClass:[UIApplication class]]; +} + +- (UIDTreeObserver*)buildWithContext:(UIDContext*)context { + return [[UIDUIApplicationObserver alloc] initWithContext:context]; +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDUIKitObserver.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDUIKitObserver.h new file mode 100644 index 000000000..23c6419cb --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDUIKitObserver.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import + +NS_ASSUME_NONNULL_BEGIN + +typedef NS_ENUM(NSUInteger, UIDViewHierarchyUpdate) { + UIDViewHierarchyUpdateAddChild, + UIDViewHierarchyUpdateRemoveChild, +}; + +@protocol UIDUIKitObserverDelegate + +- (void)onDisplayLayer:(UIView*)view; +- (void)onDrawLayer:(UIView*)view; +- (void)onNeedsDisplay:(UIView*)view; +- (void)onHidden:(UIView*)view; +- (void)onView:(UIView*)view didUpdateHierarchy:(UIDViewHierarchyUpdate)update; + +@end + +@interface UIDUIKitObserver : NSObject + ++ (instancetype)sharedInstance; ++ (void)enable; + +- (void)setDrawObservationEnabled:(BOOL)enabled; + +@property(nonatomic, weak) id delegate; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDUIKitObserver.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDUIKitObserver.m new file mode 100644 index 000000000..68999cf1a --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDUIKitObserver.m @@ -0,0 +1,39 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "UIDUIKitObserver.h" +#import "UIView+Observer.h" + +@implementation UIDUIKitObserver + ++ (instancetype)sharedInstance { + static UIDUIKitObserver* instance = nil; + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + instance = [UIDUIKitObserver new]; + }); + + return instance; +} + ++ (void)enable { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + [UIView UID_observe]; + }); +} + +- (void)setDrawObservationEnabled:(BOOL)enabled { + [UIView UID_setDrawObservationEnabled:enabled]; +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDUIViewBasicObserver.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDUIViewBasicObserver.h new file mode 100644 index 000000000..cede168fa --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDUIViewBasicObserver.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import +#import +#import "UIDUIViewObserverDelegate.h" + +NS_ASSUME_NONNULL_BEGIN + +@class UIDContext; + +@interface UIDUIViewBasicObserver : NSObject + +- (instancetype)initWithContext:(UIDContext*)context + delegate:(id)delegate; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDUIViewBasicObserver.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDUIViewBasicObserver.m new file mode 100644 index 000000000..383ae847b --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDUIViewBasicObserver.m @@ -0,0 +1,63 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "UIDUIViewBasicObserver.h" +#import "UIDContext.h" +#import "UIDUIKitObserver.h" + +@interface UIDUIViewBasicObserver () { + UIDContext* _context; +} + +@property(nonatomic, weak) id delegate; + +@end + +@implementation UIDUIViewBasicObserver + +- (instancetype)initWithContext:(UIDContext*)context + delegate:(id)delegate { + self = [super init]; + if (self) { + _context = context; + _delegate = delegate; + + [UIDUIKitObserver sharedInstance].delegate = self; + [UIDUIKitObserver enable]; + } + return self; +} + +- (void)dealloc { + [UIDUIKitObserver sharedInstance].delegate = nil; +} + +- (void)onDisplayLayer:(nonnull UIView*)view { + [self.delegate viewUpdateWith:view]; +} + +- (void)onDrawLayer:(nonnull UIView*)view { + [self.delegate viewUpdateWith:view]; +} + +- (void)onNeedsDisplay:(nonnull UIView*)view { + [self.delegate viewUpdateWith:view]; +} + +- (void)onHidden:(nonnull UIView*)view { + [self.delegate viewUpdateWith:view]; +} + +- (void)onView:(UIView*)view didUpdateHierarchy:(UIDViewHierarchyUpdate)update { + [self.delegate viewUpdateWith:view]; +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDUIViewObserverDelegate.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDUIViewObserverDelegate.h new file mode 100644 index 000000000..a1f0bb96a --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIDUIViewObserverDelegate.h @@ -0,0 +1,16 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import + +@protocol UIDUIViewObserverDelegate +- (void)viewUpdateWith:(nonnull UIView*)view; +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIView+Observer.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIView+Observer.h new file mode 100644 index 000000000..bad66c5a0 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIView+Observer.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import +#import + +NS_ASSUME_NONNULL_BEGIN + +FB_LINK_REQUIRE_CATEGORY(UIView_Observer) +@interface UIView (Observer) + ++ (void)UID_observe; ++ (void)UID_setDrawObservationEnabled:(BOOL)enabled; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIView+Observer.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIView+Observer.m new file mode 100644 index 000000000..a988cbd77 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Observer/UIView+Observer.m @@ -0,0 +1,158 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "UIDSwizzle.h" +#import "UIDUIKitObserver.h" +#import "UIView+Observer.h" + +static BOOL UID_DrawObserverEnabled = true; + +FB_LINKABLE(UIView_Observer) +@implementation UIView (Observer) + ++ (void)UID_setDrawObservationEnabled:(BOOL)enabled { + UID_DrawObserverEnabled = enabled; +} + ++ (void)UID_observe { + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + UIDSwizzleMethod( + [self class], + @selector(drawLayer:inContext:), + @selector(UID_drawLayer:inContext:), + false); + UIDSwizzleMethod( + [self class], + @selector(displayLayer:), + @selector(UID_displayLayer:), + false); + UIDSwizzleMethod( + [self class], + @selector(setNeedsDisplay), + @selector(UID_setNeedsDisplay), + false); + UIDSwizzleMethod( + [self class], + @selector(addSubview:), + @selector(UID_addSubview:), + false); + UIDSwizzleMethod( + [self class], + @selector(insertSubview:atIndex:), + @selector(UID_insertSubview:atIndex:), + false); + UIDSwizzleMethod( + [self class], + @selector(insertSubview:aboveSubview:), + @selector(UID_insertSubview:aboveSubview:), + false); + UIDSwizzleMethod( + [self class], + @selector(insertSubview:belowSubview:), + @selector(UID_insertSubview:belowSubview:), + false); + UIDSwizzleMethod( + [self class], + @selector(removeFromSuperview), + @selector(UID_removeFromSuperview), + false); + UIDSwizzleMethod( + [self class], @selector(setHidden:), @selector(UID_setHidden:), true); + }); +} + +- (void)UID_displayLayer:(CALayer*)layer { + [self UID_displayLayer:layer]; + if (UID_DrawObserverEnabled) { + [[UIDUIKitObserver sharedInstance].delegate onDisplayLayer:self]; + } +} + +- (void)UID_drawLayer:(CALayer*)layer inContext:(CGContextRef)ctx { + [self UID_drawLayer:layer inContext:ctx]; + if (UID_DrawObserverEnabled) { + [[UIDUIKitObserver sharedInstance].delegate onDrawLayer:self]; + } +} + +- (void)UID_setNeedsDisplay { + [self UID_setNeedsDisplay]; + if (UID_DrawObserverEnabled) { + [[UIDUIKitObserver sharedInstance].delegate onNeedsDisplay:self]; + } +} + +- (void)UID_setHidden:(BOOL)isHidden { + [self UID_setHidden:isHidden]; + if (UID_DrawObserverEnabled) { + [[UIDUIKitObserver sharedInstance].delegate onHidden:self]; + } +} + +- (void)UID_addSubview:(UIView*)view { + [self UID_addSubview:view]; + if (UID_DrawObserverEnabled) { + [[UIDUIKitObserver sharedInstance].delegate + onView:self + didUpdateHierarchy:UIDViewHierarchyUpdateAddChild]; + } +} + +- (void)UID_insertSubview:(UIView*)subview atIndex:(NSInteger)index { + [self UID_insertSubview:subview atIndex:index]; + if (UID_DrawObserverEnabled) { + [[UIDUIKitObserver sharedInstance].delegate + onView:self + didUpdateHierarchy:UIDViewHierarchyUpdateAddChild]; + } +} + +- (void)UID_insertSubview:(UIView*)subview + aboveSubview:(UIView*)siblingSubview { + [self UID_insertSubview:subview aboveSubview:siblingSubview]; + if (UID_DrawObserverEnabled) { + [[UIDUIKitObserver sharedInstance].delegate + onView:self + didUpdateHierarchy:UIDViewHierarchyUpdateAddChild]; + } +} + +- (void)UID_insertSubview:(UIView*)subview + belowSubview:(UIView*)siblingSubview { + [self UID_insertSubview:subview belowSubview:siblingSubview]; + if (UID_DrawObserverEnabled) { + [[UIDUIKitObserver sharedInstance].delegate + onView:self + didUpdateHierarchy:UIDViewHierarchyUpdateAddChild]; + } +} + +- (void)UID_removeFromSuperview { + UIView* oldSuperview = self.superview; + [self UID_removeFromSuperview]; + /** + Not enough to check if draw observation is enabled (which is disabled + during snapshots). An extra check is needed in case the old superview is + _UISnapshotWindow_. The reason is _UIView_removeFromSuperview_ can also be + called during deallocation after the screenshot is taken and observation is + re-enabled. Without the extra check, it creates a recursive loop in which + the view is constantly marked as dirty. + */ + if (UID_DrawObserverEnabled && + ![oldSuperview isKindOfClass:NSClassFromString(@"_UISnapshotWindow")]) { + [[UIDUIKitObserver sharedInstance].delegate + onView:oldSuperview + didUpdateHierarchy:UIDViewHierarchyUpdateRemoveChild]; + } +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/NSArray+Foundation.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/NSArray+Foundation.h new file mode 100644 index 000000000..4aac6badf --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/NSArray+Foundation.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import +#import +#import "UIDFoundation.h" + +NS_ASSUME_NONNULL_BEGIN + +FB_LINK_REQUIRE_CATEGORY(NSArray_Foundation) +@interface NSArray(Foundation) + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/NSArray+Foundation.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/NSArray+Foundation.m new file mode 100644 index 000000000..43459ce5f --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/NSArray+Foundation.m @@ -0,0 +1,25 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "NSArray+Foundation.h" + +FB_LINKABLE(NSArray_Foundation) +@implementation NSArray (Foundation) + +- (id)toFoundation { + NSMutableArray* copy = [NSMutableArray arrayWithCapacity:self.count]; + for (id object in self) { + [copy addObject:[object toFoundation]]; + } + return copy; +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/NSDictionary+Foundation.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/NSDictionary+Foundation.h new file mode 100644 index 000000000..eeb91cef1 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/NSDictionary+Foundation.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import +#import +#import "UIDFoundation.h" + +NS_ASSUME_NONNULL_BEGIN + +FB_LINK_REQUIRE_CATEGORY(NSDictionary_Foundation) +@interface NSDictionary (Foundation) + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/NSDictionary+Foundation.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/NSDictionary+Foundation.m new file mode 100644 index 000000000..6819370b9 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/NSDictionary+Foundation.m @@ -0,0 +1,28 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "NSDictionary+Foundation.h" + +FB_LINKABLE(NSDictionary_Foundation) +@implementation NSDictionary (Foundation) + +- (id)toFoundation { + NSMutableDictionary* copy = + [NSMutableDictionary dictionaryWithCapacity:self.count]; + + for (id key in self) { + [copy setObject:[self[key] toFoundation] forKey:[key description]]; + } + + return copy; +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/NSNull+Foundation.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/NSNull+Foundation.h new file mode 100644 index 000000000..ab1ed7e16 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/NSNull+Foundation.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import +#import +#import "UIDFoundation.h" + +NS_ASSUME_NONNULL_BEGIN + +FB_LINK_REQUIRE_CATEGORY(NSNull_Foundation) +@interface NSNull (Foundation) + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/NSNull+Foundation.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/NSNull+Foundation.m new file mode 100644 index 000000000..7a7579a25 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/NSNull+Foundation.m @@ -0,0 +1,21 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "NSNull+Foundation.h" + +FB_LINKABLE(NSNull_Foundation) +@implementation NSNull (Foundation) + +- (id)toFoundation { + return self; +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/NSSet+Foundation.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/NSSet+Foundation.h new file mode 100644 index 000000000..b40359354 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/NSSet+Foundation.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import +#import +#import "UIDFoundation.h" + +NS_ASSUME_NONNULL_BEGIN + +FB_LINK_REQUIRE_CATEGORY(NSSet_Foundation) +@interface NSSet(Foundation) + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/NSSet+Foundation.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/NSSet+Foundation.m new file mode 100644 index 000000000..a4dc10e7f --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/NSSet+Foundation.m @@ -0,0 +1,25 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "NSSet+Foundation.h" + +FB_LINKABLE(NSSet_Foundation) +@implementation NSSet (Foundation) + +- (id)toFoundation { + NSMutableArray* copy = [NSMutableArray arrayWithCapacity:self.count]; + for (id object in self) { + [copy addObject:[object toFoundation]]; + } + return copy; +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/NSString+Foundation.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/NSString+Foundation.h new file mode 100644 index 000000000..cbea3e178 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/NSString+Foundation.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import +#import +#import "UIDFoundation.h" + +NS_ASSUME_NONNULL_BEGIN + +FB_LINK_REQUIRE_CATEGORY(NSString_Foundation) +@interface NSString (Foundation) + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/NSString+Foundation.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/NSString+Foundation.m new file mode 100644 index 000000000..89b00f5ef --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/NSString+Foundation.m @@ -0,0 +1,21 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "NSString+Foundation.h" + +FB_LINKABLE(NSString_Foundation) +@implementation NSString (Foundation) + +- (id)toFoundation { + return self; +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/NSValue+Foundation.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/NSValue+Foundation.h new file mode 100644 index 000000000..deeac3e60 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/NSValue+Foundation.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import +#import +#import "UIDFoundation.h" + +NS_ASSUME_NONNULL_BEGIN + +FB_LINK_REQUIRE_CATEGORY(NSValue_Foundation) +@interface NSValue (Foundation) + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/NSValue+Foundation.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/NSValue+Foundation.m new file mode 100644 index 000000000..b2db647aa --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/NSValue+Foundation.m @@ -0,0 +1,21 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "NSValue+Foundation.h" + +FB_LINKABLE(NSValue_Foundation) +@implementation NSValue (Foundation) + +- (id)toFoundation { + return self; +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDBounds+Foundation.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDBounds+Foundation.h new file mode 100644 index 000000000..249e348ba --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDBounds+Foundation.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import +#import +#import "UIDBounds.h" +#import "UIDFoundation.h" + +NS_ASSUME_NONNULL_BEGIN + +FB_LINK_REQUIRE_CATEGORY(UIDBounds_Foundation) +@interface UIDBounds (Foundation) + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDBounds+Foundation.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDBounds+Foundation.m new file mode 100644 index 000000000..ea37a627a --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDBounds+Foundation.m @@ -0,0 +1,26 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "UIDBounds+Foundation.h" + +FB_LINKABLE(UIDBounds_Foundation) +@implementation UIDBounds (Foundation) + +- (id)toFoundation { + return @{ + @"x" : [NSNumber numberWithInt:self.x], + @"y" : [NSNumber numberWithInt:self.y], + @"width" : [NSNumber numberWithInt:self.width], + @"height" : [NSNumber numberWithInt:self.height], + }; +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDEdgeInsets+Foundation.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDEdgeInsets+Foundation.h new file mode 100644 index 000000000..3b576225d --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDEdgeInsets+Foundation.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import +#import +#import "UIDEdgeInsets.h" +#import "UIDFoundation.h" + +NS_ASSUME_NONNULL_BEGIN + +FB_LINK_REQUIRE_CATEGORY(UIDEdgeInsets_Foundation) +@interface UIDEdgeInsets (Foundation) + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDEdgeInsets+Foundation.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDEdgeInsets+Foundation.m new file mode 100644 index 000000000..2241908e7 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDEdgeInsets+Foundation.m @@ -0,0 +1,26 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "UIDEdgeInsets+Foundation.h" + +FB_LINKABLE(UIDEdgeInsets_Foundation) +@implementation UIDEdgeInsets (Foundation) + +- (id)toFoundation { + return @{ + @"top" : [NSNumber numberWithFloat:self.top], + @"right" : [NSNumber numberWithFloat:self.right], + @"bottom" : [NSNumber numberWithFloat:self.bottom], + @"left" : [NSNumber numberWithFloat:self.left], + }; +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDFoundation.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDFoundation.h new file mode 100644 index 000000000..5ec458c80 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDFoundation.h @@ -0,0 +1,16 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +@protocol UIDFoundation + +- (id)toFoundation; + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDFrameworkEvent+Foundation.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDFrameworkEvent+Foundation.h new file mode 100644 index 000000000..5bf903fdd --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDFrameworkEvent+Foundation.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import +#import +#import "UIDFoundation.h" +#import "UIDFrameworkEvent.h" + +NS_ASSUME_NONNULL_BEGIN + +FB_LINK_REQUIRE_CATEGORY(UIDFrameworkEvent_Foundation) +@interface UIDFrameworkEvent (Foundation) + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDFrameworkEvent+Foundation.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDFrameworkEvent+Foundation.m new file mode 100644 index 000000000..5092d0c3e --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDFrameworkEvent+Foundation.m @@ -0,0 +1,34 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "UIDFrameworkEvent+Foundation.h" + +FB_LINKABLE(UIDFrameworkEvent_Foundation) +@implementation UIDFrameworkEvent (Foundation) + +- (id)toFoundation { + NSMutableDictionary* data = [NSMutableDictionary dictionaryWithDictionary:@{ + @"nodeId" : [NSNumber numberWithUnsignedInt:self.nodeIdentifier], + @"type" : self.type, + @"timestamp" : + [NSNumber numberWithDouble:self.timestamp.timeIntervalSince1970], + @"payload" : self.payload ?: @{}, + }]; + + if (self.stacktrace) { + [data setObject:@{@"stacktrace" : self.stacktrace, @"type" : @"stacktrace"} + forKey:@"attribution"]; + } + + return data; +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDFrameworkEventMetadata+Foundation.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDFrameworkEventMetadata+Foundation.h new file mode 100644 index 000000000..8c51eb9ec --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDFrameworkEventMetadata+Foundation.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import +#import +#import "UIDFoundation.h" +#import "UIDFrameworkEventMetadata.h" + +NS_ASSUME_NONNULL_BEGIN + +FB_LINK_REQUIRE_CATEGORY(UIDFrameworkEventMetadata_Foundation) +@interface UIDFrameworkEventMetadata (Foundation) + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDFrameworkEventMetadata+Foundation.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDFrameworkEventMetadata+Foundation.m new file mode 100644 index 000000000..5c2e79cc7 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDFrameworkEventMetadata+Foundation.m @@ -0,0 +1,25 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "NSDictionary+Foundation.h" +#import "UIDFrameworkEventMetadata+Foundation.h" + +FB_LINKABLE(UIDFrameworkEventMetadata_Foundation) +@implementation UIDFrameworkEventMetadata (Foundation) + +- (id)toFoundation { + return @{ + @"type" : self.type, + @"documentation" : self.documentation, + }; +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDInitEvent+Foundation.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDInitEvent+Foundation.h new file mode 100644 index 000000000..0ce208e98 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDInitEvent+Foundation.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import +#import +#import "UIDFoundation.h" +#import "UIDInitEvent.h" + +NS_ASSUME_NONNULL_BEGIN + +FB_LINK_REQUIRE_CATEGORY(UIDInitEvent_Foundation) +@interface UIDInitEvent (Foundation) + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDInitEvent+Foundation.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDInitEvent+Foundation.m new file mode 100644 index 000000000..01b42df4c --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDInitEvent+Foundation.m @@ -0,0 +1,27 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "NSArray+Foundation.h" +#import "UIDInitEvent+Foundation.h" + +FB_LINKABLE(UIDInitEvent_Foundation) +@implementation UIDInitEvent (Foundation) + +- (id)toFoundation { + return @{ + @"rootId" : [NSNumber numberWithUnsignedInt:self.rootId], + @"frameworkEventMetadata" : self.frameworkEventMetadata + ? [self.frameworkEventMetadata toFoundation] + : @[], + }; +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDInspectable+Foundation.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDInspectable+Foundation.h new file mode 100644 index 000000000..7c4ffe103 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDInspectable+Foundation.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import +#import +#import "UIDFoundation.h" +#import "UIDInspectable.h" + +NS_ASSUME_NONNULL_BEGIN + +FB_LINK_REQUIRE_CATEGORY(UIDInspectable_Foundation) +@interface UIDInspectable (Foundation) + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDInspectable+Foundation.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDInspectable+Foundation.m new file mode 100644 index 000000000..d93573e65 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDInspectable+Foundation.m @@ -0,0 +1,21 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "UIDInspectable+Foundation.h" + +FB_LINKABLE(UIDInspectable_Foundation) +@implementation UIDInspectable (Foundation) + +- (id)toFoundation { + return @{}; +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDInspectableArray+Foundation.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDInspectableArray+Foundation.h new file mode 100644 index 000000000..d7494164f --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDInspectableArray+Foundation.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import +#import +#import "UIDFoundation.h" +#import "UIDInspectable.h" + +NS_ASSUME_NONNULL_BEGIN + +FB_LINK_REQUIRE_CATEGORY(UIDInspectableArray_Foundation) +@interface UIDInspectableArray (Foundation) + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDInspectableArray+Foundation.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDInspectableArray+Foundation.m new file mode 100644 index 000000000..419a0377d --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDInspectableArray+Foundation.m @@ -0,0 +1,27 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "UIDInspectable+Foundation.h" +#import "UIDInspectableArray+Foundation.h" + +FB_LINKABLE(UIDInspectableArray_Foundation) +@implementation UIDInspectableArray (Foundation) + +- (id)toFoundation { + NSMutableArray* items = [NSMutableArray arrayWithCapacity:self.items.count]; + for (UIDInspectable* object in self.items) { + [items addObject:[object toFoundation]]; + } + + return @{@"type" : @"array", @"items" : items}; +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDInspectableObject+Foundation.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDInspectableObject+Foundation.h new file mode 100644 index 000000000..316e438f6 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDInspectableObject+Foundation.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import +#import +#import "UIDFoundation.h" +#import "UIDInspectable.h" + +NS_ASSUME_NONNULL_BEGIN + +FB_LINK_REQUIRE_CATEGORY(UIDInspectableObject_Foundation) +@interface UIDInspectableObject (Foundation) + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDInspectableObject+Foundation.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDInspectableObject+Foundation.m new file mode 100644 index 000000000..fb8a4e433 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDInspectableObject+Foundation.m @@ -0,0 +1,22 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "NSDictionary+Foundation.h" +#import "UIDInspectableObject+Foundation.h" + +FB_LINKABLE(UIDInspectableObject_Foundation) +@implementation UIDInspectableObject (Foundation) + +- (id)toFoundation { + return @{@"type" : @"object", @"fields" : [self.fields toFoundation]}; +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDInspectableValue+Foundation.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDInspectableValue+Foundation.h new file mode 100644 index 000000000..c2fa46f3d --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDInspectableValue+Foundation.h @@ -0,0 +1,69 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import +#import +#import "UIDFoundation.h" +#import "UIDInspectable.h" + +NS_ASSUME_NONNULL_BEGIN + +FB_LINK_REQUIRE_CATEGORY(UIDInspectableText_Foundation) +@interface UIDInspectableText (Foundation) + +@end + +FB_LINK_REQUIRE_CATEGORY(UIDInspectableNumber_Foundation) +@interface UIDInspectableNumber (Foundation) + +@end + +FB_LINK_REQUIRE_CATEGORY(UIDInspectableBoolean_Foundation) +@interface UIDInspectableBoolean (Foundation) + +@end + +FB_LINK_REQUIRE_CATEGORY(UIDInspectableBounds_Foundation) +@interface UIDInspectableBounds (Foundation) + +@end + +FB_LINK_REQUIRE_CATEGORY(UIDInspectableSize_Foundation) +@interface UIDInspectableSize (Foundation) + +@end + +FB_LINK_REQUIRE_CATEGORY(UIDInspectableCoordinate_Foundation) +@interface UIDInspectableCoordinate (Foundation) + +@end + +FB_LINK_REQUIRE_CATEGORY(UIDInspectableEdgeInsets_Foundation) +@interface UIDInspectableEdgeInsets (Foundation) + +@end + +FB_LINK_REQUIRE_CATEGORY(UIDInspectableColor_Foundation) +@interface UIDInspectableColor (Foundation) + +@end + +FB_LINK_REQUIRE_CATEGORY(UIDInspectableUnknown_Foundation) +@interface UIDInspectableUnknown (Foundation) + +@end + +FB_LINK_REQUIRE_CATEGORY(UIDInspectableEnum_Foundation) +@interface UIDInspectableEnum (Foundation) + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDInspectableValue+Foundation.mm b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDInspectableValue+Foundation.mm new file mode 100644 index 000000000..b706232f5 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDInspectableValue+Foundation.mm @@ -0,0 +1,183 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import +#import "UIDBounds.h" +#import "UIDEdgeInsets+Foundation.h" +#import "UIDInspectableValue+Foundation.h" + +static auto CGFloatToNSNumber(CGFloat x) -> NSNumber* { +#if CGFLOAT_IS_DOUBLE + return [NSNumber numberWithDouble:x]; +#else + return [NSNumber numberWithFloat:x]; +#endif +} + +static auto boxedNumber(CGFloat x) -> NSObject* { + if (std::isinf(x)) { + return @"Infinity"; + } + + if (std::isnan(x)) { + return @"NaN"; + } + + return CGFloatToNSNumber(x); +} + +FB_LINKABLE(UIDInspectableText_Foundation) +@implementation UIDInspectableText (Foundation) + +- (id)toFoundation { + return @{ + @"type" : @"text", + @"value" : self.value ?: @"null", + }; +} + +@end + +FB_LINKABLE(UIDInspectableNumber_Foundation) +@implementation UIDInspectableNumber (Foundation) + +- (id)toFoundation { + return @{ + @"type" : @"number", + @"value" : std::isinf([self.value doubleValue]) ? @"Infinity" : self.value, + }; +} + +@end + +FB_LINKABLE(UIDInspectableBoolean_Foundation) +@implementation UIDInspectableBoolean (Foundation) + +- (id)toFoundation { + return @{ + @"type" : @"boolean", + @"value" : [NSNumber numberWithBool:self.value], + }; +} + +@end + +FB_LINKABLE(UIDInspectableBounds_Foundation) +@implementation UIDInspectableBounds (Foundation) + +- (id)toFoundation { + return @{ + @"type" : @"bounds", + @"value" : @{ + @"x" : CGFloatToNSNumber(self.value.x), + @"y" : CGFloatToNSNumber(self.value.y), + @"width" : CGFloatToNSNumber(self.value.width), + @"height" : CGFloatToNSNumber(self.value.height), + }, + }; +} + +@end + +FB_LINKABLE(UIDInspectableSize_Foundation) +@implementation UIDInspectableSize (Foundation) + +- (id)toFoundation { + return @{ + @"type" : @"size", + @"value" : @{ + @"width" : boxedNumber(self.value.width), + @"height" : boxedNumber(self.value.height), + }, + }; +} + +@end + +FB_LINKABLE(UIDInspectableCoordinate_Foundation) +@implementation UIDInspectableCoordinate (Foundation) + +- (id)toFoundation { + return @{ + @"type" : @"coordinate", + @"value" : @{ + @"x" : CGFloatToNSNumber(self.value.x), + @"y" : CGFloatToNSNumber(self.value.y), + }, + }; +} + +@end + +FB_LINKABLE(UIDInspectableEdgeInsets_Foundation) +@implementation UIDInspectableEdgeInsets (Foundation) + +- (id)toFoundation { + return @{ + @"type" : @"space", + @"value" : [self.value toFoundation], + }; +} + +@end + +FB_LINKABLE(UIDInspectableColor_Foundation) +@implementation UIDInspectableColor (Foundation) + +- (id)toFoundation { + CGFloat red = 0; + CGFloat green = 0; + CGFloat blue = 0; + CGFloat alpha = 0; + + [self.value getRed:&red green:&green blue:&blue alpha:&alpha]; + + red *= 255; + green *= 255; + blue *= 255; + alpha = 0; + + return @{ + @"type" : @"color", + @"value" : @{ + @"r" : [NSNumber numberWithLong:red], + @"g" : [NSNumber numberWithLong:green], + @"b" : [NSNumber numberWithLong:blue], + @"a" : [NSNumber numberWithLong:alpha], + }, + }; +} + +@end + +FB_LINKABLE(UIDInspectableUnknown_Foundation) +@implementation UIDInspectableUnknown (Foundation) + +- (id)toFoundation { + return @{ + @"type" : @"unknown", + @"value" : self.value ?: @"unknown", + }; +} + +@end + +FB_LINKABLE(UIDInspectableEnum_Foundation) +@implementation UIDInspectableEnum (Foundation) + +- (id)toFoundation { + return @{ + @"type" : @"enum", + @"value" : self.value, + }; +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDJSONSerializer.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDJSONSerializer.h new file mode 100644 index 000000000..8710c6495 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDJSONSerializer.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import +#import "UIDFoundation.h" +#import "UIDInitEvent+Foundation.h" +#import "UIDMetadataUpdateEvent+Foundation.h" +#import "UIDPerfStatsEvent+Foundation.h" +#import "UIDSubtreeUpdateEvent+Foundation.h" + +#ifdef __cplusplus +extern "C" { +#endif +extern NSString* UID_toJSON(id obj); +extern NSString* UID_FoundationtoJSON(id obj); +extern id UID_toFoundation(id obj); +#ifdef __cplusplus +} +#endif + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDJSONSerializer.mm b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDJSONSerializer.mm new file mode 100644 index 000000000..403b734df --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDJSONSerializer.mm @@ -0,0 +1,68 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "UIDJSONSerializer.h" +#import "NSArray+Foundation.h" +#import "NSDictionary+Foundation.h" +#import "NSSet+Foundation.h" +#import "UIDBounds+Foundation.h" +#import "UIDEdgeInsets+Foundation.h" +#import "UIDInspectable+Foundation.h" +#import "UIDInspectableArray+Foundation.h" +#import "UIDInspectableObject+Foundation.h" +#import "UIDInspectableValue+Foundation.h" +#import "UIDMetadata+Foundation.h" +#import "UIDNode+Foundation.h" +#import "UIDPerformance.h" +#import "UIDTimeUtilities.h" +#import "UIImage+Foundation.h" + +#ifdef __cplusplus +extern "C" { +#endif +id UID_toFoundation(id object) { + return [object toFoundation]; +} + +NSString* UID_FoundationtoJSON(id object) { + NSError* error = NULL; + if (![NSJSONSerialization isValidJSONObject:object]) { + return @""; + } + + uint64_t t0 = UIDPerformanceNow(); + + NSData* data = [NSJSONSerialization dataWithJSONObject:object + options:0 + error:&error]; + uint64_t t1 = UIDPerformanceNow(); + + UIDPerformanceAggregate( + @"serialisationBinaryMS", UIDMonotonicTimeConvertMachUnitsToMS(t1 - t0)); + + NSString* JSON = [[NSString alloc] initWithData:data + encoding:NSUTF8StringEncoding]; + + uint64_t t2 = UIDPerformanceNow(); + + UIDPerformanceAggregate( + @"serialisationEncodingMS", + UIDMonotonicTimeConvertMachUnitsToMS(t2 - t1)); + + return JSON; +} + +NSString* UID_toJSON(id object) { + return UID_FoundationtoJSON(UID_toFoundation(object)); +} +#ifdef __cplusplus +} +#endif + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDLazyInspectable+Foundation.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDLazyInspectable+Foundation.h new file mode 100644 index 000000000..a636f5d49 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDLazyInspectable+Foundation.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import +#import +#import "UIDFoundation.h" +#import "UIDInspectable.h" + +NS_ASSUME_NONNULL_BEGIN + +FB_LINK_REQUIRE_CATEGORY(UIDLazyInspectable_Foundation) +@interface UIDLazyInspectable (Foundation) + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDLazyInspectable+Foundation.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDLazyInspectable+Foundation.m new file mode 100644 index 000000000..ee9e7d81f --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDLazyInspectable+Foundation.m @@ -0,0 +1,31 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "UIDInspectable+Foundation.h" +#import "UIDLazyInspectable+Foundation.h" + +FB_LINKABLE(UIDLazyInspectable_Foundation) +@implementation UIDLazyInspectable (Foundation) + +/** + Converting a lazy inspectable to its Foundation type + equivalent may trigger materialisation of its value. + + If the lazy inspectable was already materialised, then + this value will be returned and serialised. + Otherwise, the loader will be invoked to obtain the value + first. + */ +- (id)toFoundation { + return [[self value] toFoundation]; +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDMetadata+Foundation.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDMetadata+Foundation.h new file mode 100644 index 000000000..990456f10 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDMetadata+Foundation.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import +#import +#import "UIDFoundation.h" +#import "UIDMetadata.h" + +NS_ASSUME_NONNULL_BEGIN + +FB_LINK_REQUIRE_CATEGORY(UIDMetadata_Foundation) +@interface UIDMetadata (Foundation) + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDMetadata+Foundation.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDMetadata+Foundation.m new file mode 100644 index 000000000..a47cd1fed --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDMetadata+Foundation.m @@ -0,0 +1,27 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "NSSet+Foundation.h" +#import "UIDMetadata+Foundation.h" + +FB_LINKABLE(UIDMetadata_Foundation) +@implementation UIDMetadata (Foundation) + +- (id)toFoundation { + return @{ + @"id" : self.identifier, + @"type" : self.type, + @"name" : self.name, + @"mutable" : [NSNumber numberWithBool:self.isMutable], + }; +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDMetadataUpdateEvent+Foundation.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDMetadataUpdateEvent+Foundation.h new file mode 100644 index 000000000..8698ddbc7 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDMetadataUpdateEvent+Foundation.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import +#import +#import "UIDFoundation.h" +#import "UIDMetadataUpdateEvent.h" + +NS_ASSUME_NONNULL_BEGIN + +FB_LINK_REQUIRE_CATEGORY(UIDMetadataUpdateEvent_Foundation) +@interface UIDMetadataUpdateEvent (Foundation) + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDMetadataUpdateEvent+Foundation.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDMetadataUpdateEvent+Foundation.m new file mode 100644 index 000000000..39feb8ae9 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDMetadataUpdateEvent+Foundation.m @@ -0,0 +1,24 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "NSDictionary+Foundation.h" +#import "UIDMetadataUpdateEvent+Foundation.h" + +FB_LINKABLE(UIDMetadataUpdateEvent_Foundation) +@implementation UIDMetadataUpdateEvent (Foundation) + +- (id)toFoundation { + return @{ + @"attributeMetadata" : [self.attributeMetadata toFoundation], + }; +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDNode+Foundation.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDNode+Foundation.h new file mode 100644 index 000000000..08e3f1f06 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDNode+Foundation.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import +#import +#import "UIDFoundation.h" +#import "UIDNode.h" + +NS_ASSUME_NONNULL_BEGIN + +FB_LINK_REQUIRE_CATEGORY(UIDNode_Foundation) +@interface UIDNode (Foundation) + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDNode+Foundation.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDNode+Foundation.m new file mode 100644 index 000000000..f7407ba9c --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDNode+Foundation.m @@ -0,0 +1,47 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "NSDictionary+Foundation.h" +#import "UIDBounds+Foundation.h" +#import "UIDNode+Foundation.h" + +FB_LINKABLE(UIDNode_Foundation) +@implementation UIDNode (Foundation) + +- (id)toFoundation { + NSMutableDictionary* data = [NSMutableDictionary dictionaryWithDictionary:@{ + @"id" : [NSNumber numberWithUnsignedInt:self.identifier], + @"qualifiedName" : self.qualifiedName, + @"name" : self.name, + @"bounds" : [self.bounds toFoundation], + @"tags" : self.tags.allObjects, + @"inlineAttributes" : self.inlineAttributes, + @"children" : self.children, + }]; + + if (self.activeChild) { + [data setObject:self.activeChild forKey:@"activeChild"]; + } + if (self.attributes) { + [data setObject:[self.attributes toFoundation] forKey:@"attributes"]; + } + if (self.hiddenAttributes) { + [data setObject:[self.hiddenAttributes toFoundation] + forKey:@"hiddenAttributes"]; + } + if (self.parent) { + [data setObject:self.parent forKey:@"parent"]; + } + + return data; +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDPerfStatsEvent+Foundation.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDPerfStatsEvent+Foundation.h new file mode 100644 index 000000000..a4908af98 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDPerfStatsEvent+Foundation.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import +#import +#import "UIDFoundation.h" +#import "UIDPerfStatsEvent.h" + +NS_ASSUME_NONNULL_BEGIN + +FB_LINK_REQUIRE_CATEGORY(UIDPerfStatsEvent_Foundation) +@interface UIDPerfStatsEvent (Foundation) + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDPerfStatsEvent+Foundation.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDPerfStatsEvent+Foundation.m new file mode 100644 index 000000000..a20e1fb05 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDPerfStatsEvent+Foundation.m @@ -0,0 +1,43 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "UIDPerfStatsEvent+Foundation.h" + +FB_LINKABLE(UIDPerfStatsEvent_Foundation) +@implementation UIDPerfStatsEvent (Foundation) + +- (id)toFoundation { + NSMutableDictionary* data = [NSMutableDictionary dictionaryWithDictionary:@{ + @"txId" : [NSNumber numberWithDouble:self.txId], + @"observerType" : self.observerType, + @"nodesCount" : [NSNumber numberWithUnsignedInt:self.nodesCount], + @"eventsCount" : [NSNumber numberWithUnsignedInt:self.eventsCount], + @"start" : [NSNumber numberWithLong:self.start], + @"traversalMS" : [NSNumber numberWithLong:self.traversalMS], + @"snapshotMS" : [NSNumber numberWithLong:self.snapshotMS], + @"queuingMS" : [NSNumber numberWithLong:self.queuingMS], + @"deferredComputationMS" : + [NSNumber numberWithLong:self.deferredComputationMS], + @"serializationMS" : [NSNumber numberWithLong:self.serializationMS], + @"socketMS" : [NSNumber numberWithLong:self.socketMS], + @"payloadSize" : [NSNumber numberWithLong:self.payloadSize], + @"frameworkEventsMS" : [NSNumber numberWithLong:self.frameworkEventsMS], + }]; + + if (self.dynamicMeasures) { + for (id key in self.dynamicMeasures) { + [data setObject:self.dynamicMeasures[key] forKey:key]; + } + } + return data; +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDSubtreeUpdateEvent+Foundation.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDSubtreeUpdateEvent+Foundation.h new file mode 100644 index 000000000..375720792 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDSubtreeUpdateEvent+Foundation.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import +#import +#import "UIDFoundation.h" +#import "UIDSubtreeUpdateEvent.h" + +NS_ASSUME_NONNULL_BEGIN + +FB_LINK_REQUIRE_CATEGORY(UIDSubtreeUpdateEvent_Foundation) +@interface UIDSubtreeUpdateEvent (Foundation) + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDSubtreeUpdateEvent+Foundation.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDSubtreeUpdateEvent+Foundation.m new file mode 100644 index 000000000..a76b8de15 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIDSubtreeUpdateEvent+Foundation.m @@ -0,0 +1,32 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "NSArray+Foundation.h" +#import "UIDSubtreeUpdateEvent+Foundation.h" +#import "UIImage+Foundation.h" + +FB_LINKABLE(UIDSubtreeUpdateEvent_Foundation) +@implementation UIDSubtreeUpdateEvent (Foundation) + +- (id)toFoundation { + return @{ + @"txId" : [NSNumber numberWithDouble:self.txId], + @"observerType" : self.observerType, + @"rootId" : [NSNumber numberWithUnsignedInt:self.rootId], + @"nodes" : [self.nodes toFoundation], + @"snapshot" : self.snapshot ? [self.snapshot toFoundation] : @"", + @"frameworkEvents" : self.frameworkEvents + ? [self.frameworkEvents toFoundation] + : @[], + }; +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIImage+Foundation.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIImage+Foundation.h new file mode 100644 index 000000000..a8e1c7343 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIImage+Foundation.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import +#import +#import "UIDFoundation.h" + +NS_ASSUME_NONNULL_BEGIN + +FB_LINK_REQUIRE_CATEGORY(UIImage_Foundation) +@interface UIImage (Foundation) + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIImage+Foundation.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIImage+Foundation.m new file mode 100644 index 000000000..00b56d44c --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Serializers/UIImage+Foundation.m @@ -0,0 +1,33 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "UIDPerformance.h" +#import "UIDTimeUtilities.h" +#import "UIImage+Foundation.h" + +FB_LINKABLE(UIImage_Foundation) +@implementation UIImage (Foundation) + +- (NSString*)toFoundation { + uint64_t t0 = UIDPerformanceNow(); + NSData* data = UIImagePNGRepresentation(self); + NSString* base64 = [data + base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength]; + uint64_t t1 = UIDPerformanceNow(); + + UIDPerformanceAggregate( + @"snapshotSerialisationMS", + UIDMonotonicTimeConvertMachUnitsToMS(t1 - t0)); + + return base64; +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Traversal/UIDHierarchyTraversal.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Traversal/UIDHierarchyTraversal.h new file mode 100644 index 000000000..2b14d615f --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Traversal/UIDHierarchyTraversal.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import +#import "UIDDescriptorRegister.h" + +NS_ASSUME_NONNULL_BEGIN + +@class UIDNode; + +@interface UIDHierarchyTraversal : NSObject + +@property(nonatomic, strong) UIDDescriptorRegister* descriptorRegister; + ++ (instancetype)createWithDescriptorRegister: + (UIDDescriptorRegister*)descriptorRegister; + +- (NSArray*)traverse:(id)root; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Traversal/UIDHierarchyTraversal.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Traversal/UIDHierarchyTraversal.m new file mode 100644 index 000000000..5ce902386 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Traversal/UIDHierarchyTraversal.m @@ -0,0 +1,142 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "UIDHierarchyTraversal.h" +#import "UIDNode.h" + +@interface UIDTransientNode : NSObject + +@property(nonatomic) id node; +@property(nonatomic, nullable) NSNumber* parent; +@property(nonatomic) BOOL shallow; + +- (instancetype)initWithNode:(id)node; +- (instancetype)initWithNode:(id)node parent:(NSNumber*)parent; + +@end + +@implementation UIDTransientNode + +- (instancetype)initWithNode:(id)node { + return [self initWithNode:node parent:nil]; +} + +- (instancetype)initWithNode:(id)node parent:(NSNumber*)parent { + if (self = [super init]) { + _node = node; + _parent = parent; + _shallow = NO; + } + return self; +} + +@end + +@implementation UIDHierarchyTraversal + +- (instancetype)initWithDescriptorRegister: + (UIDDescriptorRegister*)descriptorRegister { + self = [super init]; + if (self) { + self.descriptorRegister = descriptorRegister; + } + return self; +} + ++ (instancetype)createWithDescriptorRegister: + (UIDDescriptorRegister*)descriptorRegister { + return [[UIDHierarchyTraversal alloc] + initWithDescriptorRegister:descriptorRegister]; +} + +- (NSArray*)traverse:(id)root { + if (root == nil) { + return [NSArray array]; + } + + NSMutableArray* nodes = [NSMutableArray new]; + + NSMutableArray* stack = [NSMutableArray new]; + [stack addObject:[[UIDTransientNode alloc] initWithNode:root]]; + + while (stack.count > 0) { + UIDTransientNode* transientNode = [stack lastObject]; + [stack removeLastObject]; + + id node = transientNode.node; + + UIDNodeDescriptor* descriptor = + [self.descriptorRegister descriptorForClass:[node class]]; + + NSUInteger nodeIdentifier = [descriptor identifierForNode:node]; + + UIDNode* uidNode = + [[UIDNode alloc] initWithIdentifier:nodeIdentifier + qualifiedName:[descriptor nameForNode:node] + name:[descriptor nameForNode:node] + bounds:[descriptor boundsForNode:node] + tags:[descriptor tagsForNode:node]]; + + uidNode.inlineAttributes = [descriptor inlineAttributesForNode:node]; + uidNode.hiddenAttributes = [descriptor hiddenAttributesForNode:node]; + uidNode.parent = transientNode.parent; + + if (transientNode.shallow) { + // Not interested in attributes nor traversing the hierarchy formed + // under this node, so children and attributes will be empty. + [nodes addObject:uidNode]; + continue; + } + + NSArray* children = [descriptor childrenOfNode:node]; + id activeChild = [descriptor activeChildForNode:node]; + NSNumber* activeChildId = nil; + if (activeChild != nil) { + UIDNodeDescriptor* activeChildDescriptor = + [self.descriptorRegister descriptorForClass:[activeChild class]]; + NSUInteger childId = + [activeChildDescriptor identifierForNode:activeChild]; + activeChildId = [NSNumber numberWithUnsignedInt:childId]; + } + + NSMutableArray* childrenIds = [NSMutableArray new]; + for (id child in children) { + UIDNodeDescriptor* childDescriptor = + [self.descriptorRegister descriptorForClass:[child class]]; + assert(childDescriptor != nil); + [childrenIds + addObject:[NSNumber + numberWithUnsignedInt:[childDescriptor + identifierForNode:child]]]; + + UIDTransientNode* transientChildNode = [[UIDTransientNode alloc] + initWithNode:child + parent:[NSNumber numberWithUnsignedInt:nodeIdentifier]]; + + // This is a child which is not active, so mark it as to not + // traverse its children. + if (activeChild != nil && activeChild != child) { + transientChildNode.shallow = YES; + } + [stack addObject:transientChildNode]; + } + + uidNode.children = childrenIds; + uidNode.activeChild = activeChildId; + uidNode.attributes = [descriptor attributesForNode:node]; + + [nodes addObject:uidNode]; + } + + return nodes; +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Utilities/UIDMainThread.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Utilities/UIDMainThread.h new file mode 100644 index 000000000..6bead54c4 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Utilities/UIDMainThread.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import + +#ifdef __cplusplus +extern "C" { +#endif +extern void UIDRunBlockOnMainThread(dispatch_block_t block); +extern void UIDRunBlockOnMainThreadAsync(dispatch_block_t block); +extern void UIDRunBlockOnMainThreadAfter( + dispatch_block_t block, + unsigned int seconds); +#ifdef __cplusplus +} +#endif + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Utilities/UIDMainThread.mm b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Utilities/UIDMainThread.mm new file mode 100644 index 000000000..e8f8da29c --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Utilities/UIDMainThread.mm @@ -0,0 +1,37 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "UIDMainThread.h" + +#ifdef __cplusplus +extern "C" { +#endif +void UIDRunBlockOnMainThread(dispatch_block_t block) { + if ([NSThread isMainThread]) { + block(); + } else { + dispatch_sync(dispatch_get_main_queue(), block); + } +} +void UIDRunBlockOnMainThreadAsync(dispatch_block_t block) { + dispatch_async(dispatch_get_main_queue(), block); +} +void UIDRunBlockOnMainThreadAfter( + dispatch_block_t block, + unsigned int seconds) { + dispatch_after( + dispatch_time(DISPATCH_TIME_NOW, seconds * NSEC_PER_SEC), + dispatch_get_main_queue(), + block); +} +#ifdef __cplusplus +} +#endif + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Utilities/UIDPerformance.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Utilities/UIDPerformance.h new file mode 100644 index 000000000..9db39df1b --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Utilities/UIDPerformance.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import + +#ifdef __cplusplus +extern "C" { +#endif +void UIDPerformanceSet(NSString* name, double ms); +void UIDPerformanceAggregate(NSString* name, double ms); +NSDictionary* UIDPerformanceGet(void); +void UIDPerformanceClear(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Utilities/UIDPerformance.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Utilities/UIDPerformance.m new file mode 100644 index 000000000..ea402ddb1 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Utilities/UIDPerformance.m @@ -0,0 +1,49 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#include "UIDPerformance.h" + +#ifdef __cplusplus +extern "C" { +#endif + +static NSMutableDictionary* performance = nil; +static void ensureInitialised(void) { + if (!performance) { + performance = [NSMutableDictionary new]; + } +} + +void UIDPerformanceSet(NSString* name, double ms) { + ensureInitialised(); + [performance setObject:@(ms) forKey:name]; +} +void UIDPerformanceAggregate(NSString* name, double ms) { + ensureInitialised(); + NSNumber* existingMs = [performance objectForKey:name]; + if (existingMs) { + ms += existingMs.doubleValue; + } + + [performance setObject:@(ms) forKey:name]; +} +NSDictionary* UIDPerformanceGet(void) { + ensureInitialised(); + return [performance copy]; +} +void UIDPerformanceClear(void) { + ensureInitialised(); + [performance removeAllObjects]; +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Utilities/UIDSnapshot.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Utilities/UIDSnapshot.h new file mode 100644 index 000000000..ee6101795 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Utilities/UIDSnapshot.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import + +NS_ASSUME_NONNULL_BEGIN +#ifdef __cplusplus +extern "C" { +#endif +UIImage* UIDViewSnapshot(UIView* view); +UIImage* UIDApplicationSnapshot(UIApplication* application, NSArray* windows); +#ifdef __cplusplus +} +#endif +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Utilities/UIDSnapshot.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Utilities/UIDSnapshot.m new file mode 100644 index 000000000..eaf28c2fa --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Utilities/UIDSnapshot.m @@ -0,0 +1,49 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "UIDSnapshot.h" +#import "UIDUIKitObserver.h" + +#ifdef __cplusplus +extern "C" { +#endif +UIImage* UIDViewSnapshot(UIView* view) { + UIGraphicsImageRenderer* renderer = + [[UIGraphicsImageRenderer alloc] initWithSize:view.bounds.size]; + return [renderer imageWithActions:^(UIGraphicsImageRendererContext* context) { + [[UIDUIKitObserver sharedInstance] setDrawObservationEnabled:false]; + [view drawViewHierarchyInRect:view.bounds afterScreenUpdates:true]; + [[UIDUIKitObserver sharedInstance] setDrawObservationEnabled:true]; + }]; +} + +UIImage* UIDApplicationSnapshot(UIApplication* application, NSArray* windows) { + CGSize size = [UIScreen mainScreen].bounds.size; + UIGraphicsImageRenderer* renderer = + [[UIGraphicsImageRenderer alloc] initWithSize:size]; + + [[UIDUIKitObserver sharedInstance] setDrawObservationEnabled:false]; + UIImage* snapshot = [renderer + imageWithActions:^(UIGraphicsImageRendererContext* rendererContext) { + for (UIWindow* window in windows) { + if (window.isHidden) + continue; + [window + drawViewHierarchyInRect:CGRectMake(0, 0, size.width, size.height) + afterScreenUpdates:true]; + } + }]; + [[UIDUIKitObserver sharedInstance] setDrawObservationEnabled:true]; + return snapshot; +} +#ifdef __cplusplus +} +#endif + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Utilities/UIDSwizzle.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Utilities/UIDSwizzle.h new file mode 100644 index 000000000..fd15ae1d3 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Utilities/UIDSwizzle.h @@ -0,0 +1,14 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import + +void UIDSwizzleMethod(Class cls, SEL original, SEL replacement, BOOL create); + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Utilities/UIDSwizzle.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Utilities/UIDSwizzle.m new file mode 100644 index 000000000..56f3062b4 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Utilities/UIDSwizzle.m @@ -0,0 +1,57 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "UIDSwizzle.h" +#import +#import + +void UIDSwizzleMethod(Class cls, SEL original, SEL replacement, BOOL create) { + Method originalMethod = class_getInstanceMethod(cls, original); + Method replacementMethod = class_getInstanceMethod(cls, replacement); + + /** + The method is not implemented by the target class and the create option is + set to false. + */ + if (originalMethod == NULL && !create) { + return; + } + + /** + Use class_addMethod to add an implementation of the method to the target + class, which we do using our replacement implementation. + */ + BOOL didAddMethod = class_addMethod( + cls, + original, + method_getImplementation(replacementMethod), + method_getTypeEncoding(replacementMethod)); + + if (didAddMethod) { + /** + Use class_replaceMethod to replace calls to the swizzled method to the + original one. i.e. when you call the replacement method, it actually calls + the original method. + */ + class_replaceMethod( + cls, + replacement, + method_getImplementation(originalMethod), + method_getTypeEncoding(originalMethod)); + } else { + /** + If the method is defined by the target class, class_addMethod will fail. + Use method_exchangeImplementations to exchange the new and old + implementations. + */ + method_exchangeImplementations(originalMethod, replacementMethod); + } +} + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Utilities/UIDThrottle.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Utilities/UIDThrottle.h new file mode 100644 index 000000000..7d1a840f3 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Utilities/UIDThrottle.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface UIDThrottle : NSObject + +@property(nonatomic, readonly) int64_t rate; + +- (instancetype)initWithRate:(int64_t)rate; +- (instancetype)initWithRate:(int64_t)rate + queue:(nonnull dispatch_queue_t)queue; + +- (void)run:(dispatch_block_t)block; +- (void)cancel; + +@end + +NS_ASSUME_NONNULL_END + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Utilities/UIDThrottle.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Utilities/UIDThrottle.m new file mode 100644 index 000000000..b422d4e7f --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Utilities/UIDThrottle.m @@ -0,0 +1,73 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "UIDThrottle.h" + +@interface UIDThrottle () { + dispatch_queue_t _queue; + dispatch_block_t _block; + dispatch_block_t _throttleJob; +} +@end + +@implementation UIDThrottle + +- (instancetype)initWithRate:(int64_t)rate { + return [self initWithRate:rate queue:dispatch_get_main_queue()]; +} + +- (instancetype)initWithRate:(int64_t)rate + queue:(nonnull dispatch_queue_t)queue { + self = [super init]; + if (self) { + _rate = rate; + _queue = queue; + _block = nil; + } + return self; +} + +- (void)run:(dispatch_block_t)block { + /** + Check if there's a block already queued. If there's none, create one. + The reason we have two blocks: + _throttleJob captures the incoming job, always. + _block is the one currently in the queue, which will execute after a + delay. This last block is only queued when there's none, it will clear + itself after executing. + */ + _throttleJob = block; + + if (_block == nil) { + __weak typeof(self) weakSelf = self; + _block = ^(void) { + if (weakSelf) { + typeof(self) strongSelf = weakSelf; + strongSelf->_throttleJob(); + + strongSelf->_throttleJob = nil; + strongSelf->_block = nil; + } + }; + + dispatch_after( + dispatch_time(DISPATCH_TIME_NOW, _rate * NSEC_PER_MSEC), + _queue, + _block); + } +} + +- (void)cancel { + _throttleJob = nil; + _block = nil; +} + +@end + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Utilities/UIDTimeUtilities.h b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Utilities/UIDTimeUtilities.h new file mode 100644 index 000000000..6a88e7d43 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Utilities/UIDTimeUtilities.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import + +#ifdef __cplusplus +extern "C" { +#endif + +double UIDTimeIntervalToMS(NSTimeInterval timeInterval); +double UIDPerformanceTimeIntervalSince1970(void); +double UIDMonotonicTimeConvertMachUnitsToMS(uint64_t elapsed); +uint64_t UIDPerformanceNow(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Utilities/UIDTimeUtilities.m b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Utilities/UIDTimeUtilities.m new file mode 100644 index 000000000..31918a2d0 --- /dev/null +++ b/iOS/Plugins/FlipperKitUIDebuggerPlugin/FlipperKitUIDebuggerPlugin/Utilities/UIDTimeUtilities.m @@ -0,0 +1,52 @@ +/* + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#if FB_SONARKIT_ENABLED + +#import "UIDTimeUtilities.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +static mach_timebase_info_data_t _get_timebase(void) { + static mach_timebase_info_data_t machInfo; + static dispatch_once_t machInfoOnceToken = 0; + dispatch_once(&machInfoOnceToken, ^{ + const int ret = mach_timebase_info(&machInfo); + if (ret != KERN_SUCCESS) { + machInfo.numer = 0; + machInfo.denom = 1; + } + }); + return machInfo; +} + +double UIDMonotonicTimeConvertMachUnitsToMS(uint64_t elapsed) { + const mach_timebase_info_data_t tb_info = _get_timebase(); + return (double)elapsed * (double)tb_info.numer / (double)tb_info.denom / + (1000.0 * 1000.0); +} + +double UIDPerformanceTimeIntervalSince1970(void) { + return [[NSDate date] timeIntervalSince1970]; +} + +double UIDTimeIntervalToMS(NSTimeInterval timeInterval) { + return (timeInterval * 1000); +} + +uint64_t UIDPerformanceNow(void) { + return mach_absolute_time(); +} + +#ifdef __cplusplus +} +#endif + +#endif