Skip to content

Instantly share code, notes, and snippets.

@bfahey
Last active May 27, 2024 03:59
Show Gist options
  • Select an option

  • Save bfahey/c4eb72f264808f5e5dcb39e47b06def2 to your computer and use it in GitHub Desktop.

Select an option

Save bfahey/c4eb72f264808f5e5dcb39e47b06def2 to your computer and use it in GitHub Desktop.
Debug your app with Apple's private UIDebuggingInformationOverlay. Supports iOS 13-17+.
@import ObjectiveC.runtime;
@import UIKit;
@interface DebuggingOverlay: NSObject
+ (void)show API_AVAILABLE(ios(13));
@end
@implementation DebuggingOverlay
/// Shows the `UIDebuggingInformationOverlay`.
+ (void)show {
id debugOverlayClass = NSClassFromString(@"UIDebuggingInformationOverlay");
// Replace the overlay's init method with UIWindow's.
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Method newInit = class_getInstanceMethod([UIWindow class], @selector(init));
IMP newInitImp = method_getImplementation(newInit);
class_replaceMethod(debugOverlayClass, @selector(init), newInitImp, nil);
});
// [UIDebuggingInformationOverlay prepareDebuggingOverlay] calls the _UIGetDebuggingOverlayEnabled
// which checks if the device is internal to Apple. Bypass the check by invoking a tap
// gesture.
UIGestureRecognizer *tapGesture = [[UIGestureRecognizer alloc] init];
tapGesture.state = UIGestureRecognizerStateEnded;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
id handlerClass = NSClassFromString(@"UIDebuggingInformationOverlayInvokeGestureHandler");
id handler = [handlerClass performSelector:NSSelectorFromString(@"mainHandler")];
[handler performSelector:NSSelectorFromString(@"_handleActivationGesture:") withObject:tapGesture];
// If the app supports multiple scenes the debug menu needs to be added as a subview.
if ([NSBundle.mainBundle objectForInfoDictionaryKey:@"UIApplicationSceneManifest"]) {
UIView *debugOverlayView = [debugOverlayClass performSelector:NSSelectorFromString(@"overlay")];
debugOverlayView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
[[self getKeyWindow] addSubview:debugOverlayView];
}
#pragma clang diagnostic pop
}
/// Returns the app's key window.
+ (UIWindow * _Nullable)getKeyWindow {
for (UIScene *scene in UIApplication.sharedApplication.connectedScenes) {
if (![scene isKindOfClass:[UIWindowScene class]]) { continue; }
for (UIWindow *window in ((UIWindowScene *)scene).windows) {
if (window.isKeyWindow) {
return window;
}
}
}
return nil;
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment