|
1 | | -#import "SentryViewHierarchyProvider.h" |
2 | | - |
3 | | -#if SENTRY_HAS_UIKIT |
4 | | - |
5 | | -# import "SentryCrashFileUtils.h" |
6 | | -# import "SentryCrashJSONCodec.h" |
7 | | -# import "SentryLogC.h" |
8 | | -# import "SentrySwift.h" |
9 | | -# import <UIKit/UIKit.h> |
10 | | - |
11 | | -static int |
12 | | -writeJSONDataToFile(const char *const data, const int length, void *const userData) |
13 | | -{ |
14 | | - const int fd = *((int *)userData); |
15 | | - const bool success = sentrycrashfu_writeBytesToFD(fd, data, length); |
16 | | - return success ? SentryCrashJSON_OK : SentryCrashJSON_ERROR_CANNOT_ADD_DATA; |
17 | | -} |
18 | | - |
19 | | -static int |
20 | | -writeJSONDataToMemory(const char *const data, const int length, void *const userData) |
21 | | -{ |
22 | | - NSMutableData *memory = ((__bridge NSMutableData *)userData); |
23 | | - [memory appendBytes:data length:length]; |
24 | | - return SentryCrashJSON_OK; |
25 | | -} |
26 | | - |
27 | | -@interface SentryViewHierarchyProvider () |
28 | | - |
29 | | -@property (nonatomic, strong) SentryDispatchQueueWrapper *dispatchQueueWrapper; |
30 | | -@property (nonatomic, strong) id<SentryApplication> sentryUIApplication; |
31 | | - |
32 | | -@end |
33 | | - |
34 | | -@implementation SentryViewHierarchyProvider |
35 | | - |
36 | | -- (instancetype)initWithDispatchQueueWrapper:(SentryDispatchQueueWrapper *)dispatchQueueWrapper |
37 | | - sentryUIApplication:(id<SentryApplication>)sentryUIApplication |
38 | | -{ |
39 | | - if (self = [super init]) { |
40 | | - self.reportAccessibilityIdentifier = YES; |
41 | | - self.dispatchQueueWrapper = dispatchQueueWrapper; |
42 | | - self.sentryUIApplication = sentryUIApplication; |
43 | | - } |
44 | | - return self; |
45 | | -} |
46 | | - |
47 | | -- (BOOL)saveViewHierarchy:(NSString *)filePath |
48 | | -{ |
49 | | - NSArray<UIWindow *> *windows = [self.sentryUIApplication getWindows]; |
50 | | - |
51 | | - const char *path = [filePath UTF8String]; |
52 | | - int fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0644); |
53 | | - if (fd < 0) { |
54 | | - SENTRY_LOG_DEBUG(@"Could not open file %s for writing: %s", path, strerror(errno)); |
55 | | - return NO; |
56 | | - } |
57 | | - |
58 | | - BOOL result = [self processViewHierarchy:windows addFunction:writeJSONDataToFile userData:&fd]; |
59 | | - |
60 | | - close(fd); |
61 | | - return result; |
62 | | -} |
63 | | - |
64 | | -- (NSData *)appViewHierarchyFromMainThread |
65 | | -{ |
66 | | - __block NSData *result; |
67 | | - |
68 | | - void (^fetchViewHierarchy)(void) = ^{ result = [self appViewHierarchy]; }; |
69 | | - |
70 | | - SENTRY_LOG_INFO(@"Starting to fetch the view hierarchy from the main thread."); |
71 | | - |
72 | | - [self.dispatchQueueWrapper dispatchSyncOnMainQueue:fetchViewHierarchy]; |
73 | | - |
74 | | - SENTRY_LOG_INFO(@"Finished fetching the view hierarchy from the main thread."); |
75 | | - |
76 | | - return result; |
77 | | -} |
78 | | - |
79 | | -- (NSData *)appViewHierarchy |
80 | | -{ |
81 | | - NSMutableData *result = [[NSMutableData alloc] init]; |
82 | | - NSArray<UIWindow *> *windows = [self.sentryUIApplication getWindows]; |
83 | | - |
84 | | - if (![self processViewHierarchy:windows |
85 | | - addFunction:writeJSONDataToMemory |
86 | | - userData:(__bridge void *)(result)]) { |
87 | | - |
88 | | - result = nil; |
89 | | - } |
90 | | - |
91 | | - return result; |
92 | | -} |
93 | | - |
94 | | -# define tryJson(code) \ |
95 | | - if ((result = (code)) != SentryCrashJSON_OK) \ |
96 | | - return result; |
97 | | - |
98 | | -- (BOOL)processViewHierarchy:(NSArray<UIView *> *)windows |
99 | | - addFunction:(SentryCrashJSONAddDataFunc)addJSONDataFunc |
100 | | - userData:(void *const)userData |
101 | | -{ |
102 | | - |
103 | | - __block SentryCrashJSONEncodeContext JSONContext; |
104 | | - sentrycrashjson_beginEncode(&JSONContext, NO, addJSONDataFunc, userData); |
105 | | - |
106 | | - SENTRY_LOG_DEBUG(@"Processing view hierarchy."); |
107 | | - |
108 | | - int (^serializeJson)(void) = ^int() { |
109 | | - int result; |
110 | | - tryJson(sentrycrashjson_beginObject(&JSONContext, NULL)); |
111 | | - tryJson(sentrycrashjson_addStringElement( |
112 | | - &JSONContext, "rendering_system", "UIKIT", SentryCrashJSON_SIZE_AUTOMATIC)); |
113 | | - tryJson(sentrycrashjson_beginArray(&JSONContext, "windows")); |
114 | | - |
115 | | - for (UIView *window in windows) { |
116 | | - tryJson([self viewHierarchyFromView:window intoContext:&JSONContext]); |
117 | | - } |
118 | | - |
119 | | - tryJson(sentrycrashjson_endContainer(&JSONContext)); |
120 | | - |
121 | | - result = sentrycrashjson_endEncode(&JSONContext); |
122 | | - return result; |
123 | | - }; |
124 | | - |
125 | | - int result = serializeJson(); |
126 | | - if (result != SentryCrashJSON_OK) { |
127 | | - SENTRY_LOG_DEBUG( |
128 | | - @"Could not create view hierarchy json: %s", sentrycrashjson_stringForError(result)); |
129 | | - return NO; |
130 | | - } |
131 | | - return YES; |
132 | | -} |
133 | | - |
134 | | -- (int)viewHierarchyFromView:(UIView *)view intoContext:(SentryCrashJSONEncodeContext *)context |
135 | | -{ |
136 | | - SENTRY_LOG_DEBUG(@"Processing view hierarchy of view: %@", view); |
137 | | - |
138 | | - int result = 0; |
139 | | - tryJson(sentrycrashjson_beginObject(context, NULL)); |
140 | | - const char *viewClassName = [[SwiftDescriptor getObjectClassName:view] UTF8String]; |
141 | | - tryJson(sentrycrashjson_addStringElement( |
142 | | - context, "type", viewClassName, SentryCrashJSON_SIZE_AUTOMATIC)); |
143 | | - |
144 | | - if (self.reportAccessibilityIdentifier && view.accessibilityIdentifier |
145 | | - && view.accessibilityIdentifier.length != 0) { |
146 | | - tryJson(sentrycrashjson_addStringElement(context, "identifier", |
147 | | - view.accessibilityIdentifier.UTF8String, SentryCrashJSON_SIZE_AUTOMATIC)); |
148 | | - } |
149 | | - |
150 | | - tryJson(sentrycrashjson_addFloatingPointElement(context, "width", view.frame.size.width)); |
151 | | - tryJson(sentrycrashjson_addFloatingPointElement(context, "height", view.frame.size.height)); |
152 | | - tryJson(sentrycrashjson_addFloatingPointElement(context, "x", view.frame.origin.x)); |
153 | | - tryJson(sentrycrashjson_addFloatingPointElement(context, "y", view.frame.origin.y)); |
154 | | - tryJson(sentrycrashjson_addFloatingPointElement(context, "alpha", view.alpha)); |
155 | | - tryJson(sentrycrashjson_addBooleanElement(context, "visible", !view.hidden)); |
156 | | - |
157 | | - if ([view.nextResponder isKindOfClass:[UIViewController class]]) { |
158 | | - UIViewController *vc = (UIViewController *)view.nextResponder; |
159 | | - if (vc.view == view) { |
160 | | - const char *viewControllerClassName = |
161 | | - [[SwiftDescriptor getViewControllerClassName:vc] UTF8String]; |
162 | | - tryJson(sentrycrashjson_addStringElement(context, "view_controller", |
163 | | - viewControllerClassName, SentryCrashJSON_SIZE_AUTOMATIC)); |
164 | | - } |
165 | | - } |
166 | | - |
167 | | - tryJson(sentrycrashjson_beginArray(context, "children")); |
168 | | - for (UIView *child in view.subviews) { |
169 | | - tryJson([self viewHierarchyFromView:child intoContext:context]); |
170 | | - } |
171 | | - tryJson(sentrycrashjson_endContainer(context)); |
172 | | - tryJson(sentrycrashjson_endContainer(context)); |
173 | | - return result; |
174 | | -} |
175 | | - |
176 | | -@end |
177 | | - |
178 | | -#endif // SENTRY_HAS_UIKIT |
| 1 | +// #import "SentryViewHierarchyProvider.h" |
| 2 | +// |
| 3 | +// #if SENTRY_HAS_UIKIT |
| 4 | +// |
| 5 | +// # import "SentryCrashFileUtils.h" |
| 6 | +// # import "SentryCrashJSONCodec.h" |
| 7 | +// # import "SentryLogC.h" |
| 8 | +// # import "SentrySwift.h" |
| 9 | +// # import <UIKit/UIKit.h> |
| 10 | +// |
| 11 | +// static int |
| 12 | +// writeJSONDataToFile(const char *const data, const int length, void *const userData) |
| 13 | +//{ |
| 14 | +// const int fd = *((int *)userData); |
| 15 | +// const bool success = sentrycrashfu_writeBytesToFD(fd, data, length); |
| 16 | +// return success ? SentryCrashJSON_OK : SentryCrashJSON_ERROR_CANNOT_ADD_DATA; |
| 17 | +// } |
| 18 | +// |
| 19 | +// static int |
| 20 | +// writeJSONDataToMemory(const char *const data, const int length, void *const userData) |
| 21 | +//{ |
| 22 | +// NSMutableData *memory = ((__bridge NSMutableData *)userData); |
| 23 | +// [memory appendBytes:data length:length]; |
| 24 | +// return SentryCrashJSON_OK; |
| 25 | +// } |
| 26 | +// |
| 27 | +//@interface SentryViewHierarchyProvider () |
| 28 | +// |
| 29 | +//@property (nonatomic, strong) SentryDispatchQueueWrapper *dispatchQueueWrapper; |
| 30 | +//@property (nonatomic, strong) id<SentryApplication> sentryUIApplication; |
| 31 | +// |
| 32 | +//@end |
| 33 | +// |
| 34 | +//@implementation SentryViewHierarchyProvider |
| 35 | +// |
| 36 | +//- (instancetype)initWithDispatchQueueWrapper:(SentryDispatchQueueWrapper *)dispatchQueueWrapper |
| 37 | +// sentryUIApplication:(id<SentryApplication>)sentryUIApplication |
| 38 | +//{ |
| 39 | +// if (self = [super init]) { |
| 40 | +// self.reportAccessibilityIdentifier = YES; |
| 41 | +// self.dispatchQueueWrapper = dispatchQueueWrapper; |
| 42 | +// self.sentryUIApplication = sentryUIApplication; |
| 43 | +// } |
| 44 | +// return self; |
| 45 | +// } |
| 46 | +// |
| 47 | +//- (BOOL)saveViewHierarchy:(NSString *)filePath |
| 48 | +//{ |
| 49 | +// NSArray<UIWindow *> *windows = [self.sentryUIApplication getWindows]; |
| 50 | +// |
| 51 | +// const char *path = [filePath UTF8String]; |
| 52 | +// int fd = open(path, O_RDWR | O_CREAT | O_TRUNC, 0644); |
| 53 | +// if (fd < 0) { |
| 54 | +// SENTRY_LOG_DEBUG(@"Could not open file %s for writing: %s", path, strerror(errno)); |
| 55 | +// return NO; |
| 56 | +// } |
| 57 | +// |
| 58 | +// BOOL result = [self processViewHierarchy:windows addFunction:writeJSONDataToFile |
| 59 | +// userData:&fd]; |
| 60 | +// |
| 61 | +// close(fd); |
| 62 | +// return result; |
| 63 | +// } |
| 64 | +// |
| 65 | +//- (NSData *)appViewHierarchyFromMainThread |
| 66 | +//{ |
| 67 | +// __block NSData *result; |
| 68 | +// |
| 69 | +// void (^fetchViewHierarchy)(void) = ^{ result = [self appViewHierarchy]; }; |
| 70 | +// |
| 71 | +// SENTRY_LOG_INFO(@"Starting to fetch the view hierarchy from the main thread."); |
| 72 | +// |
| 73 | +// [self.dispatchQueueWrapper dispatchSyncOnMainQueue:fetchViewHierarchy]; |
| 74 | +// |
| 75 | +// SENTRY_LOG_INFO(@"Finished fetching the view hierarchy from the main thread."); |
| 76 | +// |
| 77 | +// return result; |
| 78 | +// } |
| 79 | +// |
| 80 | +//- (NSData *)appViewHierarchy |
| 81 | +//{ |
| 82 | +// NSMutableData *result = [[NSMutableData alloc] init]; |
| 83 | +// NSArray<UIWindow *> *windows = [self.sentryUIApplication getWindows]; |
| 84 | +// |
| 85 | +// if (![self processViewHierarchy:windows |
| 86 | +// addFunction:writeJSONDataToMemory |
| 87 | +// userData:(__bridge void *)(result)]) { |
| 88 | +// |
| 89 | +// result = nil; |
| 90 | +// } |
| 91 | +// |
| 92 | +// return result; |
| 93 | +// } |
| 94 | +// |
| 95 | +// # define tryJson(code) \ |
| 96 | +// if ((result = (code)) != SentryCrashJSON_OK) \ |
| 97 | +// return result; |
| 98 | +// |
| 99 | +//- (BOOL)processViewHierarchy:(NSArray<UIView *> *)windows |
| 100 | +// addFunction:(SentryCrashJSONAddDataFunc)addJSONDataFunc |
| 101 | +// userData:(void *const)userData |
| 102 | +//{ |
| 103 | +// |
| 104 | +// __block SentryCrashJSONEncodeContext JSONContext; |
| 105 | +// sentrycrashjson_beginEncode(&JSONContext, NO, addJSONDataFunc, userData); |
| 106 | +// |
| 107 | +// SENTRY_LOG_DEBUG(@"Processing view hierarchy."); |
| 108 | +// |
| 109 | +// int (^serializeJson)(void) = ^int() { |
| 110 | +// int result; |
| 111 | +// tryJson(sentrycrashjson_beginObject(&JSONContext, NULL)); |
| 112 | +// tryJson(sentrycrashjson_addStringElement( |
| 113 | +// &JSONContext, "rendering_system", "UIKIT", SentryCrashJSON_SIZE_AUTOMATIC)); |
| 114 | +// tryJson(sentrycrashjson_beginArray(&JSONContext, "windows")); |
| 115 | +// |
| 116 | +// for (UIView *window in windows) { |
| 117 | +// tryJson([self viewHierarchyFromView:window intoContext:&JSONContext]); |
| 118 | +// } |
| 119 | +// |
| 120 | +// tryJson(sentrycrashjson_endContainer(&JSONContext)); |
| 121 | +// |
| 122 | +// result = sentrycrashjson_endEncode(&JSONContext); |
| 123 | +// return result; |
| 124 | +// }; |
| 125 | +// |
| 126 | +// int result = serializeJson(); |
| 127 | +// if (result != SentryCrashJSON_OK) { |
| 128 | +// SENTRY_LOG_DEBUG( |
| 129 | +// @"Could not create view hierarchy json: %s", sentrycrashjson_stringForError(result)); |
| 130 | +// return NO; |
| 131 | +// } |
| 132 | +// return YES; |
| 133 | +// } |
| 134 | +// |
| 135 | +//- (int)viewHierarchyFromView:(UIView *)view intoContext:(SentryCrashJSONEncodeContext *)context |
| 136 | +//{ |
| 137 | +// SENTRY_LOG_DEBUG(@"Processing view hierarchy of view: %@", view); |
| 138 | +// |
| 139 | +// int result = 0; |
| 140 | +// tryJson(sentrycrashjson_beginObject(context, NULL)); |
| 141 | +// const char *viewClassName = [[SwiftDescriptor getObjectClassName:view] UTF8String]; |
| 142 | +// tryJson(sentrycrashjson_addStringElement( |
| 143 | +// context, "type", viewClassName, SentryCrashJSON_SIZE_AUTOMATIC)); |
| 144 | +// |
| 145 | +// if (self.reportAccessibilityIdentifier && view.accessibilityIdentifier |
| 146 | +// && view.accessibilityIdentifier.length != 0) { |
| 147 | +// tryJson(sentrycrashjson_addStringElement(context, "identifier", |
| 148 | +// view.accessibilityIdentifier.UTF8String, SentryCrashJSON_SIZE_AUTOMATIC)); |
| 149 | +// } |
| 150 | +// |
| 151 | +// tryJson(sentrycrashjson_addFloatingPointElement(context, "width", view.frame.size.width)); |
| 152 | +// tryJson(sentrycrashjson_addFloatingPointElement(context, "height", view.frame.size.height)); |
| 153 | +// tryJson(sentrycrashjson_addFloatingPointElement(context, "x", view.frame.origin.x)); |
| 154 | +// tryJson(sentrycrashjson_addFloatingPointElement(context, "y", view.frame.origin.y)); |
| 155 | +// tryJson(sentrycrashjson_addFloatingPointElement(context, "alpha", view.alpha)); |
| 156 | +// tryJson(sentrycrashjson_addBooleanElement(context, "visible", !view.hidden)); |
| 157 | +// |
| 158 | +// if ([view.nextResponder isKindOfClass:[UIViewController class]]) { |
| 159 | +// UIViewController *vc = (UIViewController *)view.nextResponder; |
| 160 | +// if (vc.view == view) { |
| 161 | +// const char *viewControllerClassName = |
| 162 | +// [[SwiftDescriptor getViewControllerClassName:vc] UTF8String]; |
| 163 | +// tryJson(sentrycrashjson_addStringElement(context, "view_controller", |
| 164 | +// viewControllerClassName, SentryCrashJSON_SIZE_AUTOMATIC)); |
| 165 | +// } |
| 166 | +// } |
| 167 | +// |
| 168 | +// tryJson(sentrycrashjson_beginArray(context, "children")); |
| 169 | +// for (UIView *child in view.subviews) { |
| 170 | +// tryJson([self viewHierarchyFromView:child intoContext:context]); |
| 171 | +// } |
| 172 | +// tryJson(sentrycrashjson_endContainer(context)); |
| 173 | +// tryJson(sentrycrashjson_endContainer(context)); |
| 174 | +// return result; |
| 175 | +// } |
| 176 | +// |
| 177 | +//@end |
| 178 | +// |
| 179 | +// #endif // SENTRY_HAS_UIKIT |
0 commit comments