-
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Restructured project Added an AppKit demo project
- Loading branch information
Showing
24 changed files
with
2,867 additions
and
831 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
16 changes: 16 additions & 0 deletions
16
LNViewHierarchyDumper/LNViewHierarchyDumper/LNViewHierarchyDumper+LibraryDebug.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
// | ||
// LNViewHierarchyDumper+LibraryDebug.h | ||
// | ||
// | ||
// Created by Leo Natan on 02/08/2023. | ||
// | ||
|
||
#import "LNViewHierarchyDumper-Private.h" | ||
|
||
NS_ASSUME_NONNULL_BEGIN | ||
|
||
@interface LNViewHierarchyDumper (LibraryDebug) | ||
|
||
@end | ||
|
||
NS_ASSUME_NONNULL_END |
85 changes: 85 additions & 0 deletions
85
LNViewHierarchyDumper/LNViewHierarchyDumper/LNViewHierarchyDumper+LibraryDebug.m
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
// | ||
// LNViewHierarchyDumper+LibraryDebug.m | ||
// | ||
// | ||
// Created by Leo Natan on 02/08/2023. | ||
// | ||
|
||
#import "LNViewHierarchyDumper+LibraryDebug.h" | ||
|
||
//#if DEBUG | ||
//@import ObjectiveC; | ||
//@import Darwin; | ||
//@import MachO.dyld; | ||
//#endif | ||
|
||
@implementation LNViewHierarchyDumper (LibraryDebug) | ||
|
||
//#if DEBUG | ||
//static void func(const struct mach_header* mh, intptr_t vmaddr_slide) | ||
//{ | ||
// Dl_info DlInfo; | ||
// dladdr(mh, &DlInfo); | ||
// const char* image_name = DlInfo.dli_fname; | ||
// | ||
// NSLog(@"%s", image_name); | ||
//} | ||
// | ||
// | ||
//static NSUInteger requestCounter = 0; | ||
//static NSUInteger responseCounter = 0; | ||
// | ||
//__attribute__((constructor)) | ||
//static void zzz(void) | ||
//{ | ||
// //We are looking for: | ||
// // DebugHierarchyFoundation.framework | ||
// // libViewDebuggerSupport.dylib | ||
// _dyld_register_func_for_add_image(func); | ||
// | ||
// //The following code will dump requests made by Xcode View Hierarchy Inspector (and this framework) to ~/Desktop/Request_x.json | ||
// { | ||
// Class cls = NSClassFromString(@"DebugHierarchyRequest"); | ||
// SEL sel = NSSelectorFromString(@"requestWithBase64Data:error:"); | ||
// Method m = class_getClassMethod(cls, sel); | ||
// id(*orig)(id, SEL, NSString*, NSError**) = (void*)method_getImplementation(m); | ||
// | ||
// method_setImplementation(m, imp_implementationWithBlock(^(id _self, NSString* request, NSError** error) { | ||
// NSData* b64Data = [[NSData alloc] initWithBase64EncodedString:request options:0]; | ||
// NSString* homePath = NSHomeDirectory(); | ||
//#if TARGET_OS_SIMULATOR | ||
// homePath = [homePath substringToIndex:[homePath rangeOfString:@"/Library"].location]; | ||
//#endif | ||
// NSLog(@"%@", homePath); | ||
// NSURL* outputURL = [[NSURL fileURLWithPath:homePath] URLByAppendingPathComponent:[NSString stringWithFormat:@"Desktop/Request_%@.json", @(requestCounter++)]]; | ||
// [b64Data writeToURL:outputURL atomically:YES]; | ||
// | ||
// return orig(_self, sel, request, error); | ||
// })); | ||
// } | ||
// | ||
// { | ||
// Class cls = NSClassFromString(@"DebugHierarchyTargetHub"); | ||
// SEL sel = NSSelectorFromString(@"performRequest:error:"); | ||
// Method m = class_getInstanceMethod(cls, sel); | ||
// NSData*(*orig)(id, SEL, id, NSError**) = (void*)method_getImplementation(m); | ||
// method_setImplementation(m, imp_implementationWithBlock(^(id _self, id request, NSError** error) { | ||
// NSData* rv = orig(_self, sel, request, error); | ||
// | ||
// if(rv != nil) | ||
// { | ||
// NSString* homePath = NSHomeDirectory(); | ||
//#if TARGET_OS_SIMULATOR | ||
// homePath = [homePath substringToIndex:[homePath rangeOfString:@"/Library"].location]; | ||
//#endif | ||
// NSURL* outputURL = [[NSURL fileURLWithPath:homePath] URLByAppendingPathComponent:[NSString stringWithFormat:@"Desktop/Response_%@.json", @(responseCounter++)]]; | ||
// [rv writeToURL:outputURL atomically:YES]; | ||
// } | ||
// | ||
// return rv; | ||
// })); | ||
// } | ||
//} | ||
//#endif | ||
|
||
@end |
59 changes: 59 additions & 0 deletions
59
LNViewHierarchyDumper/LNViewHierarchyDumper/LNViewHierarchyDumper+LibrarySupport.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
// | ||
// LNViewHierarchyDumper+LibrarySupport.h | ||
// ViewHierarchyDumpTester | ||
// | ||
// Created by Leo Natan on 02/08/2023. | ||
// | ||
|
||
#import "LNViewHierarchyDumper.h" | ||
|
||
NS_ASSUME_NONNULL_BEGIN | ||
|
||
#if TARGET_OS_IPHONE && !TARGET_OS_MACCATALYST | ||
|
||
@interface NSTask : NSObject @end | ||
|
||
@interface NSTask () | ||
|
||
@property (nullable, copy) NSURL* executableURL; | ||
@property (nullable, copy) NSArray<NSString*>* arguments; | ||
@property (nullable, copy) NSDictionary<NSString*, NSString*>* environment; | ||
@property (nullable, retain) id standardOutput; | ||
@property (nullable, retain) id standardError; | ||
|
||
@property(readonly) int terminationStatus; | ||
|
||
@property(copy, nonnull) void (^terminationHandler)(NSTask* _Nonnull task); | ||
|
||
- (BOOL)launch; | ||
|
||
@end | ||
|
||
#endif | ||
|
||
@interface NSObject () | ||
|
||
//DBGTargetHub | ||
+ (id)sharedHub; | ||
- (NSData*)performRequestWithRequestInBase64:(NSString*)arg1; | ||
|
||
//DebugHierarchyTargetHub | ||
- (nullable id)performRequest:(id /*DBGTargetHub*/)arg1 error:(NSError**)error; | ||
|
||
//DebugHierarchyRequest | ||
+ (nullable id)requestWithBase64Data:(NSString*)arg1 error:(NSError**)arg2; | ||
|
||
//NSTask | ||
- (BOOL)launchAndReturnError:(out NSError **_Nullable)error; | ||
|
||
@end | ||
|
||
@interface LNViewHierarchyDumper (LibrarySupport) | ||
|
||
+ (NSURL*)_xcodeURLOrError:(out NSError* __strong * _Nullable)outError; | ||
+ (nullable NSURL*)_debugHierarchyFoundationFrameworkURL:(NSURL*)runtimeURL error:(out NSError** _Nullable)error; | ||
+ (nullable NSURL*)_libViewDebuggerSupportURL:(NSURL*)runtimeURL error:(out NSError** _Nullable)error; | ||
|
||
@end | ||
|
||
NS_ASSUME_NONNULL_END |
212 changes: 212 additions & 0 deletions
212
LNViewHierarchyDumper/LNViewHierarchyDumper/LNViewHierarchyDumper+LibrarySupport.m
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,212 @@ | ||
// | ||
// LNViewHierarchyDumper+LibrarySupport.m | ||
// ViewHierarchyDumpTester | ||
// | ||
// Created by Leo Natan on 02/08/2023. | ||
// | ||
|
||
#import "LNViewHierarchyDumper+LibrarySupport.h" | ||
|
||
@implementation LNViewHierarchyDumper (LibrarySupport) | ||
|
||
+ (BOOL)_isMacSandboxed | ||
{ | ||
return NSProcessInfo.processInfo.environment[@"APP_SANDBOX_CONTAINER_ID"].length > 0; | ||
} | ||
|
||
+ (NSURL*)_xcodeURLOrError:(out NSError* __strong * _Nullable)outError | ||
{ | ||
static NSURL* rv = nil; | ||
static NSError* error = nil; | ||
static dispatch_once_t onceToken; | ||
dispatch_once(&onceToken, ^{ | ||
if(self._isMacSandboxed) | ||
{ | ||
rv = [NSURL fileURLWithPath:@"/Applications/Xcode.app/Contents"]; | ||
if([rv checkResourceIsReachableAndReturnError:NULL] == NO) | ||
{ | ||
rv = [NSURL fileURLWithPath:@"/Applications/Xcode-beta.app/Contents"]; | ||
if([rv checkResourceIsReachableAndReturnError:NULL] == NO) | ||
{ | ||
rv = NULL; | ||
error = [NSError errorWithDomain:@"LNViewHierarchyDumperDomain" code:0 userInfo:@{NSLocalizedDescriptionKey: @"App is sandboxed and cannot find Xcode in\n/Applications/Xcode.app\nor\n/Applications/Xcode-beta.app"}]; | ||
return; | ||
} | ||
} | ||
} | ||
else | ||
{ | ||
NSTask* whichXcodeTask = [NSTask new]; | ||
[whichXcodeTask setValue:[NSURL fileURLWithPath:@"/usr/bin/xcode-select"] forKey:@"executableURL"]; | ||
whichXcodeTask.arguments = @[@"-p"]; | ||
whichXcodeTask.environment = @{}; | ||
|
||
NSPipe* outPipe = [NSPipe pipe]; | ||
NSMutableData* outData = [NSMutableData new]; | ||
whichXcodeTask.standardOutput = outPipe; | ||
outPipe.fileHandleForReading.readabilityHandler = ^(NSFileHandle * _Nonnull fileHandle) { | ||
[outData appendData:fileHandle.availableData]; | ||
}; | ||
|
||
NSPipe* errPipe = [NSPipe pipe]; | ||
NSMutableData* errData = [NSMutableData new]; | ||
whichXcodeTask.standardError = errPipe; | ||
errPipe.fileHandleForReading.readabilityHandler = ^(NSFileHandle * _Nonnull fileHandle) { | ||
[errData appendData:fileHandle.availableData]; | ||
}; | ||
|
||
dispatch_semaphore_t waitForTermination = dispatch_semaphore_create(0); | ||
|
||
[whichXcodeTask setValue:^(NSTask* _Nonnull task) { | ||
outPipe.fileHandleForReading.readabilityHandler = nil; | ||
errPipe.fileHandleForReading.readabilityHandler = nil; | ||
|
||
dispatch_semaphore_signal(waitForTermination); | ||
} forKey:@"terminationHandler"]; | ||
|
||
NSError* taskError; | ||
[(id)whichXcodeTask launchAndReturnError:&taskError]; | ||
|
||
if(taskError == nil) | ||
{ | ||
dispatch_semaphore_wait(waitForTermination, DISPATCH_TIME_FOREVER); | ||
} | ||
else | ||
{ | ||
error = taskError; | ||
return; | ||
} | ||
|
||
if(whichXcodeTask.terminationStatus != 0) | ||
{ | ||
NSString* errString = [[NSString alloc] initWithData:errData encoding:NSUTF8StringEncoding]; | ||
error = [NSError errorWithDomain:@"LNViewHierarchyDumperDomain" code:0 userInfo:@{NSLocalizedDescriptionKey: errString}]; | ||
return; | ||
} | ||
|
||
NSString* xcodePath = [[[NSString alloc] initWithData:outData encoding:NSUTF8StringEncoding] stringByTrimmingCharactersInSet:NSCharacterSet.whitespaceAndNewlineCharacterSet]; | ||
|
||
if(xcodePath.length == 0) | ||
{ | ||
error = [NSError errorWithDomain:@"LNViewHierarchyDumperDomain" code:0 userInfo:@{NSLocalizedDescriptionKey: @"Unable to find the current active developer directory"}]; | ||
|
||
return; | ||
} | ||
|
||
rv = [[[NSURL fileURLWithPath:xcodePath] URLByAppendingPathComponent:@".."] URLByStandardizingPath]; | ||
} | ||
}); | ||
if(outError != NULL) | ||
{ | ||
*outError = error; | ||
} | ||
return rv; | ||
} | ||
|
||
+ (NSURL*)_debugHierarchyFoundationFrameworkURL_iOS:(NSURL*)runtimeURL | ||
{ | ||
NSString* rv = nil; | ||
if(@available(iOS 17, *)) | ||
{ | ||
rv = @"System/Library/PrivateFrameworks/DebugHierarchyFoundation.framework"; | ||
} | ||
else | ||
{ | ||
rv = @"Developer/Library/PrivateFrameworks/DebugHierarchyFoundation.framework"; | ||
} | ||
#if TARGET_OS_SIMULATOR | ||
return [runtimeURL URLByAppendingPathComponent:rv]; | ||
#else | ||
return [NSURL fileURLWithPath:[NSString stringWithFormat:@"/%@", rv]]; | ||
#endif | ||
} | ||
|
||
+ (NSURL*)_debugHierarchyFoundationFrameworkURL_macOS:(NSURL*)runtimeURL isCatalyst:(BOOL)isCatalyst | ||
{ | ||
return [runtimeURL URLByAppendingPathComponent:@"SharedFrameworks/DebugHierarchyFoundation.framework"]; | ||
} | ||
|
||
+ (nullable NSURL*)_debugHierarchyFoundationFrameworkURL:(NSURL*)runtimeURL error:(out NSError** _Nullable)error | ||
{ | ||
#if TARGET_OS_IPHONE && !TARGET_OS_MACCATALYST | ||
if (@available(iOS 14.0, *)) | ||
{ | ||
if(NSProcessInfo.processInfo.isiOSAppOnMac) | ||
{ | ||
NSError* xcodeError = nil; | ||
NSURL* xcodeURL = [self _xcodeURLOrError:&xcodeError]; | ||
if(xcodeError) | ||
{ | ||
if(error) { *error = xcodeError; } | ||
return nil; | ||
} | ||
|
||
return [self _debugHierarchyFoundationFrameworkURL_macOS:xcodeURL isCatalyst:YES]; | ||
} | ||
} | ||
return [self _debugHierarchyFoundationFrameworkURL_iOS:runtimeURL]; | ||
#else | ||
#if TARGET_OS_MACCATALYST | ||
BOOL isCatalyst = YES; | ||
#else | ||
BOOL isCatalyst = NO; | ||
#endif | ||
return [self _debugHierarchyFoundationFrameworkURL_macOS:runtimeURL isCatalyst:isCatalyst]; | ||
#endif | ||
} | ||
|
||
+ (NSURL*)_libViewDebuggerSupportURL_iOS:(NSURL*)runtimeURL | ||
{ | ||
NSString* rv = nil; | ||
if(@available(iOS 17, *)) | ||
{ | ||
rv = @"usr/lib/libViewDebuggerSupport.dylib"; | ||
} | ||
else | ||
{ | ||
rv = @"Developer/Library/PrivateFrameworks/DTDDISupport.framework/libViewDebuggerSupport.dylib"; | ||
} | ||
#if TARGET_OS_SIMULATOR | ||
return [runtimeURL URLByAppendingPathComponent:rv]; | ||
#else | ||
return [NSURL fileURLWithPath:[NSString stringWithFormat:@"/%@", rv]]; | ||
#endif | ||
} | ||
|
||
+ (NSURL*)_libViewDebuggerSupportURL_macOS:(NSURL*)runtimeURL isCatalyst:(BOOL)isCatalyst | ||
{ | ||
NSString* libName = isCatalyst ? @"libViewDebuggerSupport_macCatalyst.dylib" : @"libViewDebuggerSupport.dylib"; | ||
return [runtimeURL URLByAppendingPathComponent:[NSString stringWithFormat:@"Developer/Platforms/MacOSX.platform/Developer/Library/Debugger/%@", libName]]; | ||
} | ||
|
||
+ (nullable NSURL*)_libViewDebuggerSupportURL:(NSURL*)runtimeURL error:(out NSError** _Nullable)error | ||
{ | ||
#if TARGET_OS_IPHONE && !TARGET_OS_MACCATALYST | ||
if (@available(iOS 14.0, *)) | ||
{ | ||
if(NSProcessInfo.processInfo.isiOSAppOnMac) | ||
{ | ||
NSError* xcodeError = nil; | ||
NSURL* xcodeURL = [self _xcodeURLOrError:&xcodeError]; | ||
if(xcodeError) | ||
{ | ||
if(error) { *error = xcodeError; } | ||
return nil; | ||
} | ||
|
||
return [self _libViewDebuggerSupportURL_macOS:xcodeURL isCatalyst:YES]; | ||
} | ||
} | ||
return [self _libViewDebuggerSupportURL_iOS:runtimeURL]; | ||
#else | ||
#if TARGET_OS_MACCATALYST | ||
BOOL isCatalyst = YES; | ||
#else | ||
BOOL isCatalyst = NO; | ||
#endif | ||
return [self _libViewDebuggerSupportURL_macOS:runtimeURL isCatalyst:isCatalyst]; | ||
#endif | ||
} | ||
|
||
|
||
@end |
Oops, something went wrong.