LKS_RequestHandler.m 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
  1. //
  2. // LKS_RequestHandler.m
  3. // LookinServer
  4. //
  5. // Created by Li Kai on 2019/1/15.
  6. // https://lookin.work
  7. //
  8. #import "LKS_RequestHandler.h"
  9. #import "NSObject+LookinServer.h"
  10. #import "UIImage+LookinServer.h"
  11. #import "LKS_ConnectionManager.h"
  12. #import "LookinConnectionResponseAttachment.h"
  13. #import "LookinAttributeModification.h"
  14. #import "LookinDisplayItemDetail.h"
  15. #import "LookinHierarchyInfo.h"
  16. #import "LookinServerDefines.h"
  17. #import <objc/runtime.h>
  18. #import "LookinObject.h"
  19. #import "LKS_LocalInspectManager.h"
  20. #import "LookinAppInfo.h"
  21. #import "LKS_MethodTraceManager.h"
  22. #import "LKS_AttrGroupsMaker.h"
  23. #import "LKS_AttrModificationHandler.h"
  24. #import "LKS_AttrModificationPatchHandler.h"
  25. #import "LKS_HierarchyDetailsHandler.h"
  26. #import "LookinStaticAsyncUpdateTask.h"
  27. @interface LKS_RequestHandler ()
  28. @end
  29. @implementation LKS_RequestHandler {
  30. NSSet *_validRequestTypes;
  31. }
  32. - (instancetype)init {
  33. if (self = [super init]) {
  34. _validRequestTypes = [NSSet setWithObjects:@(LookinRequestTypePing),
  35. @(LookinRequestTypeApp),
  36. @(LookinRequestTypeHierarchy),
  37. @(LookinRequestTypeModification),
  38. @(LookinRequestTypeAttrModificationPatch),
  39. @(LookinRequestTypeHierarchyDetails),
  40. @(LookinRequestTypeFetchObject),
  41. @(LookinRequestTypeAllAttrGroups),
  42. @(LookinRequestTypeAllSelectorNames),
  43. @(LookinRequestTypeAddMethodTrace),
  44. @(LookinRequestTypeDeleteMethodTrace),
  45. @(LookinRequestTypeClassesAndMethodTraceLit),
  46. @(LookinRequestTypeInvokeMethod),
  47. @(LookinRequestTypeFetchImageViewImage),
  48. @(LookinRequestTypeModifyRecognizerEnable),
  49. @(LookinPush_BringForwardScreenshotTask),
  50. @(LookinPush_CanceHierarchyDetails),
  51. nil];
  52. }
  53. return self;
  54. }
  55. - (BOOL)canHandleRequestType:(uint32_t)requestType {
  56. if ([_validRequestTypes containsObject:@(requestType)]) {
  57. return YES;
  58. }
  59. NSAssert(NO, @"");
  60. return NO;
  61. }
  62. - (void)handleRequestType:(uint32_t)requestType tag:(uint32_t)tag object:(id)object {
  63. if (requestType == LookinRequestTypePing) {
  64. LookinConnectionResponseAttachment *responseAttachment = [LookinConnectionResponseAttachment new];
  65. // 当 app 处于后台时,可能可以执行代码也可能不能执行代码,如果运气好了可以执行代码,则这里直接主动使用 appIsInBackground 标识 app 处于后台,不要让 Lookin 客户端傻傻地等待超时了
  66. if (![LKS_ConnectionManager sharedInstance].applicationIsActive) {
  67. responseAttachment.appIsInBackground = YES;
  68. }
  69. [[LKS_ConnectionManager sharedInstance] respond:responseAttachment requestType:requestType tag:tag];
  70. } else if (requestType == LookinRequestTypeApp) {
  71. // 请求可用设备信息
  72. NSDictionary<NSString *, id> *params = object;
  73. BOOL needImages = ((NSNumber *)params[@"needImages"]).boolValue;
  74. NSArray<NSNumber *> *localIdentifiers = params[@"local"];
  75. LookinAppInfo *appInfo = [LookinAppInfo currentInfoWithScreenshot:needImages icon:needImages localIdentifiers:localIdentifiers];
  76. LookinConnectionResponseAttachment *responseAttachment = [LookinConnectionResponseAttachment new];
  77. responseAttachment.data = appInfo;
  78. [[LKS_ConnectionManager sharedInstance] respond:responseAttachment requestType:requestType tag:tag];
  79. } else if (requestType == LookinRequestTypeHierarchy) {
  80. LookinConnectionResponseAttachment *responseAttachment = [LookinConnectionResponseAttachment new];
  81. responseAttachment.data = [LookinHierarchyInfo staticInfo];
  82. [[LKS_ConnectionManager sharedInstance] respond:responseAttachment requestType:requestType tag:tag];
  83. } else if (requestType == LookinRequestTypeModification) {
  84. // 请求修改某个属性
  85. [LKS_AttrModificationHandler handleModification:object completion:^(LookinDisplayItemDetail *data, NSError *error) {
  86. LookinConnectionResponseAttachment *attachment = [LookinConnectionResponseAttachment new];
  87. if (error) {
  88. attachment.error = error;
  89. } else {
  90. attachment.data = data;
  91. }
  92. [[LKS_ConnectionManager sharedInstance] respond:attachment requestType:requestType tag:tag];
  93. }];
  94. } else if (requestType == LookinRequestTypeAttrModificationPatch) {
  95. NSArray<LookinStaticAsyncUpdateTask *> *tasks = object;
  96. NSUInteger dataTotalCount = tasks.count;
  97. [LKS_AttrModificationHandler handlePatchWithTasks:tasks block:^(LookinDisplayItemDetail *data) {
  98. LookinConnectionResponseAttachment *attrAttachment = [LookinConnectionResponseAttachment new];
  99. attrAttachment.data = data;
  100. attrAttachment.dataTotalCount = dataTotalCount;
  101. attrAttachment.currentDataCount = 1;
  102. [[LKS_ConnectionManager sharedInstance] respond:attrAttachment requestType:LookinRequestTypeAttrModificationPatch tag:tag];
  103. }];
  104. } else if (requestType == LookinRequestTypeHierarchyDetails) {
  105. NSArray<LookinStaticAsyncUpdateTasksPackage *> *packages = object;
  106. NSUInteger responsesDataTotalCount = [packages lookin_reduceInteger:^NSInteger(NSInteger accumulator, NSUInteger idx, LookinStaticAsyncUpdateTasksPackage *package) {
  107. accumulator += package.tasks.count;
  108. return accumulator;
  109. } initialAccumlator:0];
  110. [[LKS_HierarchyDetailsHandler sharedInstance] startWithPackages:packages block:^(NSArray<LookinDisplayItemDetail *> *details, NSError *error) {
  111. LookinConnectionResponseAttachment *attachment = [LookinConnectionResponseAttachment new];
  112. attachment.error = error;
  113. attachment.data = details;
  114. attachment.dataTotalCount = responsesDataTotalCount;
  115. attachment.currentDataCount = details.count;
  116. [[LKS_ConnectionManager sharedInstance] respond:attachment requestType:LookinRequestTypeHierarchyDetails tag:tag];
  117. }];
  118. } else if (requestType == LookinRequestTypeFetchObject) {
  119. unsigned long oid = ((NSNumber *)object).unsignedLongValue;
  120. NSObject *object = [NSObject lks_objectWithOid:oid];
  121. LookinObject *lookinObj = [LookinObject instanceWithObject:object];
  122. LookinConnectionResponseAttachment *attach = [LookinConnectionResponseAttachment new];
  123. attach.data = lookinObj;
  124. [[LKS_ConnectionManager sharedInstance] respond:attach requestType:requestType tag:tag];
  125. } else if (requestType == LookinRequestTypeAllAttrGroups) {
  126. unsigned long oid = ((NSNumber *)object).unsignedLongValue;
  127. CALayer *layer = (CALayer *)[NSObject lks_objectWithOid:oid];
  128. if (![layer isKindOfClass:[CALayer class]]) {
  129. [self _submitResponseWithError:LookinErr_ObjNotFound requestType:LookinRequestTypeAllAttrGroups tag:tag];
  130. return;
  131. }
  132. NSArray<LookinAttributesGroup *> *list = [LKS_AttrGroupsMaker attrGroupsForLayer:layer];
  133. [self _submitResponseWithData:list requestType:LookinRequestTypeAllAttrGroups tag:tag];
  134. } else if (requestType == LookinRequestTypeAllSelectorNames) {
  135. NSDictionary *params = object;
  136. Class targetClass = NSClassFromString(params[@"className"]);
  137. BOOL hasArg = [(NSNumber *)params[@"hasArg"] boolValue];
  138. if (!targetClass) {
  139. NSString *errorMsg = [NSString stringWithFormat:LKS_Localized(@"Didn't find the class named \"%@\". Please input another class and try again."), object];
  140. [self _submitResponseWithError:LookinErrorMake(errorMsg, @"") requestType:requestType tag:tag];
  141. return;
  142. }
  143. NSArray<NSString *> *selNames = [self _methodNameListForClass:targetClass hasArg:hasArg];
  144. [self _submitResponseWithData:selNames requestType:requestType tag:tag];
  145. } else if (requestType == LookinRequestTypeAddMethodTrace) {
  146. if (![object isKindOfClass:[NSDictionary class]]) {
  147. [self _submitResponseWithError:LookinErr_Inner requestType:requestType tag:tag];
  148. return;
  149. }
  150. NSDictionary *dict = object;
  151. NSString *className = dict[@"className"];
  152. NSString *selName = dict[@"selName"];
  153. Class targetClass = NSClassFromString(className);
  154. if (!targetClass) {
  155. NSString *errorMsg = [NSString stringWithFormat:LKS_Localized(@"Didn't find the class named \"%@\". Please input another class and try again."), object];
  156. [self _submitResponseWithError:LookinErrorMake(errorMsg, @"") requestType:requestType tag:tag];
  157. return;
  158. }
  159. SEL targetSelector = NSSelectorFromString(selName);
  160. if (class_getInstanceMethod(targetClass, targetSelector) == NULL) {
  161. NSString *errorMsg = [NSString stringWithFormat:LKS_Localized(@"%@ doesn't have a method called \"%@\". Please input another method name and try again."), className, selName];
  162. [self _submitResponseWithError:LookinErrorMake(errorMsg, @"") requestType:requestType tag:tag];
  163. return;
  164. }
  165. [[LKS_MethodTraceManager sharedInstance] addWithClassName:className selName:selName];
  166. [self _submitResponseWithData:[LKS_MethodTraceManager sharedInstance].currentActiveTraceList requestType:requestType tag:tag];
  167. } else if (requestType == LookinRequestTypeDeleteMethodTrace) {
  168. if (![object isKindOfClass:[NSDictionary class]]) {
  169. [self _submitResponseWithError:LookinErr_Inner requestType:requestType tag:tag];
  170. return;
  171. }
  172. NSDictionary *dict = object;
  173. NSString *className = dict[@"className"];
  174. NSString *selName = dict[@"selName"];
  175. [[LKS_MethodTraceManager sharedInstance] removeWithClassName:className selName:selName];
  176. [self _submitResponseWithData:[LKS_MethodTraceManager sharedInstance].currentActiveTraceList requestType:requestType tag:tag];
  177. } else if (requestType == LookinRequestTypeClassesAndMethodTraceLit) {
  178. LKS_MethodTraceManager *mng = [LKS_MethodTraceManager sharedInstance];
  179. NSDictionary *dict = @{@"classes":mng.allClassesListInApp, @"activeList":mng.currentActiveTraceList};
  180. [self _submitResponseWithData:dict requestType:requestType tag:tag];
  181. } else if (requestType == LookinRequestTypeInvokeMethod) {
  182. NSDictionary *param = object;
  183. unsigned long oid = [param[@"oid"] unsignedLongValue];
  184. NSString *text = param[@"text"];
  185. if (!text.length) {
  186. [self _submitResponseWithError:LookinErr_Inner requestType:requestType tag:tag];
  187. return;
  188. }
  189. NSObject *targerObj = [NSObject lks_objectWithOid:oid];
  190. if (!targerObj) {
  191. [self _submitResponseWithError:LookinErr_ObjNotFound requestType:requestType tag:tag];
  192. return;
  193. }
  194. SEL targetSelector = NSSelectorFromString(text);
  195. if (targetSelector && [targerObj respondsToSelector:targetSelector]) {
  196. NSString *resultDescription;
  197. NSObject *resultObject;
  198. NSError *error;
  199. [self _handleInvokeWithObject:targerObj selector:targetSelector resultDescription:&resultDescription resultObject:&resultObject error:&error];
  200. if (error) {
  201. [self _submitResponseWithError:error requestType:requestType tag:tag];
  202. return;
  203. }
  204. NSMutableDictionary *responseData = [NSMutableDictionary dictionaryWithCapacity:2];
  205. if (resultDescription) {
  206. responseData[@"description"] = resultDescription;
  207. }
  208. if (resultObject) {
  209. responseData[@"object"] = resultObject;
  210. }
  211. [self _submitResponseWithData:responseData requestType:requestType tag:tag];
  212. } else {
  213. NSString *errMsg = [NSString stringWithFormat:LKS_Localized(@"%@ doesn't have an instance method called \"%@\"."), NSStringFromClass(targerObj.class), text];
  214. [self _submitResponseWithError:LookinErrorMake(errMsg, @"") requestType:requestType tag:tag];
  215. }
  216. } else if (requestType == LookinPush_BringForwardScreenshotTask) {
  217. [[LKS_HierarchyDetailsHandler sharedInstance] bringForwardWithPackages:object];
  218. } else if (requestType == LookinPush_CanceHierarchyDetails) {
  219. [[LKS_HierarchyDetailsHandler sharedInstance] cancel];
  220. } else if (requestType == LookinRequestTypeFetchImageViewImage) {
  221. if (![object isKindOfClass:[NSNumber class]]) {
  222. [self _submitResponseWithError:LookinErr_Inner requestType:requestType tag:tag];
  223. return;
  224. }
  225. unsigned long imageViewOid = [(NSNumber *)object unsignedLongValue];
  226. UIImageView *imageView = (UIImageView *)[NSObject lks_objectWithOid:imageViewOid];
  227. if (!imageView) {
  228. [self _submitResponseWithError:LookinErr_ObjNotFound requestType:requestType tag:tag];
  229. return;
  230. }
  231. if (![imageView isKindOfClass:[UIImageView class]]) {
  232. [self _submitResponseWithError:LookinErr_Inner requestType:requestType tag:tag];
  233. return;
  234. }
  235. UIImage *image = imageView.image;
  236. NSData *imageData = [image lookin_data];
  237. [self _submitResponseWithData:imageData requestType:requestType tag:tag];
  238. } else if (requestType == LookinRequestTypeModifyRecognizerEnable) {
  239. NSDictionary<NSString *, NSNumber *> *params = object;
  240. unsigned long recognizerOid = ((NSNumber *)params[@"oid"]).unsignedLongValue;
  241. BOOL shouldBeEnabled = ((NSNumber *)params[@"enable"]).boolValue;
  242. UIGestureRecognizer *recognizer = (UIGestureRecognizer *)[NSObject lks_objectWithOid:recognizerOid];
  243. if (!recognizer) {
  244. [self _submitResponseWithError:LookinErr_ObjNotFound requestType:requestType tag:tag];
  245. return;
  246. }
  247. if (![recognizer isKindOfClass:[UIGestureRecognizer class]]) {
  248. [self _submitResponseWithError:LookinErr_Inner requestType:requestType tag:tag];
  249. return;
  250. }
  251. recognizer.enabled = shouldBeEnabled;
  252. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
  253. // dispatch 以确保拿到的 enabled 是比较新的
  254. [self _submitResponseWithData:@(recognizer.enabled) requestType:requestType tag:tag];
  255. });
  256. }
  257. }
  258. - (NSArray<NSString *> *)_methodNameListForClass:(Class)aClass hasArg:(BOOL)hasArg {
  259. NSSet<NSString *> *prefixesToVoid = [NSSet setWithObjects:@"_", @"CA_", @"cpl", @"mf_", @"vs_", @"pep_", @"isNS", @"avkit_", @"PG_", @"px_", @"pl_", @"nsli_", @"pu_", @"pxg_", nil];
  260. NSMutableArray<NSString *> *array = [NSMutableArray array];
  261. Class currentClass = aClass;
  262. while (currentClass) {
  263. NSString *className = NSStringFromClass(currentClass);
  264. BOOL isSystemClass = ([className hasPrefix:@"UI"] || [className hasPrefix:@"CA"] || [className hasPrefix:@"NS"]);
  265. unsigned int methodCount = 0;
  266. Method *methods = class_copyMethodList(currentClass, &methodCount);
  267. for (unsigned int i = 0; i < methodCount; i++) {
  268. NSString *selName = NSStringFromSelector(method_getName(methods[i]));
  269. if (!hasArg && [selName containsString:@":"]) {
  270. continue;
  271. }
  272. if (isSystemClass) {
  273. BOOL invalid = [prefixesToVoid lookin_any:^BOOL(NSString *prefix) {
  274. return [selName hasPrefix:prefix];
  275. }];
  276. if (invalid) {
  277. continue;
  278. }
  279. }
  280. if (selName.length && ![array containsObject:selName]) {
  281. [array addObject:selName];
  282. }
  283. }
  284. if (methods) free(methods);
  285. currentClass = [currentClass superclass];
  286. }
  287. return [array lookin_sortedArrayByStringLength];
  288. }
  289. - (void)_handleInvokeWithObject:(NSObject *)obj selector:(SEL)selector resultDescription:(NSString **)description resultObject:(LookinObject **)resultObject error:(NSError **)error {
  290. NSMethodSignature *signature = [obj methodSignatureForSelector:selector];
  291. if (signature.numberOfArguments > 2) {
  292. *error = LookinErrorMake(LKS_Localized(@"Lookin doesn't support invoking methods with arguments yet."), @"");
  293. return;
  294. }
  295. NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
  296. [invocation setTarget:obj];
  297. [invocation setSelector:selector];
  298. [invocation invoke];
  299. const char *returnType = [signature methodReturnType];
  300. if (strcmp(returnType, @encode(void)) == 0) {
  301. //void, do nothing
  302. *description = LookinStringFlag_VoidReturn;
  303. } else if (strcmp(returnType, @encode(char)) == 0) {
  304. char charValue;
  305. [invocation getReturnValue:&charValue];
  306. *description = [NSString stringWithFormat:@"%@", @(charValue)];
  307. } else if (strcmp(returnType, @encode(int)) == 0) {
  308. int intValue;
  309. [invocation getReturnValue:&intValue];
  310. if (intValue == INT_MAX) {
  311. *description = @"INT_MAX";
  312. } else if (intValue == INT_MIN) {
  313. *description = @"INT_MIN";
  314. } else {
  315. *description = [NSString stringWithFormat:@"%@", @(intValue)];
  316. }
  317. } else if (strcmp(returnType, @encode(short)) == 0) {
  318. short shortValue;
  319. [invocation getReturnValue:&shortValue];
  320. if (shortValue == SHRT_MAX) {
  321. *description = @"SHRT_MAX";
  322. } else if (shortValue == SHRT_MIN) {
  323. *description = @"SHRT_MIN";
  324. } else {
  325. *description = [NSString stringWithFormat:@"%@", @(shortValue)];
  326. }
  327. } else if (strcmp(returnType, @encode(long)) == 0) {
  328. long longValue;
  329. [invocation getReturnValue:&longValue];
  330. if (longValue == NSNotFound) {
  331. *description = @"NSNotFound";
  332. } else if (longValue == LONG_MAX) {
  333. *description = @"LONG_MAX";
  334. } else if (longValue == LONG_MIN) {
  335. *description = @"LONG_MAX";
  336. } else {
  337. *description = [NSString stringWithFormat:@"%@", @(longValue)];
  338. }
  339. } else if (strcmp(returnType, @encode(long long)) == 0) {
  340. long long longLongValue;
  341. [invocation getReturnValue:&longLongValue];
  342. if (longLongValue == LLONG_MAX) {
  343. *description = @"LLONG_MAX";
  344. } else if (longLongValue == LLONG_MIN) {
  345. *description = @"LLONG_MIN";
  346. } else {
  347. *description = [NSString stringWithFormat:@"%@", @(longLongValue)];
  348. }
  349. } else if (strcmp(returnType, @encode(unsigned char)) == 0) {
  350. unsigned char ucharValue;
  351. [invocation getReturnValue:&ucharValue];
  352. if (ucharValue == UCHAR_MAX) {
  353. *description = @"UCHAR_MAX";
  354. } else {
  355. *description = [NSString stringWithFormat:@"%@", @(ucharValue)];
  356. }
  357. } else if (strcmp(returnType, @encode(unsigned int)) == 0) {
  358. unsigned int uintValue;
  359. [invocation getReturnValue:&uintValue];
  360. if (uintValue == UINT_MAX) {
  361. *description = @"UINT_MAX";
  362. } else {
  363. *description = [NSString stringWithFormat:@"%@", @(uintValue)];
  364. }
  365. } else if (strcmp(returnType, @encode(unsigned short)) == 0) {
  366. unsigned short ushortValue;
  367. [invocation getReturnValue:&ushortValue];
  368. if (ushortValue == USHRT_MAX) {
  369. *description = @"USHRT_MAX";
  370. } else {
  371. *description = [NSString stringWithFormat:@"%@", @(ushortValue)];
  372. }
  373. } else if (strcmp(returnType, @encode(unsigned long)) == 0) {
  374. unsigned long ulongValue;
  375. [invocation getReturnValue:&ulongValue];
  376. if (ulongValue == ULONG_MAX) {
  377. *description = @"ULONG_MAX";
  378. } else {
  379. *description = [NSString stringWithFormat:@"%@", @(ulongValue)];
  380. }
  381. } else if (strcmp(returnType, @encode(unsigned long long)) == 0) {
  382. unsigned long long ulongLongValue;
  383. [invocation getReturnValue:&ulongLongValue];
  384. if (ulongLongValue == ULONG_LONG_MAX) {
  385. *description = @"ULONG_LONG_MAX";
  386. } else {
  387. *description = [NSString stringWithFormat:@"%@", @(ulongLongValue)];
  388. }
  389. } else if (strcmp(returnType, @encode(float)) == 0) {
  390. float floatValue;
  391. [invocation getReturnValue:&floatValue];
  392. if (floatValue == FLT_MAX) {
  393. *description = @"FLT_MAX";
  394. } else if (floatValue == FLT_MIN) {
  395. *description = @"FLT_MIN";
  396. } else {
  397. *description = [NSString stringWithFormat:@"%@", @(floatValue)];
  398. }
  399. } else if (strcmp(returnType, @encode(double)) == 0) {
  400. double doubleValue;
  401. [invocation getReturnValue:&doubleValue];
  402. if (doubleValue == DBL_MAX) {
  403. *description = @"DBL_MAX";
  404. } else if (doubleValue == DBL_MIN) {
  405. *description = @"DBL_MIN";
  406. } else {
  407. *description = [NSString stringWithFormat:@"%@", @(doubleValue)];
  408. }
  409. } else if (strcmp(returnType, @encode(BOOL)) == 0) {
  410. BOOL boolValue;
  411. [invocation getReturnValue:&boolValue];
  412. *description = boolValue ? @"YES" : @"NO";
  413. } else if (strcmp(returnType, @encode(SEL)) == 0) {
  414. SEL selValue;
  415. [invocation getReturnValue:&selValue];
  416. *description = [NSString stringWithFormat:@"SEL(%@)", NSStringFromSelector(selValue)];
  417. } else if (strcmp(returnType, @encode(Class)) == 0) {
  418. Class classValue;
  419. [invocation getReturnValue:&classValue];
  420. *description = [NSString stringWithFormat:@"<%@>", NSStringFromClass(classValue)];
  421. } else if (strcmp(returnType, @encode(CGPoint)) == 0) {
  422. CGPoint targetValue;
  423. [invocation getReturnValue:&targetValue];
  424. *description = NSStringFromCGPoint(targetValue);
  425. } else if (strcmp(returnType, @encode(CGVector)) == 0) {
  426. CGVector targetValue;
  427. [invocation getReturnValue:&targetValue];
  428. *description = NSStringFromCGVector(targetValue);
  429. } else if (strcmp(returnType, @encode(CGSize)) == 0) {
  430. CGSize targetValue;
  431. [invocation getReturnValue:&targetValue];
  432. *description = NSStringFromCGSize(targetValue);
  433. } else if (strcmp(returnType, @encode(CGRect)) == 0) {
  434. CGRect rectValue;
  435. [invocation getReturnValue:&rectValue];
  436. *description = NSStringFromCGRect(rectValue);
  437. } else if (strcmp(returnType, @encode(CGAffineTransform)) == 0) {
  438. CGAffineTransform rectValue;
  439. [invocation getReturnValue:&rectValue];
  440. *description = NSStringFromCGAffineTransform(rectValue);
  441. } else if (strcmp(returnType, @encode(UIEdgeInsets)) == 0) {
  442. UIEdgeInsets targetValue;
  443. [invocation getReturnValue:&targetValue];
  444. *description = NSStringFromUIEdgeInsets(targetValue);
  445. } else if (strcmp(returnType, @encode(UIOffset)) == 0) {
  446. UIOffset targetValue;
  447. [invocation getReturnValue:&targetValue];
  448. *description = NSStringFromUIOffset(targetValue);
  449. } else {
  450. if (@available(iOS 11.0, tvOS 11.0, *)) {
  451. if (strcmp(returnType, @encode(NSDirectionalEdgeInsets)) == 0) {
  452. NSDirectionalEdgeInsets targetValue;
  453. [invocation getReturnValue:&targetValue];
  454. *description = NSStringFromDirectionalEdgeInsets(targetValue);
  455. return;
  456. }
  457. }
  458. NSString *argType_string = [[NSString alloc] lookin_safeInitWithUTF8String:returnType];
  459. if ([argType_string hasPrefix:@"@"] || [argType_string hasPrefix:@"^{"]) {
  460. __unsafe_unretained id returnObjValue;
  461. [invocation getReturnValue:&returnObjValue];
  462. if (returnObjValue) {
  463. *description = [NSString stringWithFormat:@"%@", returnObjValue];
  464. LookinObject *parsedLookinObj = [LookinObject instanceWithObject:returnObjValue];
  465. *resultObject = parsedLookinObj;
  466. } else {
  467. *description = @"nil";
  468. }
  469. } else {
  470. *description = [NSString stringWithFormat:LKS_Localized(@"%@ was invoked successfully, but Lookin can't parse the return value:%@"), NSStringFromSelector(selector), argType_string];
  471. }
  472. }
  473. }
  474. - (void)_submitResponseWithError:(NSError *)error requestType:(uint32_t)requestType tag:(uint32_t)tag {
  475. LookinConnectionResponseAttachment *attachment = [LookinConnectionResponseAttachment new];
  476. attachment.error = error;
  477. [[LKS_ConnectionManager sharedInstance] respond:attachment requestType:requestType tag:tag];
  478. }
  479. - (void)_submitResponseWithData:(NSObject *)data requestType:(uint32_t)requestType tag:(uint32_t)tag {
  480. LookinConnectionResponseAttachment *attachment = [LookinConnectionResponseAttachment new];
  481. attachment.data = data;
  482. [[LKS_ConnectionManager sharedInstance] respond:attachment requestType:requestType tag:tag];
  483. }
  484. @end