Skip to content

Instantly share code, notes, and snippets.

@jbruchanov
Created January 3, 2020 21:49
Show Gist options
  • Save jbruchanov/ccefec4c93f6ceb5252478ad6dea1d39 to your computer and use it in GitHub Desktop.
Save jbruchanov/ccefec4c93f6ceb5252478ad6dea1d39 to your computer and use it in GitHub Desktop.
UITor - macos
//
// AppDelegate.m
// uitor
//
// Created by Jiri Bruchanov on 03/01/2020.
// Copyright © 2020 Jiri Bruchanov. All rights reserved.
//
//WEB Server https://github.com/swisspol/GCDWebServer
#import "AppDelegate.h"
@interface AppDelegate ()
@end
@implementation AppDelegate
GCDWebServer* _webServer;
-(GCDWebServerDataResponse*) runOnMainThread : (GCDWebServerDataResponse* (^)(void))action {
__block GCDWebServerDataResponse *result = nil;
dispatch_semaphore_t semaphor = dispatch_semaphore_create(0);
dispatch_async(dispatch_get_main_queue(), ^{
result = action();
dispatch_semaphore_signal(semaphor);
});
dispatch_semaphore_wait(semaphor, DISPATCH_TIME_FOREVER);
// dispatch_release(semaphor);
return result;
}
-(UIView*)findView:(int) position withRootView:(UIView*) view withPosition:(int*) counter {
if((*counter) == position){
return view;
}
if([view.subviews count] > 0){
for (UIView *childView in view.subviews) {
(*counter)++;
UIView *result = [self findView:position withRootView:childView withPosition:counter];
if(result){
return result;
}
}
}
return nil;
}
-(NSDictionary*) getData:(UIView*) view{
NSMutableDictionary *data = [[NSMutableDictionary alloc]init];
if ([view isKindOfClass:[UITextField class]]) {
UITextField *v = (UITextField*)view;
[data setObject:v.text forKey:@"Text"];
} else if ([view isKindOfClass:[UIButton class]]) {
UIButton *v = (UIButton*)view;
//[data setObject:v.titleLabel.text forKey:@"TitleLabel.Text"];
}
[data setObject:@"-" forKey:@"Owner:"];
[data setObject:NSStringFromClass([view class]) forKey:@"Type"];
[data setObject:@1 forKey:@"_ScaleX"];
[data setObject:@1 forKey:@"_ScaleY"];
[data setObject:@0 forKey:@"_Visibility"];
BOOL isVisible = !view.hidden;
BOOL isViewGroup = [view.subviews count] > 0;
BOOL hasBackground = view.backgroundColor != nil;
BOOL shouldRender = isVisible && !isViewGroup;
[data setObject:@(shouldRender) forKey:@"_RenderViewContent"];
float scale = [UIScreen mainScreen].scale;
CGRect bounds = [view convertRect:view.bounds toView:nil];
[data setObject:@(bounds.origin.x * scale) forKey:@"LocationScreenX"];
[data setObject:@(bounds.origin.y * scale) forKey:@"LocationScreenY"];
[data setObject:@(view.bounds.size.width * scale) forKey:@"Width"];
[data setObject:@(view.bounds.size.height * scale) forKey:@"Height"];
return data;
}
-(void)fillData:(NSMutableDictionary*) data withView:(UIView*) view withLevel:(int) level withPosition:(int*) position {
[data setObject:@0 forKey:@"IDi"];
[data setObject:@"" forKey:@"IDs"];
[data setObject:@"" forKey:@"Owner:"];
[data setObject:@(level) forKey:@"Level"];
[data setObject:@(*position) forKey:@"Position"];
[data setObject:[self getData:view] forKey:@"Data"];
if([view.subviews count] > 0){
NSMutableArray* arr = [[NSMutableArray alloc]init];
[data setObject:arr forKey:@"Nodes"];
for (UIView *childView in view.subviews) {
NSMutableDictionary *childData = [[NSMutableDictionary alloc]init];
[arr addObject:childData];
(*position)++;
[self fillData:childData withView:childView withLevel:level+1 withPosition:position];
}
}
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
// Create server
_webServer = [[GCDWebServer alloc] init];
// Add a handler to respond to GET requests on any URL
[_webServer addDefaultHandlerForMethod:@"GET"
requestClass:[GCDWebServerRequest class]
processBlock:^GCDWebServerResponse *(GCDWebServerRequest* request) {
NSString *url = request.URL.absoluteString;
NSString *relativePath = [url substringFromIndex:[_webServer.serverURL.absoluteString length]];
NSLog(@"URL %@", relativePath);
__block NSData *data = nil;
if([relativePath isEqualToString:@"screen/0"]) {
// Native Bounds - Detect Screen size in Pixels.
return [self runOnMainThread: ^{
CGFloat width = [UIScreen mainScreen].nativeBounds.size.width;
CGFloat height = [UIScreen mainScreen].nativeBounds.size.height;
UIWindow *keyWindow = [[UIApplication sharedApplication].windows objectAtIndex:0];
CGSize size = CGSizeMake(width, height);
UIGraphicsBeginImageContextWithOptions(size, NO, [UIScreen mainScreen].scale);
if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) {
UIGraphicsBeginImageContextWithOptions(keyWindow.bounds.size, NO, [UIScreen mainScreen].scale);
} else {
UIGraphicsBeginImageContext(keyWindow.bounds.size);
}
[keyWindow.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext;
NSData *data = UIImagePNGRepresentation(image);
return [GCDWebServerDataResponse responseWithData:data contentType:@"image/png"];
}];
} else if ([relativePath isEqualToString:@"viewhierarchy/0"]) {
return [self runOnMainThread: ^{
UIWindow *keyWindow = [[UIApplication sharedApplication].windows objectAtIndex:0];
NSMutableDictionary *dict = [[NSMutableDictionary alloc]init];
int pos = 0;
[self fillData:dict withView:keyWindow.rootViewController.view withLevel:0 withPosition:&pos];
NSError* error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dict
options:(NSJSONWritingOptions) (false ? NSJSONWritingPrettyPrinted : 0)
error:&error];
NSData *data = jsonData;
return [GCDWebServerDataResponse responseWithData:data contentType:@"application/json"];
}];
} else if ([relativePath isEqualToString:@"screens"]) {
NSData *data = [@"[\"IOSSample\"]" dataUsingEncoding:NSUTF8StringEncoding];
return [GCDWebServerDataResponse responseWithData:data contentType:@"application/json"];
} else if ([relativePath isEqualToString:@"config"]) {
NSMutableDictionary *config = [[NSMutableDictionary alloc]init];
[config setObject:@[@"LayoutInspectorPage",@"TidyTreePage", @"ThreeDPage", @"ScreenshotPage"] forKey:@"Pages"];
[config setObject:@NO forKey:@"Groovy"];
[config setObject:@1 forKey:@"ServerVersion"];
[config setObject:@"#F00" forKey:@"SelectionColor"];
[config setObject:@{
@"MODEL": @"IOS",
@"MANUFACTURER": @"Apple",
@"API": @1,
} forKey:@"Devivce"];
NSError* error;
NSData *data = [NSJSONSerialization dataWithJSONObject:config
options:(NSJSONWritingOptions) (false ? NSJSONWritingPrettyPrinted : 0)
error:&error];
return [GCDWebServerDataResponse responseWithData:data contentType:@"application/json"];
} else if ([relativePath hasPrefix:@"view/0/"]) {
return [self runOnMainThread: ^{
NSArray *items = [relativePath componentsSeparatedByString:@"/"];
int position = [[items objectAtIndex:(items.count - 1)] integerValue];
UIWindow *keyWindow = [[UIApplication sharedApplication].windows objectAtIndex:0];
int pos = 0;
UIView *view = [self findView:position withRootView:keyWindow.rootViewController.view withPosition:&pos];
UIGraphicsBeginImageContext(CGSizeMake(view.frame.size.width, view.frame.size.height));
[view drawViewHierarchyInRect:CGRectMake(0, 0, view.frame.size.width, view.frame.size.height) afterScreenUpdates:YES];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
data = UIImagePNGRepresentation(image);
return [GCDWebServerDataResponse responseWithData:data contentType:@"image/png"];
}];
} else {
NSData *data = [@"{}" dataUsingEncoding:NSUTF8StringEncoding];
return [GCDWebServerDataResponse responseWithData:data contentType:@"application/json"];
}
}];
// Start server on port 8080
[_webServer startWithPort:8081 bonjourName:nil];
NSLog(@"Visit %@ in your web browser", _webServer.serverURL);
return YES;
}
#pragma mark - UISceneSession lifecycle
- (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options {
// Called when a new scene session is being created.
// Use this method to select a configuration to create the new scene with.
return [[UISceneConfiguration alloc] initWithName:@"Default Configuration" sessionRole:connectingSceneSession.role];
}
- (void)application:(UIApplication *)application didDiscardSceneSessions:(NSSet<UISceneSession *> *)sceneSessions {
// Called when the user discards a scene session.
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment